/* Hallmark · genre: atmospheric · macrostructure: Deck (one slide per scroll, device-led opener) · hero knobs: two-column, film-in-device, one CTA
 * theme: custom · vibe: "dark canvas, one film, ember trail" · paper: oklch(14% 0.006 45) · accent: #FF5A1F (≈ oklch 68% 0.21 37)
 * display: Geist 600 · body: Geist 400 · outlier: Geist Mono (tally, receipt labels, colophon) · axes: dark / grotesk-sans / warm
 * nav: N9 edge-aligned minimal · footer: Ft5 statement (the last slide) · enrichment: the app's own film inside a photoreal iPhone 17 Pro frame; the app's own gift art
 * motion: every move is scrubbed by the scroll — the slides crossfade in place, the trail draws, the gifts land, the screens shuffle. Nothing timed, nothing loops.
 * studied: no · context: inferred (audience: runners arriving from a shared link · use: download · tone: austere)
 * Hallmark · pre-emit critique: P5 H5 E5 S5 R5 V5 */
@import url("tokens.css");
/* Geist + Geist Mono, self-hosted (Vercel's Geist, SIL Open Font License; the variable files from the geist npm package),
   subset to Latin + Latin Extended-A + punctuation + arrows + box drawing with every layout feature kept (tnum, ss01):
   70 KB → 37 KB and 71 KB → 40 KB. Regenerate with pyftsubset --layout-features='*' --flavor=woff2; new filenames
   on every change — /fonts/* is cached seven days. */
@font-face { font-family: "Geist"; src: url("/fonts/Geist-Variable-2.woff2") format("woff2"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Geist Mono"; src: url("/fonts/GeistMono-Variable-2.woff2") format("woff2"); font-weight: 100 900; font-style: normal; font-display: swap; }

/* the page never moves sideways: nothing is wider than the screen (see the glows, the ribbon and the fan below), and
   as a net the root clips sideways overflow (clip, not hidden: the pinned deck keeps working) and never rubber-bands */
html { overflow-x: clip; overscroll-behavior-x: none; -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
body {
  position: relative; margin: 0; overflow-x: clip; overscroll-behavior-x: none;
  background: var(--color-paper); color: var(--color-ink);
  font-family: var(--font-body); font-size: var(--text-base); line-height: 1.55;
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
}
*, *::before, *::after { box-sizing: border-box; }
/* the small courtesies of a finished page */
::selection { background: var(--color-accent); color: #fff; }
h1, h2, .line { text-wrap: balance; }
p, li { text-wrap: pretty; }
:where(a, button, [tabindex]):focus-visible { outline: 2px solid var(--color-accent); outline-offset: 3px; border-radius: 4px; }
.chip:focus-visible, .store:focus-visible { border-radius: var(--radius-md); }
/* one light for the whole page: a blurred ribbon, three screens tall, fixed behind the deck. The scroll
   timeline slides it past — ember behind the phone, deep blue through the marathon, amber at the gifts. */
body::before { content: ""; position: fixed; inset: 0; z-index: -2; pointer-events: none; background: linear-gradient(180deg, var(--color-paper) 0%, var(--color-field-base) 45%, var(--color-paper) 100%); }
/* the ribbon is the background of one box the width of the screen (media/field-1.svg, the same shapes and colours
   that were inline): as inline SVG its ellipses were elements running past the screen's edge */
.field { position: fixed; left: 0; top: 0; z-index: -1; width: 100%; height: 300vh; height: 300svh; pointer-events: none; will-change: transform; background: url("/media/field-1.svg") 0 0 / 100% 100% no-repeat; }

/* ---------- nav · N9 edge-aligned ---------- */
.nav {
  position: fixed; inset: 0 0 auto 0; z-index: var(--z-sticky);
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-md);
  padding: var(--space-sm) var(--gutter);
  padding-top: max(var(--space-sm), env(safe-area-inset-top));
  background: color-mix(in oklab, var(--color-paper) 84%, transparent);
  -webkit-backdrop-filter: blur(16px) saturate(1.15); backdrop-filter: blur(16px) saturate(1.15);
  border-bottom: var(--rule-hair) solid transparent;
  transition: border-color var(--dur-short) var(--ease-out);
}
.nav.is-scrolled { border-bottom-color: var(--color-rule); }
.lockup { display: inline-flex; align-items: center; gap: 0.6rem; text-decoration: none; color: var(--color-ink); }
.lockup img { height: 22px; width: auto; display: block; }
.lockup b { font-family: var(--font-display); font-weight: 800; font-size: 0.9375rem; letter-spacing: 0.16em; line-height: 1; }
.lockup .live { color: var(--color-accent); }
.ember { color: var(--color-accent); }
.amber { color: var(--color-amber); font-weight: 600; }
.chip {
  display: inline-flex; align-items: center; min-height: 40px; padding: 0 1rem;
  border: var(--rule-hair) solid var(--color-rule-2); border-radius: var(--radius-pill);
  font-size: var(--text-sm); font-weight: 500; color: var(--color-ink); text-decoration: none; white-space: nowrap;
  transition: border-color var(--dur-short) var(--ease-out), background-color var(--dur-short) var(--ease-out);
}
.chip:hover { border-color: var(--color-muted); background: var(--color-paper-2); }
.chip:active { transform: translateY(1px); }
.chip--fill { background: var(--color-accent); border-color: transparent; color: var(--color-ink-on-accent, #1a0c05); font-weight: 600; }
.chip--fill:hover { background: var(--color-accent-hot); border-color: transparent; }
.nav .chip--fill { transition: opacity var(--dur-short) var(--ease-out), visibility 0s linear 0s; }
.nav.is-hero .chip--fill { opacity: 0; visibility: hidden; transition: opacity var(--dur-short) var(--ease-out), visibility 0s linear var(--dur-short); }

/* ---------- the deck · the scroll is the timeline ----------
   The deck is pinned to the viewport; the track behind it is tall. Scrolling never moves the page —
   it moves the story: slides crossfade in place, the trail draws under your thumb, the gifts land one by
   one, the statements arrive in order. Every value below is written by device.js from scrollY. */
.track { position: relative; }
.deck { position: sticky; top: 0; height: 100vh; height: 100svh; overflow: hidden; }
.slide {
  position: absolute; inset: 0; width: 100%;
  /* the vertical padding breathes with the window (a 640px laptop window keeps every slide whole) */
  padding: calc(clamp(var(--space-md), 4svh, var(--space-xl)) + 56px) max(var(--gutter), calc((100% - var(--page)) / 2)) clamp(var(--space-md), 4svh, var(--space-xl));
  display: grid; align-content: center;
  opacity: 0; visibility: hidden; pointer-events: none;   /* will-change is set by device.js on the slides in motion only */
}

.slide.is-shown { visibility: visible; }
.slide.is-on { pointer-events: auto; }

h2 {
  font-family: var(--font-display); font-weight: 600; font-size: var(--text-3xl);
  line-height: 1.04; letter-spacing: -0.03em; margin: 0 0 var(--space-lg); max-width: 18ch; overflow-wrap: anywhere; min-width: 0;
}

/* the rail (wide screens only): a dot per slide at the right edge, the one that's up in ember; a click jumps the timeline there */
.rail { position: fixed; right: max(var(--space-md), env(safe-area-inset-right)); top: 50%; z-index: var(--z-raised); transform: translateY(-50%); display: grid; gap: 14px; padding: 6px 0; }
.rail a { display: block; width: 18px; height: 18px; position: relative; }
.rail a::before { content: ""; position: absolute; left: 50%; top: 50%; width: 6px; height: 6px; border-radius: 50%; background: var(--color-rule-2); transform: translate(-50%, -50%); transition: background-color var(--dur-short) var(--ease-out), transform var(--dur-short) var(--ease-out), box-shadow var(--dur-short) var(--ease-out); }
.rail a:hover::before { background: var(--color-muted); }
.rail a.is-on::before { background: var(--color-accent); transform: translate(-50%, -50%) scale(1.5); box-shadow: 0 0 10px var(--color-accent-glow); }
/* the active dot wears a ring that fills as you move through its slide */
.rail a::after { content: ""; position: absolute; inset: 0; border-radius: 50%; opacity: 0; background: conic-gradient(var(--color-accent-pale) calc(var(--u, 0) * 360deg), transparent 0); -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 1.4px)); mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 1.4px)); transition: opacity var(--dur-short) var(--ease-out); }
.rail a.is-on::after { opacity: 0.9; }
.rail a span { position: absolute; right: 26px; top: 50%; transform: translate(6px, -50%); white-space: nowrap; font: 500 var(--text-xs) / 1 var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-muted); opacity: 0; pointer-events: none; transition: opacity var(--dur-short) var(--ease-out), transform var(--dur-short) var(--ease-out); }
.rail a:hover span, .rail a:focus-visible span { opacity: 1; transform: translate(0, -50%); }

/* page load: the hero's pieces rise once, in order (one-shot; the timeline owns everything after) */
@keyframes rise { from { opacity: 0; transform: translateY(14px); } }
.hero__copy > * { animation: rise 720ms var(--ease-out) both; }
.hero__copy > :nth-child(1) { animation-delay: 60ms; }
.hero__copy > :nth-child(2) { animation-delay: 160ms; }
.hero__copy > :nth-child(3) { animation-delay: 260ms; }
.hero .stage { animation: rise 820ms var(--ease-out) both; animation-delay: 220ms; }

/* ---------- 1 · hero · the film in the phone ---------- */
.hero { grid-template-columns: minmax(0, 6fr) minmax(0, 5fr); align-items: center; gap: var(--space-2xl); isolation: isolate; }
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(26% 34% at 74% 56%, var(--color-amber-glow), transparent 70%);
}
.hero h1 {
  /* the size follows the copy column too (961–1360px wide), so "Every adventure" always holds its line */
  font-family: var(--font-display); font-weight: 600; font-size: min(var(--text-display), calc(6.69vw - 4.8px));
  line-height: 1.0; letter-spacing: -0.04em; margin: 0 0 var(--space-lg); max-width: 12ch;
  overflow-wrap: anywhere; min-width: 0;
}
.lede { font-size: var(--text-lg); line-height: 1.5; color: var(--color-muted); max-width: 40ch; margin: 0 0 var(--space-xl); }
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-lg) var(--space-xl); }

.store {
  display: inline-flex; align-items: center; gap: 0.65rem; min-height: 52px; padding: 0.5rem 1.15rem 0.5rem 0.95rem;
  background: var(--color-badge); border: var(--rule-hair) solid var(--color-rule-2); border-radius: var(--radius-md);
  color: var(--color-ink); text-decoration: none; white-space: nowrap;
  transition: border-color var(--dur-short) var(--ease-out), background-color var(--dur-short) var(--ease-out);
}
.store:hover { border-color: var(--color-muted); background: var(--color-paper-2); }
.store:active { transform: translateY(1px); }
.store svg { width: 22px; height: 22px; fill: currentColor; flex: none; }
.store span { display: flex; flex-direction: column; line-height: 1.05; text-align: left; }
.store small { font-size: 0.6875rem; letter-spacing: 0.01em; color: var(--color-muted); }
.store strong { font-size: 1.125rem; font-weight: 600; letter-spacing: -0.01em; }

.stage { position: relative; display: grid; justify-items: center; gap: var(--space-md); perspective: 1500px; }
/* the phone stands on something: one soft contact shadow, static */
.stage::after {
  content: ""; position: absolute; z-index: -1; left: 50%; bottom: 8%; width: 62%; height: 7%;
  transform: translateX(-50%); border-radius: 50%; pointer-events: none;
  background: radial-gradient(ellipse at center, oklch(0% 0 0 / 0.6), transparent 70%); filter: blur(8px);
}
.device {
  --w: clamp(240px, min(24vw, 38svh), 352px);   /* grown a step (2026-09-08) so the film's stats read; the height term keeps it whole in a short window */
  width: var(--w); aspect-ratio: 1350 / 2760; position: relative;
  transform-style: preserve-3d; will-change: transform; touch-action: pan-y; cursor: grab; user-select: none;
}
.device:active { cursor: grabbing; }
.device__frame { position: absolute; inset: 0; width: 100%; height: 100%; display: block; pointer-events: none; user-select: none; -webkit-user-drag: none; backface-visibility: hidden; }
/* the glass: one still highlight across the top-left of the display, under the bezel's own light — static by law */
.device::after {
  content: ""; position: absolute; pointer-events: none; backface-visibility: hidden;
  left: calc(72 / 1350 * 100%); top: calc(69 / 2760 * 100%); width: calc(1206 / 1350 * 100%); height: calc(2622 / 2760 * 100%);
  border-radius: calc(var(--w) * 0.115); transform: translateZ(0.8px);
  background: linear-gradient(118deg, rgba(255, 255, 255, 0.11) 0%, rgba(255, 255, 255, 0.035) 26%, transparent 44%);
}
.device__screen {
  position: absolute; display: block; background: var(--color-badge); pointer-events: none; backface-visibility: hidden;
  left: calc(72 / 1350 * 100%); top: calc(69 / 2760 * 100%); width: calc(1206 / 1350 * 100%); height: calc(2622 / 2760 * 100%);
  border-radius: calc(var(--w) * 0.115);
  /* THE FILM IS THE PHONE'S OWN SHAPE (2026-09-08): the hero film is rendered by the app at
     1080×2340 — the iPhone's 19.5:9 — so it fills this cutout (0.460 vs 0.462) edge to edge with the
     whole HUD inside the glass. No bars, no zoom: cover-fit loses a hairline at the sides, nothing
     more. (A 9:16 film here would either crop the stat rail or leave black bands — both rejected.) */
  object-fit: cover;
}
.device__wall {
  position: absolute; display: block; transform-origin: center; pointer-events: none;
  background: linear-gradient(to bottom, var(--color-side-dark) 0%, var(--color-side) 38%, var(--color-side-hi) 50%, var(--color-side) 62%, var(--color-side-dark) 100%);
}
.device__back {
  position: absolute; display: block; overflow: hidden; backface-visibility: hidden; pointer-events: none;
  background: linear-gradient(162deg, var(--color-back-hi), var(--color-back-lo) 72%);
  box-shadow: inset 0 0 0 1.5px var(--color-side);
}
.device__plateau {
  position: absolute; left: 4.6%; right: 4.6%; top: 3.4%; height: 19.5%; display: block;
  border-radius: calc(var(--bw, 400px) * 0.13);
  background: linear-gradient(170deg, var(--color-plateau), var(--color-back-lo));
  box-shadow: inset 0 1px 0 var(--color-side-hi), 0 1px 2px var(--color-side-dark);
}
.device__plateau b {
  position: absolute; display: block; border-radius: 50%; width: calc(var(--bw, 400px) * 0.155); aspect-ratio: 1;
  background: radial-gradient(circle at 36% 34%, var(--color-lens-hi), var(--color-lens) 46%, var(--color-lens-dark) 100%);
  box-shadow: 0 0 0 calc(var(--bw, 400px) * 0.012) var(--color-lens-ring), 0 0 0 calc(var(--bw, 400px) * 0.022) var(--color-back-lo);
}
.device__plateau .l1 { left: 7%; top: 10%; }
.device__plateau .l2 { left: 7%; bottom: 10%; }
.device__plateau .l3 { left: 30%; top: 50%; transform: translateY(-50%); }
.device__plateau .flash { left: auto; right: 9%; top: 14%; width: calc(var(--bw, 400px) * 0.05); background: radial-gradient(circle at 40% 40%, var(--color-flash), var(--color-side)); box-shadow: 0 0 0 calc(var(--bw, 400px) * 0.008) var(--color-lens-ring); }
/* the WebGL phone (phone.js): a canvas wider and taller than the box so a turned phone never clips;
   while it's up, the CSS body parts step aside and the film keeps playing underneath as the texture source */
.device__gl { position: absolute; left: -32%; top: -9%; width: 164%; height: 118%; display: none; pointer-events: none; }
.device.is-gl .device__gl { display: block; }
.device.is-gl .device__frame, .device.is-gl .device__wall, .device.is-gl .device__back { display: none !important; visibility: hidden !important; }
.device.is-gl .device__screen { opacity: 0.001; }
.device.is-gl { transform-style: flat; }
.next {
  position: absolute; left: 50%; bottom: max(var(--space-md), env(safe-area-inset-bottom)); transform: translateX(-50%);
  width: 40px; height: 40px; display: grid; place-items: center; border-radius: 50%;
  border: var(--rule-hair) solid var(--color-rule); color: var(--color-neutral); text-decoration: none;
  transition: color var(--dur-short) var(--ease-out), border-color var(--dur-short) var(--ease-out);
}
.next svg { width: 18px; height: 18px; }
.next:hover { color: var(--color-ink); border-color: var(--color-rule-2); }

/* ---------- 2 · the trail, drawn on arrival ---------- */
.draw { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--space-2xl); align-items: center; }
.draw__copy h2 { font-size: min(var(--text-3xl), 7svh); }
.draw__copy p { font-size: var(--text-md); line-height: 1.5; color: var(--color-muted); max-width: var(--measure); margin: 0 0 var(--space-lg); }
.route { display: block; width: 100%; height: auto; max-height: 70vh; max-height: 70svh; }
.route .shadow { fill: none; stroke: var(--color-shadow); stroke-width: 18; stroke-linecap: round; stroke-linejoin: round; }
.route .glow { fill: none; stroke: var(--color-accent-glow); stroke-width: 28; stroke-linecap: round; stroke-linejoin: round; filter: url(#glow); }
.route .tube { fill: none; stroke: url(#tube); stroke-width: 9; stroke-linecap: round; stroke-linejoin: round; }
.route .gloss { fill: none; stroke: var(--color-ink); stroke-opacity: 0.35; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
/* NO calc() AND NO UNITS IN THE DASH MATH (2026-09-09: on iPhone the trail drew all at once — WebKit drops
   calc() inside stroke-dasharray, and reads px there as screen pixels, not user units, so 4182px covered the
   whole scaled-down path). device.js writes plain numbers: --L (the path length), --Lh (the hot tip),
   --o and --oh (the offsets, every frame). Unitless = user units in every engine. */
.route .shadow, .route .glow, .route .tube, .route .gloss { stroke-dasharray: var(--L, 1) var(--L, 1); stroke-dashoffset: var(--o, 0); }
.route .art { transform-box: fill-box; transform-origin: center; }
/* the white-hot tip: the last 6% of the drawn line, riding under the comet */
.route .hot { fill: none; stroke: var(--color-hot-core); stroke-opacity: 0.85; stroke-width: 3.2; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: var(--Lh, 1) var(--L, 1); stroke-dashoffset: var(--oh, 0); }
.route .s0 { stop-color: var(--color-accent-deep); } .route .s1 { stop-color: var(--color-accent); } .route .s2 { stop-color: var(--color-accent-hot); } .route .s3 { stop-color: var(--color-accent-pale); }
.route .start { fill: var(--color-start); stroke: var(--color-ink); stroke-width: 2.5; transition: fill 400ms var(--ease-out); }
.route.is-closed .start { fill: var(--color-hot-core); }
/* the loop closes: one ring leaves the start marker and is gone (one-shot, fired by your scroll crossing the end) */
.route .close { fill: none; stroke: var(--color-accent-pale); stroke-width: 3; opacity: 0; transform-box: fill-box; transform-origin: center; }
.route.is-closed .close { animation: close-pop 820ms var(--ease-out) forwards; }
@keyframes close-pop { 0% { opacity: 0.85; transform: scale(0.6); } 100% { opacity: 0; transform: scale(3.4); } }
.route .halo { fill: var(--color-accent); fill-opacity: 0.35; }
.route .head-dot { fill: var(--color-hot-core); stroke: var(--color-accent); stroke-width: 2.5; }
.receipt { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-lg) var(--space-lg); margin-top: var(--space-xl); opacity: 0; transform: translateY(10px); }
.stat .n { font-style: normal; }
.stat small { display: block; font-family: var(--font-mono); font-size: var(--text-xs); letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-neutral); margin-bottom: var(--space-xs); }
.stat b { font-family: var(--font-display); font-weight: 600; font-size: min(var(--text-stat), 6.5svh); letter-spacing: -0.03em; line-height: 1; font-variant-numeric: tabular-nums; white-space: nowrap; }
.stat b span { font-size: 0.42em; font-weight: 500; color: var(--color-muted); margin-left: 0.12em; letter-spacing: 0; }
.receipt__note { grid-column: 1 / -1; margin: var(--space-xs) 0 0; font-size: var(--text-sm); color: var(--color-neutral); }

/* ---------- 3 · marathon day ---------- */
/* THE SECOND PHONE (2026-09-08, Cameron: "show another iPhone with a live recording of a live stream…
   a sick live stream of one of the streamers running through Sydney with all the cool 3D buildings"):
   the spectator's screen, watching Tahlia's Harbour run live. Same frame and body as the hero's phone,
   a step smaller, turned a touch toward the copy; the deck turns it in as the slide lands (scrubbed). */
.race { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); gap: var(--space-2xl); align-items: center; isolation: isolate; }
.stage--live::before { content: ""; position: absolute; z-index: -1; left: 50%; top: 48%; width: min(150%, 620px); aspect-ratio: 1; transform: translate(-50%, -50%); border-radius: 50%; background: radial-gradient(circle, var(--color-amber-glow) 0%, transparent 62%); pointer-events: none; }
.stage--live .device { --w: clamp(230px, min(22vw, 35svh), 320px); cursor: default; touch-action: auto; }
.stage__cap { margin: 0; font-size: var(--text-sm); color: var(--color-neutral); letter-spacing: 0.01em; }
.stage__cap b { color: var(--color-ink); font-weight: 600; }
.race__copy p { font-size: var(--text-md); line-height: 1.5; color: var(--color-muted); max-width: var(--measure); margin: 0 0 var(--space-md); }
.facts { list-style: none; margin: var(--space-xl) 0 0; padding: 0; max-width: var(--measure); }
.facts li { padding: var(--space-sm) 0; border-top: var(--rule-hair) solid var(--color-rule); color: var(--color-ink); line-height: 1.45; }
.facts li:last-child { border-bottom: var(--rule-hair) solid var(--color-rule); }
/* the lead-in carries each fact (2026-09-08, Cameron: "work on the Marathon day text… especially on mobile") */
.facts b { color: var(--color-accent-pale); font-weight: 600; }

/* ---------- 3 · listen along (2026-09-25, Cameron: "mentions nowhere how you can listen to their music real time
   along with them"): the stream's now-playing chip and its setlist sheet, drawn from the app (LiveView's
   nowPlayingChip, NowPlayingBars, SetlistSheet) in the site's material. device.js scrubs it: the chip is tapped
   and takes the app's listening material (orange, ink, pause), then the song changes, the chip rolls to the next
   one and the setlist pushes down a row, the old song keeping the mile it started. The EQ bars move only with the
   scroll. Stage left, copy right, like Race day. ---------- */
.music { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); gap: var(--space-2xl); align-items: center; isolation: isolate; }
.music__copy p { font-size: var(--text-md); line-height: 1.5; color: var(--color-muted); max-width: var(--measure); margin: 0 0 var(--space-md); }
.stage--music {
  --sw: clamp(320px, min(30vw, 52svh), 440px);   /* the sheet's width */
  --cu: 1.7px;                                    /* the chip: px per app point (a length, so phones can derive it from the locked height) */
  --rh: clamp(46px, 6.4svh, 58px);                /* one setlist row */
  --gap: 8px; --pitch: calc(var(--rh) + var(--gap)); --rows: 4;
  width: min(100%, var(--sw)); justify-self: center; justify-items: stretch; gap: clamp(var(--space-sm), 2svh, var(--space-lg)); perspective: none;
}
.stage--music::before { content: ""; position: absolute; z-index: -1; left: 50%; top: 50%; width: min(160%, 640px); aspect-ratio: 1; transform: translate(-50%, -50%); border-radius: 50%; background: radial-gradient(circle, var(--color-amber-glow) 0%, var(--color-bloom-2) 34%, transparent 62%); pointer-events: none; }
.stage--music::after { display: none; }
/* the chip: glass while you look, the app's orange once you're listening (the second pill fades over the first) */
.np { position: relative; justify-self: end; z-index: 2; }
.np__pill {
  display: flex; align-items: center; gap: calc(8 * var(--cu)); padding: calc(6 * var(--cu)) calc(12 * var(--cu));
  border-radius: 999px; color: #fff;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.02) 50%, rgba(0, 0, 0, 0.12)), rgba(30, 30, 35, 0.84);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.12), 0 12px 28px rgba(0, 0, 0, 0.4), 0 3px 8px rgba(0, 0, 0, 0.42);
}
.np__pill--hot {
  position: absolute; inset: 0; opacity: 0; color: #0e0a07;
  background: linear-gradient(180deg, #ff7829, #ff5a1f);
  box-shadow: inset 0 0 0 1px rgba(120, 26, 3, 0.85), 0 6px 22px rgba(255, 90, 31, 0.4), 0 2px 6px rgba(0, 0, 0, 0.35);
}
.np__pill--hot::after { content: ""; position: absolute; inset: 2px; border-radius: inherit; box-shadow: inset 0 0 0 0.5px rgba(255, 255, 255, 0.5); pointer-events: none; }
.np__songs { display: grid; overflow: hidden; }
.np__song { grid-area: 1 / 1; display: grid; gap: calc(1 * var(--cu)); line-height: 1.2; white-space: nowrap; }
.np__song b { font-weight: 600; font-size: calc(12 * var(--cu)); letter-spacing: -0.005em; }
.np__song small { font-weight: 500; font-size: calc(10.5 * var(--cu)); opacity: 0.55; }
.np__pill--hot .np__song small { opacity: 0.6; }
.np__song--b { opacity: 0; transform: translateY(100%); }
.np__glyph { flex: none; width: calc(8 * var(--cu)); height: calc(9.6 * var(--cu)); margin-left: calc(1 * var(--cu)); fill: var(--color-accent); opacity: 0.9; }
.np__pill--hot .np__glyph { fill: #0e0a07; opacity: 0.85; }
/* the equalizer: three bars, resting at three heights; device.js moves them with the scroll, never with a clock */
.eq { display: inline-flex; align-items: flex-end; gap: calc(2 * var(--cu)); width: calc(13 * var(--cu)); height: calc(12 * var(--cu)); flex: none; }
.eq i { display: block; width: calc(2.5 * var(--cu)); height: 100%; border-radius: 99px; background: var(--color-accent); transform-origin: 50% 100%; transform: scaleY(0.6); }
.eq i:nth-child(2) { transform: scaleY(1); }
.eq i:nth-child(3) { transform: scaleY(0.78); }
.np__pill--hot .eq i { background: #0e0a07; }
/* the setlist, as the sheet the chip opens: NOW on top, then every song since you tuned in with the mile it started */
.setlist {
  --cu: 1.1px;
  position: relative; display: grid; gap: var(--space-sm); margin: 0;
  padding: 10px clamp(12px, 1.4vw, 18px) clamp(12px, 1.8svh, 18px);
  border-radius: 26px; background: var(--color-paper); border: var(--rule-hair) solid var(--color-rule-2);
  box-shadow: 0 30px 60px oklch(0% 0 0 / 0.55), 0 0 0 1px oklch(0% 0 0 / 0.4), inset 0 1px 0 oklch(100% 0 0 / 0.07);
}
.setlist__grab { display: block; justify-self: center; width: 36px; height: 5px; border-radius: 99px; background: oklch(100% 0 0 / 0.2); }
.setlist__head { display: grid; gap: 3px; padding: 2px 4px 0; }
.setlist__head b { font-family: var(--font-display); font-weight: 700; font-size: clamp(1.375rem, 3.2svh, 1.75rem); letter-spacing: -0.025em; line-height: 1.05; color: var(--color-ink); }
.setlist__head span { font-size: var(--text-sm); color: var(--color-neutral); font-variant-numeric: tabular-nums; }
.setlist__head i { font-style: normal; }
/* a window of whole rows: the song change pushes every row down one pitch (--in, written by device.js), the new
   NOW slides in from under the header and the last row fades as it leaves (device.js counts the window's rows) */
.setlist__rows {
  list-style: none; margin: 0; padding: 0; position: relative; overflow: hidden;
  display: grid; grid-auto-rows: var(--rh); gap: var(--gap); align-content: start;
  height: calc(var(--rows) * var(--pitch) - var(--gap));
}
.sl {
  display: flex; align-items: center; gap: 12px; min-width: 0; padding: 0 12px 0 14px; border-radius: 14px;
  background: oklch(100% 0 0 / 0.035); box-shadow: inset 0 0 0 1px oklch(100% 0 0 / 0.07);
  transform: translateY(calc(var(--in, 0) * var(--pitch)));
  transition: background-color var(--dur-short) var(--ease-out);
}
.sl--new, .sl--was { background: oklch(100% 0 0 / 0.06); box-shadow: inset 0 0 0 1px oklch(100% 0 0 / 0.1); }
.sl--was.is-past { background: oklch(100% 0 0 / 0.035); }
.sl--new { position: absolute; left: 0; right: 0; top: 0; height: var(--rh); opacity: 0; transform: translateY(calc((var(--in, 0) - 1) * var(--pitch))); }
.sl__lead { display: grid; place-items: center; width: 16px; flex: none; }
.sl__lead > * { grid-area: 1 / 1; }
.sl__play { width: 9px; height: 11px; fill: oklch(100% 0 0 / 0.38); }
.sl__txt { display: grid; gap: 2px; flex: 1; min-width: 0; line-height: 1.2; }
.sl__txt b, .sl__txt small { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sl__txt b { font-weight: 600; font-size: clamp(0.8125rem, 1.9svh, 1rem); color: var(--color-ink); transition: color var(--dur-short) var(--ease-out); }
.sl__txt small { font-weight: 500; font-size: clamp(0.6875rem, 1.55svh, 0.8125rem); color: oklch(100% 0 0 / 0.5); }
.sl--new b, .sl.is-hot b { color: var(--color-accent); }   /* the song you're hearing is the orange one, as in the app */
.sl__stamps { display: grid; justify-items: end; flex: none; }
.sl__stamps > * { grid-area: 1 / 1; }
.sl__stamp {
  flex: none; padding: 5px 8px; border-radius: 999px; white-space: nowrap;
  font: 600 0.625rem / 1 var(--font-mono); letter-spacing: 0.05em; text-transform: uppercase; font-variant-numeric: tabular-nums;
  color: oklch(100% 0 0 / 0.5); background: oklch(100% 0 0 / 0.05); box-shadow: inset 0 0 0 1px oklch(100% 0 0 / 0.08);
}
.sl__stamp--now { color: var(--color-accent); background: color-mix(in oklab, var(--color-accent) 13%, transparent); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--color-accent) 35%, transparent); }
.sl__in { opacity: 0; }

/* ---------- 3 · marathon day (2026-09-10): the field on the map, and the wrist ---------- */
.marathon { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); gap: var(--space-2xl); align-items: center; isolation: isolate; }
.marathon__copy p { font-size: var(--text-md); line-height: 1.5; color: var(--color-muted); max-width: var(--measure); margin: 0 0 var(--space-md); }
.stage--marathon { justify-self: center; }
.stage--marathon::before { content: ""; position: absolute; z-index: -1; left: 50%; top: 46%; width: min(150%, 620px); aspect-ratio: 1; transform: translate(-50%, -50%); border-radius: 50%; pointer-events: none; background: radial-gradient(circle, var(--color-amber-glow), transparent 62%); }
/* the phone's width lives on the stage (--pw) so the watch can be sized off it — a custom property set on
   .device is invisible to its sibling */
.stage--marathon { --pw: clamp(230px, min(22vw, 35svh), 320px); }
.stage--marathon .device { --w: var(--pw); cursor: default; touch-action: auto; }
/* the second screen sits on the first; the deck fades it in as the slide is scrubbed (far field → one runner) */
.device__screen--near { opacity: 0; }
/* THE WATCH: a Series 11 case drawn in CSS around the app's own screen — black stock, a metal edge, the crown
   and the side button on the right. Sized off the phone so the pair holds its proportion at every width. */
.watch {
  --ww: calc(var(--pw) * 0.56);
  position: absolute; right: -12%; bottom: 9%; z-index: 3; width: var(--ww); aspect-ratio: 416 / 496; margin: 0;
  border-radius: calc(var(--ww) * 0.26); background: #0b0b0d;
  box-shadow: inset 0 0 0 calc(var(--ww) * 0.028) #1d1d21, inset 0 0 0 calc(var(--ww) * 0.036) #37373d, 0 18px 40px rgba(0, 0, 0, 0.55), 0 2px 6px rgba(0, 0, 0, 0.5);
  will-change: transform; transform: translateZ(0);
}
.watch::after { /* the glass, the same one still highlight as the phone */
  content: ""; position: absolute; inset: 5%; border-radius: calc(var(--ww) * 0.22); pointer-events: none;
  background: linear-gradient(118deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.03) 28%, transparent 46%);
}
.watch__screen { position: absolute; inset: 5%; width: 90%; height: 90%; display: block; object-fit: cover; border-radius: calc(var(--ww) * 0.22); background: #000; }
.watch__screen--gift { opacity: 0; }
.watch__crown { position: absolute; right: calc(var(--ww) * -0.055); top: 27%; width: calc(var(--ww) * 0.07); height: calc(var(--ww) * 0.17); border-radius: calc(var(--ww) * 0.02); background: linear-gradient(to right, #2a2a2f, #4a4a52 40%, #232327); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); }
.watch__crown::after { content: ""; position: absolute; inset: 0; border-radius: inherit; background: radial-gradient(circle at 60% 50%, #ff5a1f 0 22%, transparent 24%); opacity: 0.9; }
.watch__button { position: absolute; right: calc(var(--ww) * -0.045); top: 52%; width: calc(var(--ww) * 0.045); height: calc(var(--ww) * 0.22); border-radius: calc(var(--ww) * 0.015); background: linear-gradient(to right, #26262b, #3f3f46 50%, #202024); }

/* ---------- 3 · your people (2026-09-09): the feed and the events, as the app shows them ---------- */
.people { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--space-2xl); align-items: center; isolation: isolate; }
.people__copy p { font-size: var(--text-md); line-height: 1.5; color: var(--color-muted); max-width: var(--measure); margin: 0 0 var(--space-md); }
.screens { position: relative; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-md); align-items: start; max-width: min(760px, calc(133svh - 275px)); justify-self: end; width: 100%; }   /* the height term: the taller screen never reaches the nav in a short window */
.screens::before { content: ""; position: absolute; z-index: -1; left: 50%; top: 50%; width: min(130%, 720px); aspect-ratio: 1; transform: translate(-50%, -50%); border-radius: 50%; background: radial-gradient(circle, var(--color-amber-glow) 0%, var(--color-bloom-2) 36%, transparent 64%); pointer-events: none; }
.screen { position: relative; margin: 0; will-change: transform; }
/* the frame is the image itself; the caption sits under it, never over the faces at the foot of a screen */
.screen img {
  display: block; width: 100%; height: auto; -webkit-user-drag: none; border-radius: 22px; background: var(--color-badge);
  border: var(--rule-hair) solid var(--color-rule-2);
  box-shadow: 0 30px 60px oklch(0% 0 0 / 0.55), 0 0 0 1px oklch(0% 0 0 / 0.4), inset 0 1px 0 oklch(100% 0 0 / 0.07);
}
.screen figcaption { display: inline-block; white-space: nowrap; margin-top: 10px; padding: 6px 10px; border-radius: 999px; font: 500 var(--text-xs) / 1 var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-muted); border: var(--rule-hair) solid var(--color-rule-2); }
.screen--events { margin-top: var(--space-xl); }   /* the second screen sits a step lower: a pair, not a grid */

/* ---------- 4 · the gifts, counted on arrival ---------- */
.gifts { gap: clamp(var(--space-md), 4.4svh, var(--space-xl)); align-content: center; }
.gifts__head { max-width: none; }
.gifts__head p { font-size: var(--text-md); line-height: 1.5; color: var(--color-muted); max-width: 64ch; margin: 0; }
.tally { font-family: var(--font-mono); font-size: var(--text-sm); letter-spacing: 0.04em; color: var(--color-neutral); margin: var(--space-md) 0 0; }
.tally b { color: var(--color-ink); font-weight: 500; font-variant-numeric: tabular-nums; }
.tally #tally-cash { color: var(--color-amber); }
.tally__sep { margin: 0 0.7em; }
.gift-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: var(--space-md); }
.gift {
  position: relative; display: grid; justify-items: center; align-content: start; gap: var(--space-2xs);
  padding: var(--space-lg) var(--space-sm) var(--space-md); text-align: center;
  background: color-mix(in oklab, var(--color-paper-2) 82%, transparent); border: var(--rule-hair) solid var(--color-rule); border-radius: 20px;
  opacity: 0; transform: translateY(30px) scale(0.94);
  transition: border-color 240ms var(--ease-out), background-color 240ms var(--ease-out);
}
@media (hover: hover) { .gift:hover { border-color: var(--color-rule-2); background: color-mix(in oklab, var(--color-paper-2) 96%, transparent); } }
.gift.is-lit { border-color: var(--color-amber); box-shadow: 0 0 0 1px color-mix(in oklab, var(--color-amber) 60%, transparent), 0 0 28px var(--color-amber-glow); }
/* THE STREAM (2026-09-09, Cameron: "more like a TikTok live stream… the gift goes up the page like a live
   stream would show it"): each gift arrives as a toast — the sender's face, "MAYA sent a Water Cup", the
   art — that pops in at the foot of the slide, rises with hearts behind it and lands in its card, which
   lights up. Every frame of it is the scroll position; nothing loops. */
.gifts { position: absolute; }
.lane { position: absolute; inset: 0; pointer-events: none; overflow: hidden; z-index: 2; }
.toast {
  position: absolute; left: 0; top: 0; display: flex; align-items: center; gap: 12px; padding: 6px 8px 6px 6px;
  border-radius: 999px; white-space: nowrap; opacity: 0; will-change: transform, opacity; transform-origin: left center;
  background: linear-gradient(90deg, color-mix(in oklab, var(--color-paper-2) 94%, transparent), color-mix(in oklab, var(--color-amber) 9%, var(--color-paper-2)));
  border: var(--rule-hair) solid color-mix(in oklab, var(--color-amber) 28%, var(--color-rule-2));
  box-shadow: 0 18px 40px oklch(0% 0 0 / 0.55), 0 0 0 1px oklch(0% 0 0 / 0.35), inset 0 1px 0 oklch(100% 0 0 / 0.07);
}
.toast__who { width: 36px; height: 36px; border-radius: 50%; object-fit: cover; border: 2px solid var(--color-accent); box-shadow: 0 0 0 1px var(--color-paper); flex: none; }
.toast__text { display: grid; gap: 1px; line-height: 1.15; }
.toast__text b { font-family: var(--font-display); font-weight: 600; font-size: var(--text-sm); color: var(--color-ink); letter-spacing: 0.06em; }
.toast__text span { font-size: var(--text-xs); color: var(--color-muted); }
.toast__text em { font-style: normal; color: var(--color-amber); font-weight: 500; }
.toast__n { font-family: var(--font-mono); font-style: normal; font-size: 0.625rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-amber); padding: 5px 9px; border-radius: 999px; border: var(--rule-hair) solid color-mix(in oklab, var(--color-amber) 40%, transparent); background: color-mix(in oklab, var(--color-amber) 10%, transparent); }
.toast__gift { width: 54px; height: 54px; object-fit: contain; flex: none; filter: drop-shadow(0 8px 14px oklch(0% 0 0 / 0.55)) drop-shadow(0 0 14px var(--color-amber-glow)); }
.gift img { width: min(100%, 118px, 13svh); height: auto; aspect-ratio: 1; object-fit: contain; display: block; margin-bottom: var(--space-xs); filter: drop-shadow(0 12px 18px oklch(0% 0 0 / 0.5)); -webkit-user-drag: none; }
.gift b { font-family: var(--font-display); font-weight: 600; font-size: var(--text-base); letter-spacing: -0.01em; }
.gift span { font-family: var(--font-mono); font-size: var(--text-xs); letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-neutral); }
.gift span em { font-style: normal; font-weight: 500; color: var(--color-amber); }
.gift small { position: absolute; top: 10px; right: 12px; font-family: var(--font-mono); font-size: 0.5625rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--color-accent-pale); }
.gift--legendary { border-color: color-mix(in oklab, var(--color-amber) 32%, var(--color-rule)); background: linear-gradient(180deg, color-mix(in oklab, var(--color-amber) 8%, var(--color-paper-2)), color-mix(in oklab, var(--color-paper-2) 82%, transparent)); }
.gifts__note { font-size: var(--text-sm); line-height: 1.5; color: var(--color-neutral); max-width: 64ch; margin: 0; opacity: 0; }
.gifts__note a { color: var(--color-muted); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--color-rule-2); }
.gifts__note a:hover { color: var(--color-ink); }

/* ---------- 6 · three things (2026-09-09, Cameron: "the slides before are so alive and then there's this one"):
   the three statements keep their column; beside them, the three screens they describe — the broadcast, the film,
   the link — ride a row the scroll drives. The beat that has landed brings its screen to the centre; the others
   wait beside it, a step smaller and dimmer. The spacing keeps the cards apart through every move, so nothing
   ever pops in front of anything. Scrubbed, never looping. */
.says { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); gap: var(--space-2xl); align-items: center; align-content: center; isolation: isolate; }
.beats { list-style: none; margin: 0; padding: 0; display: grid; gap: clamp(var(--space-lg), 4.2svh, var(--space-2xl)); max-width: 50rem; }
.say { margin: 0; opacity: 0; transform: translateY(18px); }
.fan { --cw: clamp(172px, min(calc(var(--vh-lock, 100svh) * 0.285), 18vw), 300px); position: relative; justify-self: center; width: 100%; max-width: calc(var(--cw) * 3.2); height: calc(var(--cw) * 2.1667 + 40px); -webkit-mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent); mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent); }
.fan::before { content: ""; position: absolute; z-index: -1; left: 50%; top: 50%; width: min(120%, 640px); aspect-ratio: 1; transform: translate(-50%, -50%); border-radius: 50%; background: radial-gradient(circle, var(--color-amber-glow) 0%, var(--color-bloom-2) 34%, transparent 62%); pointer-events: none; }
.fan__row { position: absolute; left: 50%; top: 50%; width: var(--cw); height: calc(var(--cw) * 2.1667); transform: translate(-50%, -50%); }
/* each card's box is sized and placed by device.js and stops at the fan's own edge (where its mask has already faded
   everything out), the picture inside keeping its full size, so no card ever reaches past the screen */
.card {
  position: absolute; left: 0; top: 0; width: 100%; height: 100%; margin: 0; overflow: hidden; border-radius: calc(var(--cw) * 0.12); background: var(--color-badge);
  border: var(--rule-hair) solid var(--color-rule-2);
  box-shadow: 0 calc(30px * var(--s, 1)) calc(60px * var(--s, 1)) oklch(0% 0 0 / 0.55), 0 0 0 1px oklch(0% 0 0 / 0.4);
  transform: translate3d(0, 0, 0);
}
.card img { display: block; width: 100%; height: 100%; object-fit: cover; -webkit-user-drag: none; }
.say h3 { margin: 0; font-family: var(--font-display); font-weight: 600; font-size: var(--text-3xl); letter-spacing: -0.03em; line-height: 1.05; color: var(--color-ink); text-wrap: balance; }
.say p { margin: var(--space-xs) 0 0; font-size: var(--text-md); line-height: 1.5; color: var(--color-muted); max-width: 40ch; }

/* ---------- 6 · footer · Ft5 statement, the last slide ---------- */
/* THE LAST SLIDE (2026-09-08, Cameron: "the final slide sucks… shouldn't the footer be at the bottom"):
   the closing line and the badge hold the middle of the screen, the footer rests on the floor. */
.foot { grid-template-rows: minmax(0, 1fr) auto; align-content: stretch; gap: var(--space-xl); padding-bottom: max(var(--space-lg), env(safe-area-inset-bottom)); }
.foot__body { align-self: center; display: grid; gap: var(--space-lg); justify-items: start; }
.foot .line { font-family: var(--font-display); font-weight: 600; font-size: var(--text-display); letter-spacing: -0.04em; line-height: 1.0; max-width: 22ch; margin: 0; overflow-wrap: anywhere; }
.foot__lede { font-size: var(--text-lg); line-height: 1.5; color: var(--color-muted); max-width: 36ch; margin: 0; }
.foot .actions { margin-top: var(--space-xs); }
.chip--ghost { border-color: var(--color-rule-2); color: var(--color-muted); }
.foot .chip--ghost { min-height: 52px; padding: 0 1.35rem; font-size: 0.9375rem; }   /* one height with the App Store badge beside it */
.chip--ghost:hover { color: var(--color-ink); border-color: var(--color-muted); }
.foot .meta { position: relative; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: var(--space-md) var(--space-xl); padding-top: var(--space-md); border-top: var(--rule-hair) solid var(--color-rule); color: var(--color-neutral); font-size: var(--text-sm); }
.foot .meta::before { content: ""; position: absolute; left: 0; top: -1px; width: min(38%, 420px); height: 1px; background: linear-gradient(90deg, var(--color-accent), var(--color-accent-pale) 60%, transparent); }
.foot .colophon { font-family: var(--font-mono); font-size: var(--text-xs); letter-spacing: 0.06em; }
.foot .colophon a { color: var(--color-muted); text-decoration: none; }
.foot .colophon a:hover { color: var(--color-ink); }

/* ---------- responsive ---------- */
/* PHONES (2026-09-08, Cameron: "the iPhone is small, then when you scroll it becomes bigger — it should
   always be the same size"): every vertical size below is keyed to svh, the SMALL viewport, which does not
   move when Safari's toolbar collapses. No max-height media queries — they flip mid-scroll. Both phones on
   the page share one size. */
@media (max-width: 60rem) {
  .slide { padding-top: calc(clamp(var(--space-md), calc(var(--vh-lock, 100svh) * 0.0240), var(--space-lg)) + 56px); padding-bottom: clamp(var(--space-md), calc(var(--vh-lock, 100svh) * 0.0240), var(--space-lg)); }
  .hero { grid-template-columns: minmax(0, 1fr); text-align: center; gap: clamp(var(--space-md), calc(var(--vh-lock, 100svh) * 0.0240), var(--space-lg)); align-content: start; }
  .hero::before { background: radial-gradient(40% 22% at 50% 80%, var(--color-amber-glow), transparent 70%); }
  .hero h1 { margin-inline: auto; max-width: 14ch; font-size: clamp(1.9rem, min(10.5vw, calc(var(--vh-lock, 100svh) * 0.0540)), 3.25rem); margin-bottom: clamp(var(--space-sm), calc(var(--vh-lock, 100svh) * 0.0160), var(--space-lg)); }
  .lede { margin-inline: auto; margin-bottom: clamp(var(--space-sm), calc(var(--vh-lock, 100svh) * 0.0180), var(--space-md)); font-size: clamp(0.9375rem, calc(var(--vh-lock, 100svh) * 0.0200), 1.0625rem); max-width: 34ch; }
  .actions { justify-content: center; }
  .store, .foot .chip--ghost { min-height: clamp(44px, calc(var(--vh-lock, 100svh) * 0.0600), 52px); }
  /* as big as the height allows (Cameron: "lock in on phone size… not big enough on mobile, both of them") */
  /* and as big as the room under the copy (device.js measures it once per width against the locked height and
     writes --fitw; without it the lock alone sizes them) */
  .device { --w: min(62vw, var(--fitw, calc(var(--vh-lock, 100svh) * 0.265)), 270px); }
  .stage--live .device { --w: min(62vw, var(--fitw, calc(var(--vh-lock, 100svh) * 0.265)), 270px); }
  .next { display: none; }
  /* NO RAIL ON PHONES (2026-09-25, Cameron: "on mobile you don't need the lines, the progress parts at the bottom,
     because they're overlapping parts too"): the thumb is the navigation. The bar's room goes back to the slides;
     the floor keeps the home indicator's inset when Safari's bar is down. */
  .rail { display: none; }
  /* the glows stay on the screen: the same circle of light (same centre, same radius, same stops), drawn in a box no
     wider than the viewport. The part that used to hang past the edge was already where the light had faded out. */
  .stage--live::before, .stage--music::before, .screens::before, .fan::before { width: min(100vw, var(--glow)); height: var(--glow); aspect-ratio: auto; border-radius: 0; }
  .stage--live::before { --glow: min(calc(1.5 * (100vw - 2 * var(--gutter))), 620px); background: radial-gradient(circle calc(var(--glow) * 0.7071) at 50% 50%, var(--color-amber-glow) 0%, transparent 62%); }
  .stage--music::before { --glow: min(calc(1.6 * min(100vw - 2 * var(--gutter), 440px)), 640px); background: radial-gradient(circle calc(var(--glow) * 0.7071) at 50% 50%, var(--color-amber-glow) 0%, var(--color-bloom-2) 34%, transparent 62%); }
  .screens::before { --glow: min(calc(1.3 * (100vw - 2 * var(--gutter))), 720px); background: radial-gradient(circle calc(var(--glow) * 0.7071) at 50% 50%, var(--color-amber-glow) 0%, var(--color-bloom-2) 36%, transparent 64%); }
  .fan::before { --glow: min(calc(1.2 * (100vw - 2 * var(--gutter))), 640px); background: radial-gradient(circle calc(var(--glow) * 0.7071) at 50% 50%, var(--color-amber-glow) 0%, var(--color-bloom-2) 34%, transparent 62%); }
  /* and every slide is laid out in the LOCKED height, not the live one: when Safari's bar collapses the deck grows
     underneath, the slides keep their box, and nothing on them moves or grows (the room opens below) */
  .slide { inset: 0 0 auto 0; height: var(--vh-lock, 100%); }
  .slide { padding-bottom: max(clamp(var(--space-md), calc(var(--vh-lock, 100svh) * 0.0240), var(--space-lg)), env(safe-area-inset-bottom)); }
  .foot { padding-bottom: max(var(--space-lg), env(safe-area-inset-bottom)); }
  .draw { grid-template-columns: minmax(0, 1fr); gap: var(--space-md); align-content: center; }
  .draw__copy { order: 2; }
  .draw__copy h2 { font-size: clamp(1.75rem, min(7.2vw, calc(var(--vh-lock, 100svh) * 0.0420)), 2.25rem); margin-bottom: var(--space-sm); }
  .draw__copy p { font-size: clamp(0.9375rem, calc(var(--vh-lock, 100svh) * 0.0210), 1.0625rem); margin-bottom: var(--space-sm); }
  .route { max-height: calc(var(--vh-lock, 100svh) * 0.3200); }
  .receipt { margin-top: var(--space-sm); gap: var(--space-sm) var(--space-md); }
  .receipt .stat b { font-size: clamp(1.6rem, calc(var(--vh-lock, 100svh) * 0.0400), 2rem); }
  .receipt__note { margin-top: 0; }
  .race { grid-template-columns: minmax(0, 1fr); gap: var(--space-md); }
  .marathon { grid-template-columns: minmax(0, 1fr); gap: var(--space-md); align-content: start; }
  .marathon .stage { order: 2; gap: var(--space-xs); }
  .marathon__copy h2 { font-size: clamp(1.75rem, min(7.2vw, calc(var(--vh-lock, 100svh) * 0.0420)), 2.25rem); margin-bottom: var(--space-sm); }
  .marathon__copy p { font-size: clamp(0.9375rem, calc(var(--vh-lock, 100svh) * 0.0210), 1.0625rem); margin-bottom: var(--space-xs); }
  .stage--marathon { --pw: min(58vw, var(--fitw, calc(var(--vh-lock, 100svh) * 0.25)), 250px); }
  .watch { right: -18%; bottom: 6%; }
  .people { grid-template-columns: minmax(0, 1fr); gap: var(--space-md); align-content: center; }
  .screens { justify-self: center; width: 100%; max-width: none; }   /* device.js narrows the pair only when a short screen needs it */
  .people__copy h2 { font-size: clamp(1.75rem, min(7.2vw, calc(var(--vh-lock, 100svh) * 0.0420)), 2.25rem); margin-bottom: var(--space-sm); }
  .people__copy p { font-size: clamp(0.9375rem, calc(var(--vh-lock, 100svh) * 0.0210), 1.0625rem); margin-bottom: var(--space-xs); }
  .people__copy > p, .gifts__head > p:not(.tally), .say p, .draw__copy p { text-wrap: balance; }
  .screens { gap: var(--space-sm); margin-top: var(--space-sm); }
  .screen img { border-radius: 16px; }
  .screen--events { margin-top: var(--space-md); }
  .screen figcaption { margin-top: 6px; padding: 4px 8px; font-size: 0.5625rem; }
  .facts { margin-top: var(--space-lg); }
  .race { align-content: start; }
  .race .stage { order: 2; gap: var(--space-xs); }
  .race__copy h2 { font-size: clamp(1.75rem, min(7.2vw, calc(var(--vh-lock, 100svh) * 0.0420)), 2.25rem); margin-bottom: var(--space-sm); }
  .race__copy p { font-size: clamp(0.9375rem, calc(var(--vh-lock, 100svh) * 0.0210), 1.0625rem); margin-bottom: var(--space-xs); }
  /* listen along on phones: copy first, then the chip and a three-row setlist sized from the locked height */
  .music { grid-template-columns: minmax(0, 1fr); gap: clamp(10px, calc(var(--vh-lock, 100svh) * 0.02), var(--space-md)); align-content: start; }
  .music .stage { order: 2; }
  .music__copy h2 { font-size: clamp(1.75rem, min(7.2vw, calc(var(--vh-lock, 100svh) * 0.0420)), 2.25rem); margin-bottom: var(--space-sm); }
  .music__copy p { font-size: clamp(0.9375rem, calc(var(--vh-lock, 100svh) * 0.0210), 1.0625rem); margin-bottom: var(--space-xs); }
  .stage--music { --sw: 440px; --cu: clamp(0.95px, calc(var(--vh-lock, 100svh) * 0.00155), 1.3px); --rh: clamp(34px, calc(var(--vh-lock, 100svh) * 0.056), 50px); --gap: 6px; gap: clamp(6px, calc(var(--vh-lock, 100svh) * 0.014), var(--space-sm)); }
  .setlist { --cu: 1px; border-radius: 22px; padding: 8px 10px 10px; gap: var(--space-xs); }
  /* three rows, or two when the locked height is short (a length-only snap: 0 or one pitch, no unit division) */
  .setlist__rows { height: calc(2 * var(--pitch) - var(--gap) + min(var(--pitch), max(0px, (var(--vh-lock, 100svh) - 640px) * 1000)) + min(var(--pitch), max(0px, (var(--vh-lock, 100svh) - 790px) * 1000))); }
  .setlist__head b { font-size: 1.375rem; }
  .setlist__head span { font-size: var(--text-xs); }
  .sl { gap: 10px; padding: 0 10px 0 12px; border-radius: 12px; }
  .sl__txt b { font-size: 0.875rem; }
  .sl__txt small { font-size: 0.75rem; }
  .facts { margin-top: var(--space-xs); font-size: clamp(0.9375rem, calc(var(--vh-lock, 100svh) * 0.0215), 1.0625rem); }
  .facts li { padding: clamp(var(--space-xs), calc(var(--vh-lock, 100svh) * 0.0120), var(--space-sm)) 0; line-height: 1.4; }
  .stage__cap { display: none; }
  .gifts { gap: var(--space-lg); }
  .gifts h2 { font-size: clamp(1.75rem, min(7.2vw, calc(var(--vh-lock, 100svh) * 0.0420)), 2.25rem); margin-bottom: var(--space-sm); }
  .gift-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-sm); }
  .gift { padding: var(--space-md) var(--space-xs) var(--space-sm); border-radius: 16px; }
  .gift img { width: min(100%, var(--gift-art, clamp(46px, calc(var(--vh-lock, 100svh) * 0.0900), 84px))); }
  .toast { gap: 9px; padding: 5px 6px 5px 5px; }
  .toast__who { width: 30px; height: 30px; }
  .toast__text b { font-size: var(--text-xs); }
  .toast__text span { font-size: 0.6875rem; }
  .toast__n { display: none; }
  .toast__gift { width: 42px; height: 42px; }
  .gift b { font-size: var(--text-sm); }
  /* phones: the statement on top, one at a time in the same place; its screen under it, the next one peeking beside */
  .says { grid-template-columns: minmax(0, 1fr); gap: clamp(var(--space-md), calc(var(--vh-lock, 100svh) * 0.0240), var(--space-lg)); align-content: center; }
  .beats { gap: 0; max-width: none; }
  .say { grid-area: 1 / 1; align-self: start; }
  .say h3 { font-size: clamp(1.75rem, min(7.2vw, calc(var(--vh-lock, 100svh) * 0.0420)), 2.25rem); }
  .say p { font-size: clamp(0.9375rem, calc(var(--vh-lock, 100svh) * 0.0205), 1.0625rem); margin-top: 6px; max-width: 36ch; }
  .fan { --cw: min(52vw, calc(var(--vh-lock, 100svh) * 0.235), 220px); max-width: none; justify-self: stretch; height: calc(var(--cw) * 2.1667 + 24px); }
}
@media (max-width: 40rem) {
  .hero h1 { font-size: clamp(2.375rem, 10.5vw, 3.25rem); }
  .lede { font-size: 1.0625rem; }
  .lockup b { font-size: 0.875rem; }
  .gifts { gap: var(--space-md); }
  .gifts__head p { font-size: var(--text-base); }
  .gift-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-xs); }
  .gift { padding: var(--space-sm) var(--space-2xs) var(--space-sm); gap: 2px; border-radius: 14px; }
  .gift img { width: min(100%, var(--gift-art, clamp(44px, calc(var(--vh-lock, 100svh) * 0.0750), 64px))); margin-bottom: var(--space-2xs); }
  .gift b { font-size: 0.75rem; line-height: 1.15; }
  .gift span { font-size: 0.625rem; letter-spacing: 0.06em; }
  .gift small { position: static; margin-top: 2px; }
  .gifts__note { font-size: var(--text-xs); }
  .tally__sep { display: block; height: 0; overflow: hidden; margin: 0; }
  .people__copy .more { display: none; }   /* small phones: two lines, then the facts */
  .race__copy > p, .marathon__copy > p, .music__copy > p { display: none; }   /* phones: the heading, then the facts carry it */
  .foot__body { justify-items: center; text-align: center; }
  .foot .line { max-width: 12ch; }
  .foot__lede { font-size: var(--text-base); }
  .foot .meta { flex-direction: column; align-items: flex-start; }
  .foot .colophon { white-space: nowrap; }
  .foot .where { display: none; }   /* one line on a phone: © · Privacy · Support */
}
@media (max-width: 25rem) {
  /* the narrow phones (375–400): the facts carry Marathon day, the hero lede loses a clause,
     and the phones give back 3% (device.js then fits them to the room under the copy) */
  .lede .more { display: none; }
  .stage--marathon { --pw: min(58vw, var(--fitw, min(calc(var(--vh-lock, 100svh) * 0.23), calc((var(--vh-lock, 100svh) - 330px) * 0.46))), 250px); }
  /* and on the shortest locks (an SE on load is 553px) the phone takes what is left under the copy, whole */
  .device, .stage--live .device { --w: min(62vw, var(--fitw, min(calc(var(--vh-lock, 100svh) * 0.244), calc((var(--vh-lock, 100svh) - 320px) * 0.489))), 270px); }
  .gifts__head .more { display: none; }   /* narrow phones: the gifts' second sentence goes, the six cards stay whole */
}
/* CPU on phones (2026-09-09): no SVG blur filter re-rasterising the trail every scroll frame — a wide soft
   stroke reads the same at phone size — and no backdrop blur under the nav while video plays beneath it. */
@media (pointer: coarse) {
  .route .glow { filter: none; stroke-width: 22; stroke-opacity: 0.38; }
  .nav { -webkit-backdrop-filter: none; backdrop-filter: none; background: color-mix(in oklab, var(--color-paper) 94%, transparent); }
}
@media (hover: none) { .chip:hover { border-color: var(--color-rule-2); background: transparent; } .store:hover { border-color: var(--color-rule-2); background: var(--color-badge); } }
@media (prefers-reduced-motion: reduce) {
  .lane { display: none; }
  /* the plain page: every slide stacked and in place, nothing driven by scroll */
  html { scroll-behavior: auto; }
  body::before, .field { position: absolute; }
  .field { height: 100%; }
  .deck { position: static; height: auto; overflow: visible; }
  .slide { position: relative; inset: auto; height: auto; min-height: 100vh; min-height: 100svh; opacity: 1; visibility: visible; pointer-events: auto; transform: none; will-change: auto; }
  .gift, .say, .gifts__note, .receipt { opacity: 1; transform: none; transition: none; }
  .fan { height: auto; max-width: none; -webkit-mask-image: none; mask-image: none; }
  .fan__row { position: static; width: auto; height: auto; transform: none; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-md); }
  .card { position: relative; inset: auto; height: auto; opacity: 1 !important; transform: none !important; }
  .next, .rail { display: none; }
  .hero__copy > *, .hero .stage { animation: none; }
  .slide [data-k] { opacity: 1 !important; transform: none !important; }
  .route .close { display: none; }
  .route .art { transform: none !important; }
  .nav.is-hero .chip--fill { opacity: 1; visibility: visible; }
  .device { transition: none; }
  *, *::before, *::after { transition-duration: 150ms !important; }
}
/* the same plain page when JavaScript never arrives (the deck is script-driven) */
html.no-js { scroll-behavior: auto; }
.no-js body::before, .no-js .field { position: absolute; }
.no-js .field { height: 100%; }
.no-js .deck { position: static; height: auto; overflow: visible; }
.no-js .slide { position: relative; inset: auto; height: auto; min-height: 100vh; min-height: 100svh; opacity: 1; visibility: visible; pointer-events: auto; transform: none; will-change: auto; }
.no-js .gift, .no-js .say, .no-js .gifts__note, .no-js .receipt { opacity: 1; transform: none; transition: none; }
.no-js .fan { height: auto; max-width: none; -webkit-mask-image: none; mask-image: none; }
.no-js .fan__row { position: static; width: auto; height: auto; transform: none; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-md); }
.no-js .card { position: relative; inset: auto; height: auto; opacity: 1 !important; transform: none !important; }
.no-js .next, .no-js .rail, .no-js .lane { display: none; }
.no-js .route .shadow, .no-js .route .glow, .no-js .route .tube, .no-js .route .gloss { stroke-dasharray: none; stroke-dashoffset: 0; }
.no-js .route .hot { display: none; }
.no-js .hero__copy > *, .no-js .hero .stage { animation: none; }
.no-js .slide [data-k] { opacity: 1 !important; transform: none !important; }
.no-js .route .close { display: none; }
.no-js .route .art { transform: none !important; }
.no-js .nav.is-hero .chip--fill { opacity: 1; visibility: visible; }
.no-js .device { transition: none; }
.no-js *, .no-js *::before, .no-js *::after { transition-duration: 150ms !important; }
