Delivery — Food Delivery Landing
A crave-able, mobile-first marketing landing for a fictional food-delivery brand. It opens with a bold hero, an address bar that validates input and scrolls to results, and floating dish cards over an animated blob. A scrollable cuisine rail filters a live restaurant grid of rating, ETA and delivery-fee cards with savable favorites. Below sit a three-step how-it-works, a phone-mockup app section with SMS link capture and an animated rider on a route map, plus restaurant and rider partner CTAs, a full footer, mobile nav and scroll reveal.
MCP
Code
:root {
/* Food-delivery palette: white + appetite red/orange + sunny yellow */
--brand: #ff5a2c;
--brand-d: #e0461d;
--sun: #ffc233;
--sun-d: #f3a712;
--ink: #16181d;
--ink-2: #3b3f4a;
--muted: #71757f;
--bg: #fff7f2;
--surface: #ffffff;
--line: rgba(22, 24, 29, 0.1);
--ok: #1f9d62;
--warn: #e89422;
--danger: #d4493e;
--track: #5b8def;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 20px;
--r-xl: 28px;
--sh-sm: 0 2px 8px rgba(22, 24, 29, 0.06);
--sh-md: 0 10px 30px rgba(22, 24, 29, 0.1);
--sh-brand: 0 12px 28px rgba(255, 90, 44, 0.32);
--wrap: 1140px;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
font-family: "Inter", system-ui, -apple-system, sans-serif;
line-height: 1.5;
color: var(--ink);
background: var(--bg);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1, h2, h3, h4 { margin: 0 0 .4em; line-height: 1.12; letter-spacing: -.02em; }
p { margin: 0 0 1em; }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; }
.wrap { width: min(100% - 2.5rem, var(--wrap)); margin-inline: auto; }
.vh {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
position: absolute; left: 12px; top: -48px; z-index: 100;
background: var(--ink); color: #fff; padding: .6rem 1rem; border-radius: var(--r-sm);
transition: top .2s;
}
.skip-link:focus { top: 12px; }
:focus-visible { outline: 3px solid var(--brand); outline-offset: 2px; border-radius: 4px; }
/* buttons */
.btn {
display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
font-weight: 700; font-size: .95rem; padding: .72rem 1.25rem; border-radius: 999px;
border: 1.5px solid transparent; cursor: pointer; transition: transform .15s, box-shadow .15s, background .15s, color .15s;
white-space: nowrap; font-family: inherit;
}
.btn:active { transform: translateY(1px) scale(.99); }
.btn-solid { background: var(--brand); color: #fff; box-shadow: var(--sh-brand); }
.btn-solid:hover { background: var(--brand-d); transform: translateY(-2px); }
.btn-dark { background: var(--ink); color: #fff; }
.btn-dark:hover { background: #000; transform: translateY(-2px); }
.btn-ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn-ghost:hover { border-color: var(--brand); color: var(--brand); }
/* nav */
.nav {
position: sticky; top: 0; z-index: 60;
background: rgba(255, 255, 255, .86); backdrop-filter: blur(12px);
border-bottom: 1px solid var(--line);
}
.nav-inner { display: flex; align-items: center; gap: 1.5rem; height: 68px; }
.brand { display: inline-flex; align-items: center; gap: .5rem; font-weight: 800; font-size: 1.25rem; }
.brand-mark {
display: grid; place-items: center; width: 34px; height: 34px; border-radius: 10px;
background: linear-gradient(135deg, var(--brand), var(--sun)); font-size: 1rem;
box-shadow: var(--sh-brand);
}
.brand-name { letter-spacing: -.03em; }
.nav-links { display: flex; gap: 1.4rem; margin-left: auto; font-weight: 600; font-size: .95rem; color: var(--ink-2); }
.nav-links a { position: relative; padding: .3rem 0; }
.nav-links a::after {
content: ""; position: absolute; left: 0; bottom: -2px; height: 2px; width: 0;
background: var(--brand); transition: width .2s;
}
.nav-links a:hover { color: var(--ink); }
.nav-links a:hover::after { width: 100%; }
.nav-cta { display: flex; gap: .6rem; }
.nav-toggle {
display: none; flex-direction: column; gap: 5px; background: none; border: 0;
padding: 8px; margin-left: auto; cursor: pointer;
}
.nav-toggle span { width: 24px; height: 2.5px; background: var(--ink); border-radius: 2px; transition: .25s; }
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7.5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7.5px) rotate(-45deg); }
.mobile-nav {
display: flex; flex-direction: column; gap: .25rem; padding: 1rem 1.25rem 1.5rem;
border-bottom: 1px solid var(--line); background: var(--surface);
}
.mobile-nav a { padding: .75rem .25rem; font-weight: 600; border-bottom: 1px solid var(--line); }
.mobile-nav-cta { display: flex; gap: .6rem; margin-top: .9rem; }
.mobile-nav-cta .btn { flex: 1; }
/* shared section bits */
.eyebrow {
display: inline-flex; align-items: center; gap: .5rem; font-weight: 700; font-size: .8rem;
text-transform: uppercase; letter-spacing: .06em; color: var(--brand-d);
background: rgba(255, 90, 44, .1); padding: .4rem .8rem; border-radius: 999px;
}
.eyebrow--light { color: #fff; background: rgba(255, 255, 255, .18); }
.pulse { width: 8px; height: 8px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 0 rgba(31, 157, 98, .6); animation: pulse 1.8s infinite; }
@keyframes pulse { 70% { box-shadow: 0 0 0 8px rgba(31, 157, 98, 0); } 100% { box-shadow: 0 0 0 0 rgba(31, 157, 98, 0); } }
.section-head { text-align: center; max-width: 560px; margin: 0 auto 2rem; }
.section-head h2 { font-size: clamp(1.6rem, 4vw, 2.3rem); }
.section-head p { color: var(--muted); margin: 0; }
.section-head--row {
display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem;
text-align: left; max-width: none;
}
.section-head--row h2 { font-size: clamp(1.5rem, 3.5vw, 2rem); }
/* hero */
.hero { padding: clamp(2.5rem, 6vw, 5rem) 0 clamp(3rem, 7vw, 5rem); overflow: hidden; }
.hero-grid { display: grid; grid-template-columns: 1.05fr .95fr; gap: 3rem; align-items: center; }
.hero-copy h1 { font-size: clamp(2.4rem, 6vw, 4rem); font-weight: 800; }
.hl { color: var(--brand); position: relative; white-space: nowrap; }
.hl::after {
content: ""; position: absolute; left: -2%; right: -2%; bottom: 4%; height: 36%;
background: var(--sun); opacity: .35; z-index: -1; border-radius: 4px; transform: rotate(-1.2deg);
}
.lede { font-size: 1.12rem; color: var(--ink-2); max-width: 30ch; }
.address {
display: flex; gap: .5rem; background: var(--surface); padding: .5rem; border-radius: 999px;
box-shadow: var(--sh-md); border: 1px solid var(--line); max-width: 460px; margin-bottom: .4rem;
}
.address-field { display: flex; align-items: center; gap: .5rem; flex: 1; padding-left: .85rem; }
.pin { font-size: 1.15rem; }
.address input {
flex: 1; border: 0; background: none; font: inherit; font-size: 1rem; padding: .55rem 0; min-width: 0;
color: var(--ink);
}
.address input:focus { outline: none; }
.address-btn { flex-shrink: 0; }
.address-hint { color: var(--muted); font-size: .85rem; padding-left: 1.1rem; margin: 0; }
.trust { list-style: none; display: flex; flex-wrap: wrap; gap: 1.6rem; padding: 0; margin: 1.6rem 0 0; }
.trust li { color: var(--muted); font-size: .92rem; }
.trust strong { display: block; color: var(--ink); font-size: 1.25rem; }
.hero-art { position: relative; min-height: 360px; }
.hero-blob {
position: absolute; inset: 6% 4%; border-radius: 42% 58% 56% 44% / 50% 44% 56% 50%;
background: radial-gradient(120% 120% at 30% 20%, var(--sun), var(--brand));
filter: blur(2px); opacity: .95; animation: blob 9s ease-in-out infinite;
}
@keyframes blob {
0%, 100% { border-radius: 42% 58% 56% 44% / 50% 44% 56% 50%; }
50% { border-radius: 58% 42% 40% 60% / 44% 58% 42% 56%; }
}
.dish {
position: absolute; z-index: 2; display: flex; flex-direction: column; align-items: center; gap: .15rem;
background: var(--surface); border-radius: var(--r-lg); padding: .9rem 1rem; box-shadow: var(--sh-md);
font-size: 2rem; animation: float 5s ease-in-out infinite;
}
.dish span { font-size: .72rem; font-weight: 700; color: var(--ink-2); }
.dish-1 { top: 4%; left: 6%; animation-delay: 0s; }
.dish-2 { top: 12%; right: 2%; animation-delay: .6s; }
.dish-3 { bottom: 16%; left: 0; animation-delay: 1.2s; }
.dish-4 { bottom: 4%; right: 12%; animation-delay: 1.8s; }
@keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-12px); } }
/* categories */
.cats { padding: clamp(2rem, 5vw, 3.5rem) 0 1rem; }
.cat-row {
display: flex; gap: .75rem; overflow-x: auto; padding: .5rem .25rem 1rem;
scroll-snap-type: x mandatory; scrollbar-width: thin;
}
.cat {
scroll-snap-align: start; flex: 0 0 auto; display: flex; flex-direction: column; align-items: center; gap: .4rem;
background: var(--surface); border: 1.5px solid var(--line); border-radius: var(--r-md);
padding: .85rem 1.1rem; min-width: 88px; font: inherit; font-weight: 600; font-size: .85rem; cursor: pointer;
color: var(--ink-2); transition: transform .15s, border-color .15s, color .15s, box-shadow .15s;
}
.cat span { font-size: 1.7rem; }
.cat:hover { transform: translateY(-3px); box-shadow: var(--sh-sm); border-color: var(--sun); }
.cat.is-active { border-color: var(--brand); color: var(--brand); box-shadow: var(--sh-brand); }
/* restaurants */
.restaurants { padding: 1.5rem 0 clamp(3rem, 7vw, 5rem); }
.badge-deal {
background: var(--sun); color: var(--ink); font-weight: 700; font-size: .85rem;
padding: .45rem .85rem; border-radius: 999px; white-space: nowrap;
}
.rest-grid {
display: grid; grid-template-columns: repeat(auto-fill, minmax(252px, 1fr)); gap: 1.25rem;
}
.rest-card {
background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden;
cursor: pointer; transition: transform .18s, box-shadow .18s; display: flex; flex-direction: column;
}
.rest-card:hover { transform: translateY(-5px); box-shadow: var(--sh-md); }
.rest-thumb {
height: 132px; display: grid; place-items: center; font-size: 3.4rem; position: relative;
}
.rest-thumb .tag {
position: absolute; top: .65rem; left: .65rem; background: var(--surface); color: var(--brand-d);
font-size: .72rem; font-weight: 700; padding: .25rem .55rem; border-radius: 999px; box-shadow: var(--sh-sm);
}
.rest-thumb .fav {
position: absolute; top: .55rem; right: .55rem; width: 32px; height: 32px; border-radius: 50%;
border: 0; background: rgba(255, 255, 255, .92); cursor: pointer; font-size: .95rem; display: grid; place-items: center;
transition: transform .15s;
}
.rest-thumb .fav:hover { transform: scale(1.12); }
.rest-thumb .fav.on { color: var(--danger); }
.rest-body { padding: .95rem 1.05rem 1.15rem; display: flex; flex-direction: column; gap: .35rem; flex: 1; }
.rest-body h3 { font-size: 1.08rem; margin: 0; }
.rest-meta { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; color: var(--muted); font-size: .85rem; }
.rest-meta .rating { color: var(--ink); font-weight: 700; }
.rest-meta .star { color: var(--sun-d); }
.dot { width: 3px; height: 3px; border-radius: 50%; background: var(--muted); }
.rest-foot { margin-top: auto; display: flex; align-items: center; justify-content: space-between; padding-top: .65rem; }
.pill { font-size: .76rem; font-weight: 700; padding: .25rem .6rem; border-radius: 999px; }
.pill-ok { background: rgba(31, 157, 98, .12); color: var(--ok); }
.pill-free { background: rgba(255, 90, 44, .12); color: var(--brand-d); }
.rest-empty { text-align: center; color: var(--muted); padding: 2rem 0; }
/* how it works */
.how { background: var(--surface); border-block: 1px solid var(--line); padding: clamp(3rem, 7vw, 5rem) 0; }
.steps { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; }
.step {
position: relative; background: var(--bg); border: 1px solid var(--line); border-radius: var(--r-lg);
padding: 2rem 1.5rem 1.5rem; text-align: center;
}
.step-num {
position: absolute; top: -18px; left: 50%; transform: translateX(-50%);
width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center;
background: var(--brand); color: #fff; font-weight: 800; box-shadow: var(--sh-brand);
}
.step-ico { font-size: 2.4rem; display: block; margin-bottom: .5rem; }
.step h3 { font-size: 1.15rem; }
.step p { color: var(--muted); margin: 0; font-size: .95rem; }
/* app download */
.app { padding: clamp(3rem, 7vw, 5rem) 0; }
.app-grid {
display: grid; grid-template-columns: 1.05fr .95fr; gap: 3rem; align-items: center;
background: linear-gradient(135deg, var(--brand), var(--brand-d));
border-radius: var(--r-xl); padding: clamp(2rem, 5vw, 3.5rem); color: #fff;
box-shadow: var(--sh-md);
}
.app-copy h2 { color: #fff; font-size: clamp(1.8rem, 4vw, 2.6rem); }
.app-copy .lede { color: rgba(255, 255, 255, .9); max-width: 36ch; }
.app-form { display: flex; gap: .5rem; background: var(--surface); padding: .5rem; border-radius: 999px; max-width: 420px; margin-bottom: 1rem; }
.app-form input { flex: 1; border: 0; background: none; font: inherit; font-size: 1rem; padding: .55rem 1rem; min-width: 0; }
.app-form input:focus { outline: none; }
.stores { display: flex; gap: .65rem; flex-wrap: wrap; }
.store {
display: inline-flex; align-items: center; gap: .35rem; background: rgba(0, 0, 0, .22); color: #fff;
border: 1px solid rgba(255, 255, 255, .25); border-radius: var(--r-md); padding: .6rem 1rem;
font: inherit; font-weight: 700; cursor: pointer; transition: background .15s, transform .15s;
}
.store:hover { background: rgba(0, 0, 0, .35); transform: translateY(-2px); }
.app-phone { display: grid; place-items: center; }
.phone {
width: 250px; background: var(--surface); border-radius: 30px; padding: 1rem; color: var(--ink);
box-shadow: 0 24px 50px rgba(0, 0, 0, .28); border: 6px solid #1b1d22;
}
.phone-top { font-size: .9rem; color: var(--ink-2); text-align: center; margin-bottom: .65rem; }
.phone-top strong { color: var(--brand-d); }
.phone-map {
height: 150px; border-radius: var(--r-md); margin-bottom: .75rem; overflow: hidden;
background:
linear-gradient(rgba(91, 141, 239, .08), rgba(91, 141, 239, .08)),
repeating-linear-gradient(0deg, var(--line) 0 1px, transparent 1px 26px),
repeating-linear-gradient(90deg, var(--line) 0 1px, transparent 1px 26px),
#eef2fb;
}
.route-svg { width: 100%; height: 100%; }
.rider { animation: ride 4s ease-in-out infinite alternate; }
@keyframes ride { from { transform: translate(-104px, 74px); } to { transform: translate(2px, 2px); } }
.phone-card {
display: flex; align-items: center; gap: .6rem; background: var(--bg); border: 1px solid var(--line);
border-radius: var(--r-md); padding: .7rem;
}
.phone-ava { font-size: 1.5rem; }
.phone-card strong { display: block; font-size: .9rem; }
.phone-card small { color: var(--muted); font-size: .78rem; }
.phone-card .pill { margin-left: auto; }
/* partner */
.partner { padding: 0 0 clamp(3rem, 7vw, 5rem); }
.partner-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }
.cta-card {
border-radius: var(--r-xl); padding: clamp(1.8rem, 4vw, 2.6rem); border: 1px solid var(--line);
}
.cta-rest { background: linear-gradient(135deg, #fff, #fff2e9); }
.cta-drive { background: linear-gradient(135deg, var(--ink), #24272f); color: #fff; border-color: transparent; }
.cta-drive p { color: rgba(255, 255, 255, .82); }
.cta-ico { font-size: 2.5rem; display: block; margin-bottom: .5rem; }
.cta-card h3 { font-size: 1.4rem; }
.cta-card p { margin-bottom: 1.25rem; max-width: 34ch; }
/* footer */
.footer { background: var(--ink); color: #cfd2da; padding: 3rem 0 1.5rem; }
.footer-grid { display: grid; grid-template-columns: 1.4fr repeat(3, 1fr); gap: 2rem; padding-bottom: 2rem; border-bottom: 1px solid rgba(255, 255, 255, .1); }
.footer .brand { color: #fff; margin-bottom: .6rem; }
.footer-brand p { color: #8b909c; max-width: 30ch; font-size: .92rem; }
.footer h4 { color: #fff; font-size: .95rem; margin-bottom: .9rem; }
.footer nav a { display: block; padding: .3rem 0; color: #aeb2bd; font-size: .9rem; transition: color .15s; }
.footer nav a:hover { color: var(--sun); }
.footer-bottom { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding-top: 1.25rem; flex-wrap: wrap; }
.footer-bottom small { color: #8b909c; }
.footer-legal { display: flex; gap: 1.2rem; }
.footer-legal a { color: #aeb2bd; font-size: .85rem; }
.footer-legal a:hover { color: var(--sun); }
/* toast */
.toast-wrap { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); z-index: 200; display: flex; flex-direction: column; gap: .5rem; align-items: center; pointer-events: none; }
.toast {
background: var(--ink); color: #fff; padding: .75rem 1.1rem; border-radius: var(--r-md);
font-weight: 600; font-size: .9rem; box-shadow: var(--sh-md); display: flex; align-items: center; gap: .5rem;
opacity: 0; transform: translateY(12px); transition: opacity .25s, transform .25s; max-width: 90vw;
}
.toast.show { opacity: 1; transform: translateY(0); }
.toast::before { content: "🍴"; }
/* scroll reveal */
.reveal { opacity: 0; transform: translateY(22px); transition: opacity .6s ease, transform .6s ease; }
.reveal.in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
.reveal { opacity: 1; transform: none; }
}
/* responsive */
@media (max-width: 900px) {
.hero-grid, .app-grid { grid-template-columns: 1fr; gap: 2rem; }
.hero-art { min-height: 300px; order: -1; }
.steps { grid-template-columns: 1fr; gap: 2rem; }
.partner-grid { grid-template-columns: 1fr; }
.footer-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 760px) {
.nav-links, .nav-cta { display: none; }
.nav-toggle { display: flex; }
}
@media (max-width: 520px) {
.wrap { width: min(100% - 1.5rem, var(--wrap)); }
.section-head--row { flex-direction: column; align-items: flex-start; }
.address { flex-direction: column; border-radius: var(--r-lg); }
.address-field { padding: .5rem .85rem; }
.address-btn { width: 100%; }
.app-form { flex-direction: column; border-radius: var(--r-lg); }
.app-form .btn { width: 100%; }
.footer-grid { grid-template-columns: 1fr; gap: 1.5rem; }
.footer-bottom { flex-direction: column; align-items: flex-start; }
.trust { gap: 1.1rem; }
}(function () {
"use strict";
/* ---------- toast helper ---------- */
var toastWrap = document.getElementById("toastWrap");
function toast(msg) {
var t = document.createElement("div");
t.className = "toast";
t.textContent = msg;
toastWrap.appendChild(t);
requestAnimationFrame(function () { t.classList.add("show"); });
setTimeout(function () {
t.classList.remove("show");
setTimeout(function () { t.remove(); }, 300);
}, 2600);
}
/* ---------- mobile nav ---------- */
var navToggle = document.getElementById("navToggle");
var mobileNav = document.getElementById("mobileNav");
function closeNav() {
navToggle.setAttribute("aria-expanded", "false");
navToggle.setAttribute("aria-label", "Open menu");
mobileNav.hidden = true;
}
navToggle.addEventListener("click", function () {
var open = navToggle.getAttribute("aria-expanded") === "true";
navToggle.setAttribute("aria-expanded", String(!open));
navToggle.setAttribute("aria-label", open ? "Open menu" : "Close menu");
mobileNav.hidden = open;
});
mobileNav.addEventListener("click", function (e) {
if (e.target.closest("a")) closeNav();
});
document.addEventListener("keydown", function (e) {
if (e.key === "Escape" && navToggle.getAttribute("aria-expanded") === "true") closeNav();
});
/* ---------- restaurant data ---------- */
var RESTAURANTS = [
{ name: "Pizza Forno", emoji: "🍕", cat: "pizza", rating: 4.8, reviews: 1240, time: 25, fee: "Free", tag: "Popular", cuisine: "Wood-fired pizza", deal: true },
{ name: "Smash House", emoji: "🍔", cat: "burgers", rating: 4.7, reviews: 980, time: 22, fee: "$1.99", tag: "Trending", cuisine: "Smash burgers", deal: false },
{ name: "Sakura Sushi", emoji: "🍣", cat: "sushi", rating: 4.9, reviews: 760, time: 31, fee: "Free", tag: "Top rated", cuisine: "Sushi & rolls", deal: true },
{ name: "Ramen Bar 88", emoji: "🍜", cat: "ramen", rating: 4.6, reviews: 540, time: 28, fee: "$0.99", tag: "New", cuisine: "Tonkotsu ramen", deal: false },
{ name: "Green Bowl Co.", emoji: "🥗", cat: "healthy", rating: 4.7, reviews: 612, time: 19, fee: "Free", tag: "Healthy", cuisine: "Salads & grain bowls", deal: true },
{ name: "Sugar Cloud", emoji: "🍰", cat: "dessert", rating: 4.8, reviews: 430, time: 24, fee: "$2.49", tag: "Sweet", cuisine: "Cakes & pastries", deal: false },
{ name: "El Taco Loco", emoji: "🌮", cat: "tacos", rating: 4.5, reviews: 870, time: 21, fee: "Free", tag: "Local fave", cuisine: "Street tacos", deal: true },
{ name: "Noodle Lane", emoji: "🍝", cat: "ramen", rating: 4.4, reviews: 388, time: 27, fee: "$1.49", tag: "Comfort", cuisine: "Asian noodles", deal: false },
{ name: "Bella Slice", emoji: "🍕", cat: "pizza", rating: 4.6, reviews: 705, time: 23, fee: "Free", tag: "Family", cuisine: "Neapolitan pizza", deal: true },
{ name: "Patty & Bun", emoji: "🍔", cat: "burgers", rating: 4.5, reviews: 521, time: 26, fee: "$0.99", tag: "Juicy", cuisine: "Gourmet burgers", deal: false },
{ name: "Fresh Press", emoji: "🥗", cat: "healthy", rating: 4.8, reviews: 299, time: 18, fee: "Free", tag: "Vegan", cuisine: "Cold-press & bowls", deal: true },
{ name: "Roll & Go", emoji: "🍣", cat: "sushi", rating: 4.6, reviews: 466, time: 29, fee: "$1.99", tag: "Quick", cuisine: "Sushi to-go", deal: false }
];
var grid = document.getElementById("restGrid");
var emptyEl = document.getElementById("restEmpty");
var countEl = document.getElementById("restCount");
var activeCat = "all";
function cardHTML(r, i) {
var feeClass = r.fee === "Free" ? "pill-free" : "pill-ok";
var feeLabel = r.fee === "Free" ? "Free delivery" : r.fee + " delivery";
return (
'<article class="rest-card" tabindex="0" role="button" data-name="' + r.name + '" style="transition-delay:' + (i * 30) + 'ms">' +
'<div class="rest-thumb" style="background:linear-gradient(135deg,#fff2e9,#ffe1cf)">' +
'<span class="tag">' + r.tag + "</span>" +
'<button class="fav" type="button" aria-label="Save ' + r.name + '" title="Save">♡</button>' +
"<span>" + r.emoji + "</span>" +
"</div>" +
'<div class="rest-body">' +
"<h3>" + r.name + "</h3>" +
'<div class="rest-meta">' +
'<span class="rating"><span class="star">★</span> ' + r.rating.toFixed(1) + "</span>" +
'<span class="dot"></span><span>' + r.reviews + "+ ratings</span>" +
"</div>" +
'<div class="rest-meta"><span>' + r.cuisine + '</span><span class="dot"></span><span>' + r.time + " min</span></div>" +
'<div class="rest-foot">' +
'<span class="pill ' + feeClass + '">' + feeLabel + "</span>" +
(r.deal ? '<span class="pill pill-free">🔥 Deal</span>' : "") +
"</div>" +
"</div>" +
"</article>"
);
}
function render() {
var list = activeCat === "all" ? RESTAURANTS : RESTAURANTS.filter(function (r) { return r.cat === activeCat; });
grid.innerHTML = list.map(cardHTML).join("");
emptyEl.hidden = list.length > 0;
countEl.textContent = "Showing " + list.length + " kitchen" + (list.length === 1 ? "" : "s");
}
render();
/* card + fav interactions (event delegation) */
grid.addEventListener("click", function (e) {
var fav = e.target.closest(".fav");
if (fav) {
e.stopPropagation();
var on = fav.classList.toggle("on");
fav.textContent = on ? "♥" : "♡";
toast(on ? "Saved to favorites" : "Removed from favorites");
return;
}
var card = e.target.closest(".rest-card");
if (card) toast("Opening " + card.dataset.name + " menu…");
});
grid.addEventListener("keydown", function (e) {
var card = e.target.closest(".rest-card");
if (card && (e.key === "Enter" || e.key === " ")) {
e.preventDefault();
toast("Opening " + card.dataset.name + " menu…");
}
});
/* ---------- category tabs ---------- */
var catRow = document.getElementById("catRow");
catRow.addEventListener("click", function (e) {
var btn = e.target.closest(".cat");
if (!btn) return;
catRow.querySelectorAll(".cat").forEach(function (b) {
b.classList.remove("is-active");
b.setAttribute("aria-selected", "false");
});
btn.classList.add("is-active");
btn.setAttribute("aria-selected", "true");
activeCat = btn.dataset.cat;
render();
revealNew();
var label = btn.textContent.trim();
toast(activeCat === "all" ? "Showing all kitchens" : "Filtered to " + label);
});
/* ---------- address form ---------- */
var addressForm = document.getElementById("addressForm");
var addressInput = document.getElementById("hero-address");
var addressHint = document.getElementById("addressHint");
addressForm.addEventListener("submit", function (e) {
e.preventDefault();
var val = addressInput.value.trim();
if (val.length < 4) {
addressInput.focus();
addressHint.textContent = "Please enter a fuller address to find kitchens.";
addressHint.style.color = "var(--danger)";
toast("Add a delivery address first");
return;
}
addressHint.style.color = "";
addressHint.textContent = "Delivering to: " + val;
toast("Found 24 kitchens near " + val.split(",")[0]);
document.getElementById("restaurants").scrollIntoView({ behavior: "smooth" });
});
/* ---------- app SMS form ---------- */
var appForm = document.getElementById("appForm");
appForm.addEventListener("submit", function (e) {
e.preventDefault();
var phone = e.target.querySelector("input").value.trim();
var digits = phone.replace(/\D/g, "");
if (digits.length < 7) {
toast("Enter a valid phone number");
return;
}
toast("Link sent! Check your messages 📲");
e.target.reset();
});
/* store + partner buttons */
document.querySelectorAll(".store").forEach(function (b) {
b.addEventListener("click", function () { toast("Redirecting to " + b.dataset.store + "…"); });
});
document.querySelectorAll("[data-partner]").forEach(function (b) {
b.addEventListener("click", function (e) {
e.preventDefault();
toast(b.dataset.partner === "driver" ? "Starting rider sign-up…" : "Opening partner application…");
});
});
/* ---------- scroll reveal ---------- */
var io;
if ("IntersectionObserver" in window) {
io = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (en.isIntersecting) {
en.target.classList.add("in");
io.unobserve(en.target);
}
});
}, { threshold: 0.12, rootMargin: "0px 0px -40px 0px" });
document.querySelectorAll(".reveal").forEach(function (el) { io.observe(el); });
} else {
document.querySelectorAll(".reveal").forEach(function (el) { el.classList.add("in"); });
}
function revealNew() {
document.querySelectorAll(".rest-card.reveal:not(.in)").forEach(function (el) {
if (io) io.observe(el); else el.classList.add("in");
});
}
/* ---------- shrink-nav shadow on scroll ---------- */
var nav = document.querySelector(".nav");
var lastShadow = false;
window.addEventListener("scroll", function () {
var on = window.scrollY > 8;
if (on !== lastShadow) {
nav.style.boxShadow = on ? "var(--sh-sm)" : "none";
lastShadow = on;
}
}, { passive: true });
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Forklet — Hot food, fast to your door</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<header class="nav" id="top">
<div class="wrap nav-inner">
<a class="brand" href="#top" aria-label="Forklet home">
<span class="brand-mark" aria-hidden="true">🍴</span>
<span class="brand-name">Forklet</span>
</a>
<nav class="nav-links" aria-label="Primary">
<a href="#restaurants">Restaurants</a>
<a href="#how">How it works</a>
<a href="#app">Get the app</a>
<a href="#partner">Partner with us</a>
</nav>
<div class="nav-cta">
<a class="btn btn-ghost" href="#app">Log in</a>
<a class="btn btn-solid" href="#hero-address">Order now</a>
</div>
<button class="nav-toggle" id="navToggle" aria-expanded="false" aria-controls="mobileNav" aria-label="Open menu">
<span></span><span></span><span></span>
</button>
</div>
<div class="mobile-nav" id="mobileNav" hidden>
<a href="#restaurants">Restaurants</a>
<a href="#how">How it works</a>
<a href="#app">Get the app</a>
<a href="#partner">Partner with us</a>
<div class="mobile-nav-cta">
<a class="btn btn-ghost" href="#app">Log in</a>
<a class="btn btn-solid" href="#hero-address">Order now</a>
</div>
</div>
</header>
<main id="main">
<!-- HERO -->
<section class="hero" aria-labelledby="hero-title">
<div class="wrap hero-grid">
<div class="hero-copy reveal">
<span class="eyebrow"><span class="pulse" aria-hidden="true"></span> Avg. 28 min delivery near you</span>
<h1 id="hero-title">Hot food, <span class="hl">fast</span> to your door.</h1>
<p class="lede">Thousands of local kitchens, one tasty app. Type your address and we'll show what's cooking around the corner.</p>
<form class="address" id="addressForm" novalidate>
<div class="address-field">
<span class="pin" aria-hidden="true">📍</span>
<label class="vh" for="hero-address">Delivery address</label>
<input id="hero-address" name="address" type="text" autocomplete="street-address"
placeholder="Enter your delivery address" />
</div>
<button class="btn btn-solid address-btn" type="submit">Find food</button>
</form>
<p class="address-hint" id="addressHint">e.g. 240 Maple Street, Riverton</p>
<ul class="trust">
<li><strong>1,800+</strong> restaurants</li>
<li><strong>4.8★</strong> avg rating</li>
<li><strong>Free</strong> delivery over $20</li>
</ul>
</div>
<div class="hero-art reveal" aria-hidden="true">
<div class="dish dish-1">🍜<span>Ramen Bar</span></div>
<div class="dish dish-2">🍕<span>Pizza Forno</span></div>
<div class="dish dish-3">🥗<span>Green Bowl</span></div>
<div class="dish dish-4">🍔<span>Smash House</span></div>
<div class="hero-blob"></div>
</div>
</div>
</section>
<!-- CATEGORIES -->
<section class="cats wrap" aria-labelledby="cats-title">
<div class="section-head">
<h2 id="cats-title">Crave something?</h2>
<p>Tap a cuisine to filter the kitchens below.</p>
</div>
<div class="cat-row" id="catRow" role="tablist" aria-label="Cuisine categories">
<button class="cat is-active" role="tab" aria-selected="true" data-cat="all"><span>🍽️</span>All</button>
<button class="cat" role="tab" aria-selected="false" data-cat="pizza"><span>🍕</span>Pizza</button>
<button class="cat" role="tab" aria-selected="false" data-cat="burgers"><span>🍔</span>Burgers</button>
<button class="cat" role="tab" aria-selected="false" data-cat="sushi"><span>🍣</span>Sushi</button>
<button class="cat" role="tab" aria-selected="false" data-cat="ramen"><span>🍜</span>Noodles</button>
<button class="cat" role="tab" aria-selected="false" data-cat="healthy"><span>🥗</span>Healthy</button>
<button class="cat" role="tab" aria-selected="false" data-cat="dessert"><span>🍰</span>Dessert</button>
<button class="cat" role="tab" aria-selected="false" data-cat="tacos"><span>🌮</span>Tacos</button>
</div>
</section>
<!-- RESTAURANTS -->
<section class="restaurants wrap reveal" id="restaurants" aria-labelledby="rest-title">
<div class="section-head section-head--row">
<div>
<h2 id="rest-title">Featured near you</h2>
<p id="restCount">Showing 8 kitchens</p>
</div>
<span class="badge-deal">🔥 Free delivery week</span>
</div>
<div class="rest-grid" id="restGrid"><!-- cards injected by script --></div>
<p class="rest-empty" id="restEmpty" hidden>No kitchens match that cuisine yet — try another.</p>
</section>
<!-- HOW IT WORKS -->
<section class="how" id="how" aria-labelledby="how-title">
<div class="wrap">
<div class="section-head reveal">
<h2 id="how-title">Three taps to dinner</h2>
<p>From craving to doorstep, no phone calls required.</p>
</div>
<ol class="steps">
<li class="step reveal">
<span class="step-num">1</span>
<span class="step-ico" aria-hidden="true">📍</span>
<h3>Set your spot</h3>
<p>Drop your address and browse kitchens delivering to you right now.</p>
</li>
<li class="step reveal">
<span class="step-num">2</span>
<span class="step-ico" aria-hidden="true">🛒</span>
<h3>Build your order</h3>
<p>Add dishes, customize extras, and check out in seconds with saved cards.</p>
</li>
<li class="step reveal">
<span class="step-num">3</span>
<span class="step-ico" aria-hidden="true">🛵</span>
<h3>Track to your door</h3>
<p>Watch your rider on the map and get a ping the moment food lands.</p>
</li>
</ol>
</div>
</section>
<!-- APP DOWNLOAD -->
<section class="app" id="app" aria-labelledby="app-title">
<div class="wrap app-grid">
<div class="app-copy reveal">
<span class="eyebrow eyebrow--light">Forklet app</span>
<h2 id="app-title">Order faster from your pocket.</h2>
<p class="lede">Live tracking, one-tap reorders, and member-only deals. Text yourself a download link.</p>
<form class="app-form" id="appForm" novalidate>
<label class="vh" for="phone">Phone number</label>
<input id="phone" name="phone" type="tel" inputmode="tel" placeholder="(555) 018-2240" />
<button class="btn btn-dark" type="submit">Text me the app</button>
</form>
<div class="stores" aria-label="App stores">
<button class="store" type="button" data-store="App Store"> App Store</button>
<button class="store" type="button" data-store="Google Play">▶ Google Play</button>
</div>
</div>
<div class="app-phone reveal" aria-hidden="true">
<div class="phone">
<div class="phone-top">Arriving in <strong>9 min</strong></div>
<div class="phone-map">
<svg viewBox="0 0 200 140" class="route-svg" aria-hidden="true">
<path d="M14 120 C 60 110, 70 40, 120 44 S 180 30, 186 22" fill="none" stroke="var(--track)" stroke-width="4" stroke-dasharray="6 7" stroke-linecap="round"/>
<circle cx="14" cy="120" r="6" fill="var(--brand)"/>
<circle cx="186" cy="22" r="6" fill="var(--ok)"/>
<circle class="rider" cx="120" cy="44" r="7" fill="#fff" stroke="var(--brand)" stroke-width="4"/>
</svg>
</div>
<div class="phone-card">
<span class="phone-ava">🛵</span>
<div>
<strong>Marco is on the way</strong>
<small>Smash House · 2 items</small>
</div>
<span class="pill pill-ok">On time</span>
</div>
</div>
</div>
</div>
</section>
<!-- PARTNER / DRIVER CTAS -->
<section class="partner" id="partner" aria-labelledby="partner-title">
<div class="wrap partner-grid">
<h2 id="partner-title" class="vh">Work with Forklet</h2>
<article class="cta-card cta-rest reveal">
<span class="cta-ico" aria-hidden="true">🏪</span>
<h3>List your restaurant</h3>
<p>Reach hungry locals and grow online orders with zero setup fees this month.</p>
<a class="btn btn-solid" href="#" data-partner="restaurant">Become a partner</a>
</article>
<article class="cta-card cta-drive reveal">
<span class="cta-ico" aria-hidden="true">🛵</span>
<h3>Deliver & earn</h3>
<p>Flexible hours, weekly payouts, and keep 100% of your tips. Ride or drive.</p>
<a class="btn btn-dark" href="#" data-partner="driver">Start earning</a>
</article>
</div>
</section>
</main>
<footer class="footer">
<div class="wrap footer-grid">
<div class="footer-brand">
<span class="brand"><span class="brand-mark" aria-hidden="true">🍴</span><span class="brand-name">Forklet</span></span>
<p>Hot food, fast — from the kitchens you love.</p>
</div>
<nav aria-label="Company">
<h4>Company</h4>
<a href="#">About</a><a href="#">Careers</a><a href="#">Press</a><a href="#">Blog</a>
</nav>
<nav aria-label="For you">
<h4>For you</h4>
<a href="#restaurants">Restaurants</a><a href="#how">How it works</a><a href="#app">Get the app</a>
</nav>
<nav aria-label="Partners">
<h4>Partners</h4>
<a href="#partner">Add your restaurant</a><a href="#partner">Become a rider</a><a href="#">Help center</a>
</nav>
</div>
<div class="wrap footer-bottom">
<small>© 2026 Forklet Foods. Fictional brand for demo purposes.</small>
<div class="footer-legal"><a href="#">Privacy</a><a href="#">Terms</a><a href="#">Cookies</a></div>
</div>
</footer>
<div class="toast-wrap" id="toastWrap" aria-live="polite" aria-atomic="true"></div>
<script src="script.js"></script>
</body>
</html>Food Delivery Landing
A full marketing landing page for Forklet, a fictional food-delivery service, built in an appetite-driven palette of white, hot orange and sunny yellow with a friendly bold sans. The sticky top bar collapses into an animated hamburger menu on small screens, and the hero leads with a big crave-able headline, a live ETA eyebrow, and a pill-shaped address bar. Submitting the address validates the input, echoes the chosen delivery zone, and smooth-scrolls down to the kitchens — floating dish cards drift over a morphing gradient blob beside it.
The heart of the page is a live, filterable restaurant grid. A horizontally scrollable cuisine rail (pizza, burgers, sushi, noodles, healthy, dessert, tacos) acts as a tab strip that re-renders the cards client-side and updates the visible count, with a graceful empty state. Each kitchen card shows an emoji thumbnail, deal tag, star rating, review count, cuisine, ETA, and a delivery-fee pill, plus a heart button that toggles favorites. Cards are keyboard-operable and every action confirms through a small toast helper.
Further down, a three-step “how it works” tracker, a phone-mockup app section with an animated rider gliding along an SVG route and an SMS-link capture form, and dual partner CTAs for restaurants and riders round out the story. A four-column footer, scroll-reveal animations via IntersectionObserver, and a reduced-motion fallback keep the whole thing polished from desktop down to a 360px mobile screen.
Illustrative UI only — fictional brand, not a real delivery service.