Delivery — Last-mile / Instant Landing
A high-energy last-mile delivery landing page for a fictional instant-courier brand, built in pure HTML, CSS and vanilla JS. It pairs a bold black-and-lime hero with an address bar and a live ETA tracker card — an animated rider marker following an SVG route, a ticking countdown, status pills and a step tracker. Below sit use-case cards, an interactive coverage-cities grid, a slider-driven rider earnings calculator, an app-download section with a phone mock, plus a mobile nav, scroll reveals and toast feedback.
MCP
Code
:root {
/* Last-mile / instant — black + electric lime */
--bg: #0b0b0f;
--bg-2: #121218;
--surface: #16161d;
--surface-2: #1d1d26;
--ink: #f6f7f4;
--ink-2: #c7c9c2;
--muted: #8a8d86;
--line: rgba(255,255,255,0.1);
--line-2: rgba(255,255,255,0.06);
--lime: #c6ff00;
--lime-d: #a8db00;
--lime-ink: #0b0b0f;
--ok: #2fd07a;
--warn: #ffb020;
--danger: #ff5a4d;
--track: #5b8def;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 20px;
--r-xl: 28px;
--shadow: 0 10px 30px -12px rgba(0,0,0,0.6);
--shadow-lime: 0 12px 34px -10px rgba(198,255,0,0.4);
--maxw: 1160px;
--ease: cubic-bezier(.22,.61,.36,1);
}
* { box-sizing: border-box; }
* { margin: 0; }
html { scroll-behavior: smooth; }
body {
font-family: "Inter", system-ui, -apple-system, sans-serif;
background: var(--bg);
color: var(--ink);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
overflow-x: hidden;
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
html { scroll-behavior: auto; }
}
img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }
.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: 22px; }
.sr-only {
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: -60px; z-index: 200;
background: var(--lime); color: var(--lime-ink); padding: 10px 16px;
border-radius: var(--r-sm); font-weight: 700; transition: top .2s var(--ease);
}
.skip-link:focus { top: 12px; }
:focus-visible { outline: 3px solid var(--lime); outline-offset: 2px; border-radius: 6px; }
/* ===== Buttons ===== */
.btn {
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
padding: 12px 20px; border: 1px solid transparent; border-radius: 999px;
font-weight: 700; font-size: 15px; letter-spacing: .01em;
transition: transform .15s var(--ease), background .2s var(--ease), box-shadow .2s var(--ease);
white-space: nowrap;
}
.btn:active { transform: translateY(1px) scale(.99); }
.btn-lime { background: var(--lime); color: var(--lime-ink); box-shadow: var(--shadow-lime); }
.btn-lime:hover { background: var(--lime-d); transform: translateY(-2px); }
.btn-ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn-ghost:hover { background: var(--surface-2); border-color: var(--lime); }
.btn-dark { background: var(--surface-2); color: var(--ink); border-color: var(--line); }
.btn-dark:hover { background: #26262f; }
.btn-lg { padding: 15px 28px; font-size: 16px; }
.btn-block { width: 100%; }
.pill {
display: inline-flex; align-items: center; gap: 6px;
padding: 4px 11px; border-radius: 999px; font-size: 12px; font-weight: 700;
letter-spacing: .02em;
}
.pill-ok { background: rgba(47,208,122,.16); color: var(--ok); }
.pill-lime { background: rgba(198,255,0,.16); color: var(--lime); }
.pill-warn { background: rgba(255,176,32,.16); color: var(--warn); }
.kicker {
display: inline-block; font-size: 12px; font-weight: 800; letter-spacing: .14em;
text-transform: uppercase; color: var(--lime); margin-bottom: 14px;
}
.kicker-dark { color: var(--lime-ink); background: var(--lime); padding: 3px 10px; border-radius: 999px; }
.hl { color: var(--lime); }
/* ===== Nav ===== */
.nav {
position: sticky; top: 0; z-index: 100;
background: rgba(11,11,15,.82); backdrop-filter: blur(14px);
border-bottom: 1px solid var(--line-2);
}
.nav-inner { display: flex; align-items: center; gap: 18px; height: 68px; }
.brand { display: inline-flex; align-items: center; gap: 9px; font-weight: 800; }
.brand-mark {
display: grid; place-items: center; width: 34px; height: 34px;
background: var(--lime); color: var(--lime-ink); border-radius: 10px;
}
.brand-name { font-size: 19px; letter-spacing: -.01em; }
.nav-links { display: flex; gap: 26px; margin-left: 18px; margin-right: auto; }
.nav-links a { color: var(--ink-2); font-weight: 500; font-size: 15px; transition: color .15s; }
.nav-links a:hover { color: var(--lime); }
.nav-cta { display: flex; gap: 10px; }
.hamburger {
display: none; flex-direction: column; gap: 5px; background: none; border: 0;
padding: 8px; margin-left: auto;
}
.hamburger span { width: 24px; height: 2.5px; background: var(--ink); border-radius: 2px; transition: .25s var(--ease); }
.hamburger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7.5px) rotate(45deg); }
.hamburger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.hamburger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7.5px) rotate(-45deg); }
.mobile-nav {
display: flex; flex-direction: column; gap: 4px; padding: 12px 22px 22px;
border-bottom: 1px solid var(--line-2); background: var(--bg-2);
}
.mobile-nav a { padding: 12px 4px; color: var(--ink-2); font-weight: 600; border-bottom: 1px solid var(--line-2); }
.mobile-nav a.btn { border: 1px solid transparent; margin-top: 8px; color: var(--lime-ink); }
/* ===== Hero ===== */
.hero { position: relative; padding: 64px 0 0; overflow: hidden; }
.hero::before {
content: ""; position: absolute; top: -160px; right: -120px; width: 520px; height: 520px;
background: radial-gradient(circle, rgba(198,255,0,.18), transparent 62%); pointer-events: none;
}
.hero-grid {
display: grid; grid-template-columns: 1.05fr .95fr; gap: 48px; align-items: center;
}
.eyebrow {
display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600;
color: var(--ink-2); background: var(--surface); border: 1px solid var(--line);
padding: 6px 13px; border-radius: 999px; margin-bottom: 22px;
}
.pulse-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--lime); box-shadow: 0 0 0 0 rgba(198,255,0,.6); animation: pulse 1.8s infinite; }
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(198,255,0,.6); }
70% { box-shadow: 0 0 0 9px rgba(198,255,0,0); }
100% { box-shadow: 0 0 0 0 rgba(198,255,0,0); }
}
.hero-copy h1 {
font-size: clamp(2.5rem, 5.6vw, 4.1rem); font-weight: 900; line-height: 1.02;
letter-spacing: -.03em; margin-bottom: 18px;
}
.lede { font-size: clamp(1rem, 1.4vw, 1.18rem); color: var(--ink-2); max-width: 30em; margin-bottom: 28px; }
.addr-bar {
display: flex; align-items: center; gap: 8px; background: var(--surface);
border: 1px solid var(--line); border-radius: 999px; padding: 7px 7px 7px 16px;
max-width: 480px; transition: border-color .2s, box-shadow .2s;
}
.addr-bar:focus-within { border-color: var(--lime); box-shadow: 0 0 0 4px rgba(198,255,0,.12); }
.addr-bar .pin { color: var(--lime); flex: none; }
.addr-bar input {
flex: 1; min-width: 0; background: none; border: 0; color: var(--ink);
font-size: 16px; padding: 10px 4px;
}
.addr-bar input::placeholder { color: var(--muted); }
.addr-bar input:focus { outline: none; }
.hero-stats { display: flex; gap: 34px; list-style: none; margin-top: 32px; }
.hero-stats li { display: flex; flex-direction: column; }
.hero-stats strong { font-size: 2rem; font-weight: 800; letter-spacing: -.02em; }
.hero-stats span { font-size: 13px; color: var(--muted); }
/* ETA card */
.eta-card {
background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-xl);
box-shadow: var(--shadow); overflow: hidden;
}
.map {
position: relative; height: 220px;
background:
linear-gradient(rgba(91,141,239,.05) 1px, transparent 1px) 0 0/100% 32px,
linear-gradient(90deg, rgba(91,141,239,.05) 1px, transparent 1px) 0 0/32px 100%,
radial-gradient(circle at 70% 20%, rgba(198,255,0,.07), transparent 60%),
var(--bg-2);
}
.map .route { position: absolute; inset: 0; width: 100%; height: 100%; }
#routePath { animation: dash 1.4s linear infinite; }
@keyframes dash { to { stroke-dashoffset: -32; } }
.map-store, .map-home {
position: absolute; font-size: 22px; display: grid; place-items: center;
width: 34px; height: 34px; background: var(--surface); border: 1px solid var(--line);
border-radius: 10px;
}
.map-store { left: 14px; bottom: 14px; }
.map-home { right: 14px; top: 14px; box-shadow: 0 0 0 4px rgba(198,255,0,.18); }
.rider {
position: absolute; left: 0; top: 0; width: 28px; height: 28px;
background: var(--lime); border-radius: 50%; display: grid; place-items: center;
box-shadow: 0 4px 12px rgba(0,0,0,.4); transform: translate(-50%,-50%);
offset-path: path("M28 188 C 90 150, 70 96, 150 90 S 250 70, 292 34");
animation: ride 9s linear infinite;
}
@supports not (offset-path: path("M0 0")) { .rider { left: 50%; top: 45%; } }
@keyframes ride { from { offset-distance: 0%; } to { offset-distance: 100%; } }
.eta-body { padding: 18px 20px 22px; }
.eta-head { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 14px; }
.eta-label { display: block; font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .1em; }
.eta-time { font-size: 2.4rem; font-weight: 800; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.eta-time i { color: var(--muted); font-style: normal; }
.eta-track { height: 6px; background: var(--surface-2); border-radius: 999px; overflow: hidden; margin-bottom: 16px; }
.eta-fill { height: 100%; width: 62%; background: linear-gradient(90deg, var(--lime-d), var(--lime)); border-radius: 999px; transition: width .8s var(--ease); }
.eta-steps { list-style: none; display: flex; flex-direction: column; gap: 12px; }
.eta-steps li { display: flex; align-items: center; gap: 11px; font-size: 14px; color: var(--muted); }
.eta-steps .dot { width: 12px; height: 12px; border-radius: 50%; border: 2px solid var(--line); flex: none; }
.eta-steps li.done { color: var(--ink-2); }
.eta-steps li.done .dot { background: var(--ok); border-color: var(--ok); }
.eta-steps li.active { color: var(--ink); font-weight: 600; }
.eta-steps li.active .dot { background: var(--lime); border-color: var(--lime); box-shadow: 0 0 0 4px rgba(198,255,0,.18); }
/* Speed marquee */
.speed-strip { margin-top: 56px; border-top: 1px solid var(--line-2); border-bottom: 1px solid var(--line-2); background: var(--lime); overflow: hidden; }
.speed-track {
display: flex; gap: 26px; white-space: nowrap; padding: 13px 0;
color: var(--lime-ink); font-weight: 900; font-size: 14px; letter-spacing: .12em;
animation: marquee 18s linear infinite; width: max-content;
}
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
/* ===== Sections ===== */
.section { padding: 84px 0; }
.sec-head { max-width: 36em; margin-bottom: 44px; }
.sec-head h2, .cov-copy h2, .rider-copy h2, .app-copy h2 {
font-size: clamp(1.9rem, 3.6vw, 2.8rem); font-weight: 800; letter-spacing: -.025em; line-height: 1.08; margin-bottom: 14px;
}
.sec-head p, .cov-copy p, .rider-copy p, .app-copy p { color: var(--ink-2); font-size: 1.05rem; }
/* Use cases */
.use-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; }
.use-card {
background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
padding: 24px 22px; transition: transform .2s var(--ease), border-color .2s, box-shadow .2s;
position: relative;
}
.use-card:hover, .use-card:focus-visible { transform: translateY(-6px); border-color: var(--lime); box-shadow: var(--shadow); }
.use-ico {
display: grid; place-items: center; width: 48px; height: 48px; font-size: 26px;
background: var(--surface-2); border-radius: 14px; margin-bottom: 16px;
}
.use-card h3 { font-size: 1.15rem; font-weight: 700; margin-bottom: 6px; }
.use-card p { color: var(--muted); font-size: 14px; }
.use-eta {
display: inline-block; margin-top: 16px; font-size: 12px; font-weight: 700;
color: var(--lime); background: rgba(198,255,0,.1); padding: 4px 10px; border-radius: 999px;
}
/* Coverage */
.coverage { background: var(--bg-2); border-block: 1px solid var(--line-2); }
.cov-grid { display: grid; grid-template-columns: 1fr 1.1fr; gap: 48px; align-items: center; }
.cov-note { margin-top: 16px; font-weight: 600; color: var(--lime); min-height: 1.5em; }
.city-list { list-style: none; display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.city {
width: 100%; display: flex; align-items: center; justify-content: space-between;
padding: 16px 18px; background: var(--surface); border: 1px solid var(--line);
border-radius: var(--r-md); color: var(--ink); font-weight: 600; font-size: 16px;
transition: border-color .15s, transform .15s, background .2s;
}
.city span { font-size: 13px; color: var(--lime); font-weight: 700; }
.city:hover { transform: translateY(-2px); border-color: var(--lime); }
.city.active { background: var(--lime); color: var(--lime-ink); border-color: var(--lime); }
.city.active span { color: var(--lime-ink); }
/* Riders */
.riders { background: var(--lime); color: var(--lime-ink); }
.rider-grid { display: grid; grid-template-columns: 1.1fr .9fr; gap: 48px; align-items: center; }
.rider-copy h2 { color: var(--lime-ink); }
.rider-copy p { color: rgba(11,11,15,.78); }
.rider-perks { list-style: none; display: flex; flex-direction: column; gap: 12px; margin: 24px 0 30px; }
.rider-perks li { display: flex; align-items: center; gap: 12px; font-weight: 600; font-size: 16px; }
.rider-perks span { font-size: 20px; }
.riders .btn-lime { background: var(--lime-ink); color: var(--lime); box-shadow: 0 12px 30px -12px rgba(0,0,0,.5); }
.riders .btn-lime:hover { background: #000; }
.earn-card {
background: var(--bg); color: var(--ink); border-radius: var(--r-xl);
padding: 28px; box-shadow: 0 24px 50px -20px rgba(0,0,0,.55);
}
.earn-head { display: flex; align-items: center; justify-content: space-between; color: var(--muted); font-size: 14px; font-weight: 600; }
.earn-amount { font-size: 3.2rem; font-weight: 900; letter-spacing: -.03em; margin: 8px 0 22px; font-variant-numeric: tabular-nums; }
.earn-amount small { font-size: 1rem; font-weight: 500; color: var(--muted); margin-left: 4px; }
.earn-label { display: block; font-size: 14px; color: var(--ink-2); margin-bottom: 12px; }
.earn-label b { color: var(--lime); }
input[type="range"] {
width: 100%; -webkit-appearance: none; appearance: none; height: 6px;
background: var(--surface-2); border-radius: 999px; outline-offset: 4px;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none; appearance: none; width: 22px; height: 22px; border-radius: 50%;
background: var(--lime); border: 4px solid var(--bg); box-shadow: 0 0 0 1px var(--lime); cursor: grab;
}
input[type="range"]::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: var(--lime); border: 4px solid var(--bg); cursor: grab; }
.earn-foot { display: flex; justify-content: space-between; margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--line); font-size: 14px; color: var(--muted); }
.earn-foot b { color: var(--ink); }
/* App */
.app-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; align-items: center; }
.store-row { display: flex; gap: 12px; margin: 26px 0 18px; flex-wrap: wrap; }
.store-btn {
display: inline-flex; align-items: center; gap: 11px; background: var(--surface);
border: 1px solid var(--line); border-radius: var(--r-md); padding: 11px 18px; color: var(--ink);
transition: border-color .15s, transform .15s, background .2s;
}
.store-btn:hover { border-color: var(--lime); transform: translateY(-2px); }
.store-btn span { display: flex; flex-direction: column; line-height: 1.15; text-align: left; font-weight: 700; font-size: 16px; }
.store-btn small { font-size: 10px; font-weight: 500; color: var(--muted); letter-spacing: .04em; text-transform: uppercase; }
.sms { display: flex; gap: 10px; max-width: 440px; }
.sms input {
flex: 1; min-width: 0; background: var(--surface); border: 1px solid var(--line);
border-radius: 999px; padding: 13px 18px; color: var(--ink); font-size: 15px;
}
.sms input:focus { outline: none; border-color: var(--lime); box-shadow: 0 0 0 4px rgba(198,255,0,.12); }
.sms input::placeholder { color: var(--muted); }
.phone { display: grid; place-items: center; }
.phone-screen {
width: 280px; background: var(--surface); border: 1px solid var(--line);
border-radius: 34px; padding: 16px; box-shadow: var(--shadow); position: relative;
}
.phone-screen::before { content: ""; position: absolute; top: 12px; left: 50%; transform: translateX(-50%); width: 90px; height: 6px; background: var(--surface-2); border-radius: 999px; }
.ps-bar { display: flex; justify-content: space-between; font-size: 13px; color: var(--muted); font-weight: 600; margin: 16px 4px 14px; }
.ps-eta { background: var(--bg-2); border-radius: var(--r-md); padding: 16px; text-align: center; display: flex; flex-direction: column; gap: 6px; align-items: center; }
.ps-eta > span:first-child { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .1em; }
.ps-eta strong { font-size: 2.2rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.ps-map { height: 110px; margin: 12px 0; border-radius: var(--r-md); position: relative; overflow: hidden;
background:
linear-gradient(rgba(91,141,239,.08) 1px, transparent 1px) 0 0/100% 26px,
linear-gradient(90deg, rgba(91,141,239,.08) 1px, transparent 1px) 0 0/26px 100%,
var(--bg-2);
}
.ps-rider { position: absolute; font-size: 24px; top: 40%; left: 30%; animation: scoot 4s ease-in-out infinite; }
@keyframes scoot { 0%,100% { transform: translate(0,0); } 50% { transform: translate(80px,-22px); } }
.ps-row { display: flex; align-items: center; justify-content: space-between; padding: 4px; font-size: 14px; font-weight: 600; }
.ps-call { background: var(--lime); color: var(--lime-ink); border: 0; border-radius: 999px; padding: 7px 16px; font-weight: 700; font-size: 13px; }
/* ===== Footer ===== */
.footer { background: var(--bg-2); border-top: 1px solid var(--line-2); padding: 56px 0 28px; }
.foot-grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: 32px; }
.foot-brand p { color: var(--muted); font-size: 14px; max-width: 26em; margin-top: 14px; }
.footer nav { display: flex; flex-direction: column; gap: 10px; }
.footer h4 { font-size: 13px; text-transform: uppercase; letter-spacing: .08em; color: var(--ink); margin-bottom: 4px; }
.footer nav a { color: var(--muted); font-size: 14px; transition: color .15s; }
.footer nav a:hover { color: var(--lime); }
.foot-bottom { display: flex; justify-content: space-between; margin-top: 40px; padding-top: 22px; border-top: 1px solid var(--line-2); color: var(--muted); }
/* ===== Toast ===== */
.toast-wrap { position: fixed; right: 18px; bottom: 18px; z-index: 300; display: flex; flex-direction: column; gap: 10px; }
.toast {
background: var(--surface); color: var(--ink); border: 1px solid var(--line);
border-left: 4px solid var(--lime); border-radius: var(--r-md); padding: 13px 18px;
font-size: 14px; font-weight: 600; box-shadow: var(--shadow); min-width: 220px; max-width: 320px;
transform: translateY(16px); opacity: 0; transition: transform .3s var(--ease), opacity .3s var(--ease);
}
.toast.show { transform: translateY(0); opacity: 1; }
/* ===== Reveal ===== */
.reveal { opacity: 0; transform: translateY(22px); transition: opacity .6s var(--ease), transform .6s var(--ease); }
.reveal.in { opacity: 1; transform: none; }
/* ===== Responsive ===== */
@media (max-width: 920px) {
.hero-grid, .cov-grid, .rider-grid, .app-grid { grid-template-columns: 1fr; gap: 36px; }
.use-grid { grid-template-columns: repeat(2, 1fr); }
.foot-grid { grid-template-columns: 1fr 1fr; }
.eta-card { max-width: 460px; }
.phone { order: -1; }
}
@media (max-width: 760px) {
.nav-links, .nav-cta { display: none; }
.hamburger { display: flex; }
}
@media (max-width: 520px) {
.wrap { padding-inline: 18px; }
.section { padding: 60px 0; }
.hero { padding-top: 40px; }
.use-grid, .city-list, .foot-grid { grid-template-columns: 1fr; }
.hero-stats { gap: 22px; }
.hero-stats strong { font-size: 1.6rem; }
.addr-bar { flex-wrap: wrap; border-radius: var(--r-lg); }
.addr-bar input { flex: 1 1 100%; }
.addr-bar .btn { flex: 1 1 100%; }
.sms { flex-direction: column; }
.foot-bottom { flex-direction: column; gap: 8px; }
.store-row { flex-direction: column; }
.store-btn { width: 100%; }
}(function () {
"use strict";
/* ---------- Toast helper ---------- */
var toastWrap = document.getElementById("toastWrap");
function toast(msg) {
if (!toastWrap) return;
var el = document.createElement("div");
el.className = "toast";
el.textContent = msg;
toastWrap.appendChild(el);
requestAnimationFrame(function () { el.classList.add("show"); });
setTimeout(function () {
el.classList.remove("show");
setTimeout(function () { el.remove(); }, 320);
}, 2800);
}
/* ---------- Mobile nav ---------- */
var hamburger = document.getElementById("hamburger");
var mobileNav = document.getElementById("mobileNav");
if (hamburger && mobileNav) {
hamburger.addEventListener("click", function () {
var open = hamburger.getAttribute("aria-expanded") === "true";
hamburger.setAttribute("aria-expanded", String(!open));
mobileNav.hidden = open;
});
mobileNav.querySelectorAll("a").forEach(function (a) {
a.addEventListener("click", function () {
hamburger.setAttribute("aria-expanded", "false");
mobileNav.hidden = true;
});
});
}
/* ---------- Smooth scroll for in-page links ---------- */
document.querySelectorAll('a[href^="#"]').forEach(function (a) {
a.addEventListener("click", function (e) {
var id = a.getAttribute("href");
if (id.length < 2) return;
var target = document.querySelector(id);
if (target) {
e.preventDefault();
target.scrollIntoView({ behavior: "smooth", block: "start" });
}
});
});
/* ---------- Scroll reveal ---------- */
var revealEls = document.querySelectorAll(".reveal");
if ("IntersectionObserver" in window) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (en.isIntersecting) {
en.target.classList.add("in");
io.unobserve(en.target);
}
});
}, { threshold: 0.14 });
revealEls.forEach(function (el) { io.observe(el); });
} else {
revealEls.forEach(function (el) { el.classList.add("in"); });
}
/* ---------- Count-up stats ---------- */
function countUp(el) {
var target = parseInt(el.getAttribute("data-count"), 10) || 0;
var dur = 1200, start = null;
function step(ts) {
if (!start) start = ts;
var p = Math.min((ts - start) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(target * eased);
if (p < 1) requestAnimationFrame(step);
else el.textContent = target;
}
requestAnimationFrame(step);
}
var counted = false;
var statHost = document.querySelector(".hero-stats");
if (statHost && "IntersectionObserver" in window) {
var sio = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (en.isIntersecting && !counted) {
counted = true;
document.querySelectorAll("[data-count]").forEach(countUp);
sio.disconnect();
}
});
}, { threshold: 0.4 });
sio.observe(statHost);
}
/* ---------- Live ETA countdown ---------- */
var etaMin = document.getElementById("etaMin");
var etaSec = document.getElementById("etaSec");
var etaFill = document.getElementById("etaFill");
var etaPill = document.getElementById("etaPill");
var etaSteps = document.getElementById("etaSteps");
var TOTAL = 8 * 60; // 8 minutes baseline
var remaining = 7 * 60 + 42;
function pad(n) { return n < 10 ? "0" + n : "" + n; }
function renderEta() {
if (etaMin) etaMin.textContent = pad(Math.floor(remaining / 60));
if (etaSec) etaSec.textContent = pad(remaining % 60);
var pct = Math.max(0, Math.min(100, (1 - remaining / TOTAL) * 100));
if (etaFill) etaFill.style.width = pct.toFixed(1) + "%";
if (etaPill) {
if (remaining <= 0) { etaPill.textContent = "Delivered"; etaPill.className = "pill pill-ok"; }
else if (remaining <= 90) { etaPill.textContent = "Almost there"; etaPill.className = "pill pill-lime"; }
else { etaPill.textContent = "On the move"; etaPill.className = "pill pill-ok"; }
}
if (etaSteps) {
var items = etaSteps.querySelectorAll("li");
if (remaining <= 0) {
items.forEach(function (li) { li.className = "done"; });
} else if (remaining <= 90 && items[3]) {
items[2].className = "done";
items[3].className = "active";
}
}
}
renderEta();
var etaTimer = setInterval(function () {
if (remaining > 0) {
remaining--;
renderEta();
if (remaining === 0) {
toast("Delivered to 221B Maple Ave — enjoy!");
clearInterval(etaTimer);
}
}
}, 1000);
/* ---------- Address form ---------- */
var addrForm = document.getElementById("addrForm");
var addrInput = document.getElementById("addrInput");
if (addrForm) {
addrForm.addEventListener("submit", function (e) {
e.preventDefault();
var val = (addrInput && addrInput.value.trim()) || "";
if (!val) { toast("Add an address so we can route a rider."); addrInput && addrInput.focus(); return; }
var min = 8 + Math.floor(Math.random() * 9);
remaining = min * 60 + Math.floor(Math.random() * 60);
TOTAL = remaining;
renderEta();
toast("A rider near " + val + " — ETA " + min + " min.");
});
}
/* ---------- Coverage cities ---------- */
var cityList = document.getElementById("cityList");
var covNote = document.getElementById("covNote");
if (cityList && covNote) {
cityList.addEventListener("click", function (e) {
var btn = e.target.closest(".city");
if (!btn) return;
cityList.querySelectorAll(".city").forEach(function (c) { c.classList.remove("active"); });
btn.classList.add("active");
var city = btn.getAttribute("data-city");
var min = btn.getAttribute("data-min");
var riders = btn.getAttribute("data-riders");
covNote.textContent = city + ": " + riders + " riders online · " + min + " min average drop right now.";
toast(city + " is live — " + riders + " riders nearby.");
});
}
/* ---------- Rider earnings calculator ---------- */
var hoursRange = document.getElementById("hoursRange");
var hoursOut = document.getElementById("hoursOut");
var earnVal = document.getElementById("earnVal");
var dropsOut = document.getElementById("dropsOut");
var earnTier = document.getElementById("earnTier");
function updateEarn() {
var h = parseInt(hoursRange.value, 10);
var dropsPerHour = 4;
var drops = h * dropsPerHour;
var pay = Math.round(drops * 7.1);
if (hoursOut) hoursOut.textContent = h;
if (earnVal) earnVal.textContent = pay;
if (dropsOut) dropsOut.textContent = drops;
if (earnTier) {
if (h >= 38) { earnTier.textContent = "Top rider"; }
else if (h >= 22) { earnTier.textContent = "Steady"; }
else { earnTier.textContent = "Casual"; }
}
}
if (hoursRange) {
hoursRange.addEventListener("input", updateEarn);
updateEarn();
}
/* ---------- App store + SMS ---------- */
document.querySelectorAll(".store-btn").forEach(function (b) {
b.addEventListener("click", function () {
toast("Dasher for " + b.getAttribute("data-store") + " — opening store…");
});
});
var smsForm = document.getElementById("smsForm");
var smsInput = document.getElementById("smsInput");
if (smsForm) {
smsForm.addEventListener("submit", function (e) {
e.preventDefault();
var v = (smsInput && smsInput.value.trim()) || "";
if (v.replace(/\D/g, "").length < 7) { toast("Enter a valid phone number."); smsInput && smsInput.focus(); return; }
toast("Link sent — check your texts!");
smsForm.reset();
});
}
var psCall = document.querySelector(".ps-call");
if (psCall) psCall.addEventListener("click", function () { toast("Calling Mara K…"); });
/* ---------- Phone mini ETA tick ---------- */
var phEta = document.getElementById("phEta");
if (phEta) {
var phR = 6 * 60 + 21;
setInterval(function () {
if (phR > 0) { phR--; phEta.textContent = pad(Math.floor(phR / 60)) + ":" + pad(phR % 60); }
}, 1000);
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Dasher — Last-mile delivery in minutes</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;900&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<!-- ===== NAV ===== -->
<header class="nav" id="top">
<div class="wrap nav-inner">
<a class="brand" href="#top" aria-label="Dasher home">
<span class="brand-mark" aria-hidden="true">
<svg viewBox="0 0 24 24" width="22" height="22"><path d="M3 13l4-8 5 3 4-6 5 11z" fill="currentColor"/></svg>
</span>
<span class="brand-name">Dasher</span>
</a>
<nav class="nav-links" aria-label="Primary">
<a href="#coverage">Coverage</a>
<a href="#uses">Use cases</a>
<a href="#riders">Become a rider</a>
<a href="#app">Get the app</a>
</nav>
<div class="nav-cta">
<a class="btn btn-ghost" href="#track" data-scroll>Track a drop</a>
<a class="btn btn-lime" href="#app" data-scroll>Order now</a>
</div>
<button class="hamburger" id="hamburger" 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="#coverage" data-scroll>Coverage</a>
<a href="#uses" data-scroll>Use cases</a>
<a href="#riders" data-scroll>Become a rider</a>
<a href="#app" data-scroll>Get the app</a>
<a class="btn btn-lime btn-block" href="#app" data-scroll>Order now</a>
</div>
</header>
<main id="main">
<!-- ===== HERO ===== -->
<section class="hero" id="track">
<div class="wrap hero-grid">
<div class="hero-copy reveal">
<span class="eyebrow"><span class="pulse-dot" aria-hidden="true"></span> Live in 38 cities · avg 14 min</span>
<h1>Anything you need,<br><span class="hl">on your door in minutes.</span></h1>
<p class="lede">Groceries, pharmacy, hot food, that charger you forgot. Dasher routes the nearest rider the instant you tap order — no slots, no waiting windows, just speed.</p>
<form class="addr-bar" id="addrForm" autocomplete="off">
<label class="sr-only" for="addrInput">Delivery address</label>
<span class="pin" aria-hidden="true">
<svg viewBox="0 0 24 24" width="20" height="20"><path d="M12 2a7 7 0 0 0-7 7c0 5 7 13 7 13s7-8 7-13a7 7 0 0 0-7-7zm0 9.5A2.5 2.5 0 1 1 12 6.5a2.5 2.5 0 0 1 0 5z" fill="currentColor"/></svg>
</span>
<input id="addrInput" type="text" placeholder="Enter your address" value="221B Maple Ave" />
<button class="btn btn-lime" type="submit">See ETA</button>
</form>
<ul class="hero-stats">
<li><strong data-count="14">14</strong><span>min avg drop</span></li>
<li><strong data-count="38">38</strong><span>live cities</span></li>
<li><strong data-count="99">99</strong><span>% on-time</span></li>
</ul>
</div>
<!-- Live ETA demo card -->
<aside class="eta-card reveal" aria-label="Live delivery tracker demo">
<div class="map" role="img" aria-label="Map showing rider en route to your address">
<svg class="route" viewBox="0 0 320 220" preserveAspectRatio="none" aria-hidden="true">
<path id="routePath" d="M28 188 C 90 150, 70 96, 150 90 S 250 70, 292 34" fill="none" stroke="var(--lime)" stroke-width="4" stroke-linecap="round" stroke-dasharray="6 10"/>
</svg>
<span class="map-store" aria-hidden="true">🛒</span>
<span class="map-home" aria-hidden="true">🏠</span>
<span class="rider" id="rider" aria-hidden="true">
<svg viewBox="0 0 24 24" width="16" height="16"><path d="M5 18a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm14 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM14 6l2 4h2l2 3M9 16h4l1-6-4-2-3 3" fill="none" stroke="#0b0b0f" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>
</span>
</div>
<div class="eta-body">
<div class="eta-head">
<div>
<span class="eta-label">Arriving in</span>
<span class="eta-time"><b id="etaMin">07</b><i>:</i><b id="etaSec">42</b></span>
</div>
<span class="pill pill-ok" id="etaPill">On the move</span>
</div>
<div class="eta-track" aria-hidden="true">
<div class="eta-fill" id="etaFill"></div>
</div>
<ol class="eta-steps" id="etaSteps">
<li class="done"><span class="dot"></span>Order placed</li>
<li class="done"><span class="dot"></span>Rider assigned · Mara K.</li>
<li class="active"><span class="dot"></span>Picked up — heading to you</li>
<li><span class="dot"></span>Delivered</li>
</ol>
</div>
</aside>
</div>
<div class="speed-strip" aria-hidden="true">
<div class="speed-track"><span>FAST</span><span>·</span><span>FRESH</span><span>·</span><span>14 MIN AVG</span><span>·</span><span>NO SLOTS</span><span>·</span><span>FAST</span><span>·</span><span>FRESH</span><span>·</span><span>14 MIN AVG</span><span>·</span><span>NO SLOTS</span><span>·</span></div>
</div>
</section>
<!-- ===== USE CASES ===== -->
<section class="section uses" id="uses">
<div class="wrap">
<header class="sec-head reveal">
<span class="kicker">What people send</span>
<h2>One app. Everything that can't wait.</h2>
<p>From a forgotten phone charger to a full grocery run — pick a category and a nearby rider is on it.</p>
</header>
<div class="use-grid">
<article class="use-card reveal" tabindex="0">
<span class="use-ico" aria-hidden="true">🥬</span>
<h3>Groceries</h3>
<p>Fresh produce, dairy and pantry staples from local stores, kept cold the whole way.</p>
<span class="use-eta">~12 min</span>
</article>
<article class="use-card reveal" tabindex="0">
<span class="use-ico" aria-hidden="true">🍜</span>
<h3>Hot food</h3>
<p>Restaurant meals delivered while still steaming, in insulated rider bags.</p>
<span class="use-eta">~18 min</span>
</article>
<article class="use-card reveal" tabindex="0">
<span class="use-ico" aria-hidden="true">💊</span>
<h3>Pharmacy</h3>
<p>Over-the-counter essentials and prescriptions, handed off with ID check.</p>
<span class="use-eta">~15 min</span>
</article>
<article class="use-card reveal" tabindex="0">
<span class="use-ico" aria-hidden="true">📦</span>
<h3>Anything else</h3>
<p>Send a packet across town, return a parcel, courier the keys you left behind.</p>
<span class="use-eta">~20 min</span>
</article>
</div>
</div>
</section>
<!-- ===== COVERAGE ===== -->
<section class="section coverage" id="coverage">
<div class="wrap cov-grid">
<div class="cov-copy reveal">
<span class="kicker">Coverage</span>
<h2>Live now in <span class="hl" id="cityCount">38</span> cities.</h2>
<p>Tap a city to check live rider availability and the average drop time in that zone right now.</p>
<p class="cov-note" id="covNote" role="status">Pick a city to see live numbers.</p>
</div>
<ul class="city-list reveal" id="cityList" role="list">
<li><button class="city" data-city="Austin" data-min="11" data-riders="142">Austin <span>11 min</span></button></li>
<li><button class="city" data-city="Denver" data-min="13" data-riders="98">Denver <span>13 min</span></button></li>
<li><button class="city" data-city="Lisbon" data-min="9" data-riders="167">Lisbon <span>9 min</span></button></li>
<li><button class="city" data-city="Berlin" data-min="15" data-riders="210">Berlin <span>15 min</span></button></li>
<li><button class="city" data-city="Toronto" data-min="16" data-riders="124">Toronto <span>16 min</span></button></li>
<li><button class="city" data-city="Manila" data-min="12" data-riders="305">Manila <span>12 min</span></button></li>
<li><button class="city" data-city="Nairobi" data-min="14" data-riders="89">Nairobi <span>14 min</span></button></li>
<li><button class="city" data-city="Bogotá" data-min="13" data-riders="176">Bogotá <span>13 min</span></button></li>
</ul>
</div>
</section>
<!-- ===== RIDERS / PARTNER CTA ===== -->
<section class="section riders" id="riders">
<div class="wrap rider-grid">
<div class="rider-copy reveal">
<span class="kicker kicker-dark">Earn with Dasher</span>
<h2>Ride your city. Get paid by the drop.</h2>
<p>Flexible hours, instant payouts after every shift, and surge boosts when demand spikes. Bring a bike, scooter or car — we route you the closest jobs.</p>
<ul class="rider-perks">
<li><span aria-hidden="true">⚡</span> Cash out instantly after each delivery</li>
<li><span aria-hidden="true">📍</span> Smart routing keeps your stops short</li>
<li><span aria-hidden="true">🛡️</span> In-app insurance on every active trip</li>
</ul>
<a class="btn btn-lime btn-lg" href="#app" data-scroll>Start riding</a>
</div>
<div class="earn-card reveal" aria-label="Estimated weekly earnings">
<div class="earn-head">
<span>Estimated earnings</span>
<span class="pill pill-lime" id="earnTier">Steady</span>
</div>
<div class="earn-amount">$<span id="earnVal">680</span><small>/ week</small></div>
<label class="earn-label" for="hoursRange">Hours per week: <b id="hoursOut">24</b>h</label>
<input id="hoursRange" type="range" min="6" max="48" value="24" step="2" />
<div class="earn-foot">
<span><b id="dropsOut">96</b> drops</span>
<span><b>$7.10</b> avg / drop</span>
</div>
</div>
</div>
</section>
<!-- ===== APP DOWNLOAD ===== -->
<section class="section app" id="app">
<div class="wrap app-grid">
<div class="app-copy reveal">
<span class="kicker">Get the app</span>
<h2>Your city, in your pocket.</h2>
<p>Set your address once, save your favourites, and reorder a drop in two taps. Real-time tracking on every order.</p>
<div class="store-row">
<button class="store-btn" data-store="iOS">
<svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><path d="M16.4 12.9c0-2.3 1.9-3.4 2-3.5-1.1-1.6-2.8-1.8-3.4-1.8-1.4-.1-2.8.8-3.5.8s-1.8-.8-3-.8c-1.5 0-3 .9-3.8 2.3-1.6 2.8-.4 7 1.2 9.3.8 1.1 1.7 2.4 2.9 2.3 1.2 0 1.6-.7 3-.7s1.8.7 3 .7 2-1.1 2.8-2.2c.9-1.3 1.2-2.5 1.3-2.6-.1 0-2.4-1-2.4-3.6zM14.2 5.9c.6-.8 1-1.9.9-3-.9 0-2 .6-2.7 1.4-.6.7-1 1.8-.9 2.9 1 .1 2-.5 2.7-1.3z" fill="currentColor"/></svg>
<span><small>Download on the</small>App Store</span>
</button>
<button class="store-btn" data-store="Android">
<svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><path d="M3 20l9-9-9-9c-.3.2-.5.6-.5 1v16c0 .4.2.8.5 1zm10.5-10.5L16 12l-2.5 2.5L13 14l1-2-1-2 .5-.5zM5 4l9 5-2 2-7-7zm0 16l7-7 2 2-9 5z" fill="currentColor"/></svg>
<span><small>Get it on</small>Google Play</span>
</button>
</div>
<form class="sms" id="smsForm">
<label class="sr-only" for="smsInput">Phone number for app link</label>
<input id="smsInput" type="tel" placeholder="Text me the link — phone number" />
<button class="btn btn-dark" type="submit">Send link</button>
</form>
</div>
<div class="phone reveal" aria-hidden="true">
<div class="phone-screen">
<div class="ps-bar"><span>9:41</span><span>Dasher</span></div>
<div class="ps-eta">
<span>Arriving</span>
<strong id="phEta">06:21</strong>
<span class="pill pill-ok">2 stops away</span>
</div>
<div class="ps-map"><span class="ps-rider">🛵</span></div>
<div class="ps-row"><span>Mara K. · 4.9★</span><button class="ps-call">Call</button></div>
</div>
</div>
</div>
</section>
</main>
<!-- ===== FOOTER ===== -->
<footer class="footer">
<div class="wrap foot-grid">
<div class="foot-brand">
<span class="brand">
<span class="brand-mark" aria-hidden="true"><svg viewBox="0 0 24 24" width="20" height="20"><path d="M3 13l4-8 5 3 4-6 5 11z" fill="currentColor"/></svg></span>
<span class="brand-name">Dasher</span>
</span>
<p>Last-mile delivery, measured in minutes. Order anything, anytime.</p>
</div>
<nav aria-label="Company"><h4>Company</h4><a href="#uses">About</a><a href="#coverage">Cities</a><a href="#riders">Careers</a></nav>
<nav aria-label="Help"><h4>Help</h4><a href="#track">Track order</a><a href="#app">Get the app</a><a href="#riders">Rider support</a></nav>
<nav aria-label="Legal"><h4>Legal</h4><a href="#top">Terms</a><a href="#top">Privacy</a><a href="#top">Cookies</a></nav>
</div>
<div class="wrap foot-bottom">
<small>© 2026 Dasher Logistics — fictional demo brand.</small>
<small>Made for speed.</small>
</div>
</footer>
<div class="toast-wrap" id="toastWrap" aria-live="polite" aria-atomic="true"></div>
<script src="script.js"></script>
</body>
</html>Last-mile / Instant Landing
A speed-forward marketing landing for Dasher, a fictional instant-delivery service. The black-and-electric-lime hero leads with an “on your door in minutes” headline, an address bar that estimates a fresh ETA on submit, and count-up stats. Beside it, a live tracker card animates a rider marker along an SVG route across a CSS-grid map while a countdown ticks down second by second — status pills and a four-step tracker flip from “on the move” to “almost there” to “delivered”. A lime speed marquee scrolls beneath the fold.
The body is a full multi-section page: use-case cards (groceries, hot food, pharmacy, anything) with per-category ETAs; an interactive coverage grid where tapping a city surfaces live rider counts and average drop time; a rider/partner section with a range-slider earnings calculator that recomputes weekly pay, drops and tier; and an app-download block with store buttons, an SMS-link form and an animated phone mock. A sticky nav collapses to a hamburger mobile menu, sections reveal on scroll, and a small toast() helper narrates every interaction.
Everything is self-contained vanilla JS — no frameworks or build step. Layout is responsive from wide desktop down to ~360px, with mobile-first reflow on the hero, coverage and download sections, tabular-numeric countdowns, and prefers-reduced-motion handling for the marquee and animated route.
Illustrative UI only — fictional brand, not a real delivery service.