/* ============================================================
   GOTCHA — sections.
   Every pinned section is `Nvh tall` + a `100svh` sticky child, so
   its scroll progress 0→1 spans exactly the pinned lifetime.
   ============================================================ */

/* ═══════════ NAV ═══════════ */
.nav {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  transition: background 400ms var(--ease), backdrop-filter 400ms var(--ease),
              padding 400ms var(--ease), border-color 400ms var(--ease);
  padding: 18px 0;
  border-bottom: 1px solid transparent;
}
.nav.is-condensed {
  padding: 9px 0;
  background: rgba(5,7,11,0.72);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  border-bottom-color: var(--line);
}
.nav-in {
  max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gut);
  display: flex; align-items: center; gap: 26px;
}
.nav-brand { display: flex; align-items: center; gap: 10px; margin-right: auto; }
.nav-mark { width: 26px; height: 26px; color: var(--blue); flex: none; }
.nav-mark svg { width: 100%; height: 100%; }
/* the real app icon, worn as the mark — rounded like it is on a phone */
.nav-mark img {
  width: 100%; height: 100%; display: block;
  border-radius: 7px; object-fit: cover;
  box-shadow: 0 2px 8px rgba(0,0,0,0.35);
}
.nav-word {
  font-weight: 900; font-size: 1.06rem; letter-spacing: 0.13em;
}
.nav-links { display: flex; gap: 4px; }
.nav-link {
  position: relative;
  padding: 8px 13px; border-radius: 999px;
  font-size: 0.9rem; font-weight: 500; color: var(--fg-dim);
  transition: color 220ms var(--ease), background 220ms var(--ease);
}
.nav-link:hover { color: var(--fg); background: rgba(150,180,225,0.06); }
.nav-link.is-active { color: var(--fg); }
.nav-link.is-active::after {
  content: ''; position: absolute; left: 13px; right: 13px; bottom: 2px;
  height: 1px; background: var(--blue);
}
.nav-burger {
  display: none; width: 40px; height: 40px; border: 1px solid var(--line-2);
  border-radius: 12px; background: transparent; cursor: pointer;
  flex-direction: column; justify-content: center; align-items: center; gap: 5px;
}
.nav-burger span { width: 15px; height: 1.5px; background: var(--fg); }

.menu {
  position: fixed; inset: 0; z-index: 99;
  background: rgba(5,7,11,0.96);
  backdrop-filter: blur(20px);
  display: flex; flex-direction: column; justify-content: center; gap: 6px;
  padding: 0 var(--gut);
  opacity: 0; pointer-events: none;
  transition: opacity 320ms var(--ease);
}
.menu.is-open { opacity: 1; pointer-events: auto; }
.menu a {
  font-size: 1.9rem; font-weight: 800; letter-spacing: -0.03em;
  padding: 10px 0; border-bottom: 1px solid var(--line);
}

/* ═══════════ CHAPTER RAIL ═══════════ */
.rail {
  position: fixed; z-index: 90;
  right: 22px; top: 50%; transform: translateY(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 12px;
}
.rail-line {
  position: absolute; top: -14px; bottom: -14px; width: 1px;
  background: var(--line);
}
.rail-fill {
  position: absolute; inset: 0; background: var(--blue);
  transform-origin: top; transform: scaleY(0);
}
.rail-mark {
  position: relative; z-index: 1;
  width: 22px; height: 16px; padding: 0;
  border: 0; background: transparent; cursor: pointer;
  display: grid; place-items: center;
}
.rail-mark i {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--ink); box-shadow: 0 0 0 1px var(--line-2);
  transition: background 300ms var(--ease), box-shadow 300ms var(--ease), transform 300ms var(--ease);
}
.rail-mark em {
  position: absolute; right: 26px;
  font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.1em;
  color: var(--fg-mute); font-style: normal;
  opacity: 0; transform: translateX(6px);
  transition: opacity 300ms var(--ease), transform 300ms var(--ease);
}
.rail-mark.is-here i { background: var(--blue); box-shadow: 0 0 0 1px var(--blue), 0 0 12px rgba(46,134,255,0.8); transform: scale(1.25); }
.rail-mark.is-here em, .rail-mark:hover em { opacity: 1; transform: none; }

/* ═══════════ THE STAGE ═══════════════════════════════════════
   One fixed canvas under the entire document. Sections scroll
   over it as captions; there is no per-section canvas to fade,
   which is exactly why the motion reads as one continuous shot.
──────────────────────────────────────────────────────────────── */
.stage {
  position: fixed; inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
.stage-canvas { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 2; }
.stage-leaders {
  position: absolute; inset: 0; z-index: 3;
  width: 100%; height: 100%; overflow: visible;
}
.stage-labels { position: absolute; inset: 0; z-index: 4; }
.stage .field, .stage .grid-lines, .stage .scan { position: absolute; }
.stage .scan { z-index: 5; }

.leader { fill: none; stroke: rgba(199,224,255,0.4); stroke-width: 1; vector-effect: non-scaling-stroke; }
.leader-dot { fill: var(--blue); filter: drop-shadow(0 0 5px rgba(46,134,255,0.9)); }

.tech-label {
  position: absolute;
  display: flex; flex-direction: column; gap: 3px;
  max-width: 220px; opacity: 0;
  transform: translateY(-50%) translateX(var(--slide, 0px));
  will-change: opacity, transform;
}
.tech-label--left { text-align: right; align-items: flex-end; }
.tech-label--right { text-align: left; align-items: flex-start; }

/* ═══════════ BEATS ═══════════════════════════════════════════
   A beat owns a slice of scroll. Its copy holds still in a
   sticky frame while the stage underneath keeps moving.
──────────────────────────────────────────────────────────────── */
main { position: relative; z-index: 1; }
.beat { position: relative; }
.hold {
  position: sticky; top: 0;
  min-height: 100svh;
  display: grid; align-content: center;
  pointer-events: none;                    /* the stage stays visible/clickable-through */
}
.hold > * { pointer-events: auto; }
.beat--flow { padding: clamp(100px, 15vh, 180px) 0; }
/* the fixed nav sits over the top of every pinned stage, so a beat whose
   content is tall enough to reach it has to be pushed clear */
.beat--gap .hold { padding-top: 96px; }
/* The story lands here. These two are reading surfaces, so they go opaque and
   the subject rests behind them; #get stays transparent so the final flight
   into the nav mark happens over the closing call to action. */
#pricing { background: var(--ink); }
.foot { background: var(--ink); position: relative; z-index: 1; }
.beat--get { border-top: 1px solid var(--line); }

/* ── The film's length is the reading time. Do not shorten it. ──
   These look like a lot of scroll, and they are, but each beat's height
   IS how long its copy is on screen: the story is scrubbed by progress
   through the beat, so halving a beat does not trim the film, it plays
   the same arc at twice the speed with the same paragraphs to read.

   They were cut by ~40% on 2026-09-12 to get a first-time visitor to a
   price sooner, and the result was exactly that — the copy scrolled
   away before it could be read. Restored the same day. The right fix
   for "too long before I get an answer" is a way PAST the film, not a
   faster film: `.skip` points at #pricing, the hero carries "or skip to
   what it costs", the nav links jump, and anyone who has asked for
   reduced motion gets the phone build instead. */
.beat--hero { min-height: 300vh; }
.beat--gap { min-height: 220vh; }
.beat--tap { min-height: 280vh; }
/* The longest beat, on purpose: the camera explodes, names its five
   parts, closes again, and only then does the copy arrive. All of
   that inside one beat, so it needs the room. */
.beat--device { min-height: 460vh; }
.beat--circle { min-height: 260vh; }
.beat--custody { min-height: 200vh; }
/* The finale needs the same kind of room the other beats get. Left as a flow
   section it was 552px tall against an 823px viewport, so the last beat owned
   whatever scroll happened to be left over — about 90px for the whole
   reassembly and flight home. */
.beat--get { min-height: 220vh; }

/* the running clock */
.hud {
  position: fixed; z-index: 60;
  left: var(--gut); bottom: 22px;
  display: flex; align-items: center; gap: 12px;
  font-family: var(--mono); font-size: 0.64rem;
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--fg-mute);
  pointer-events: none;
}
.hud-tc { color: var(--fg); font-weight: 700; letter-spacing: 0.1em; font-feature-settings: 'tnum'; }
.hud-rule { width: 26px; height: 1px; background: var(--line-2); }

/* per-beat footer strip (was .hero-meta / .dev-meta) */
.beat-foot {
  position: absolute; left: 50%; bottom: 7%;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 16px;
  font-family: var(--mono); font-size: 0.62rem;
  letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--fg-mute); white-space: nowrap;
}

/* ═══════════ BEAT 0 · HERO ═══════════ */
.hero-intro {
  position: relative;
  width: 100%; max-width: var(--maxw);
  margin-inline: auto; padding-inline: var(--gut);
}
.hero-intro > * + * { margin-top: 26px; }
.hero-intro h1 { max-width: 15ch; }
.hero-intro .row { margin-top: 34px; }

.stat-row {
  display: flex; gap: clamp(22px, 3.4vw, 52px);
  margin: 44px 0 0; padding: 22px 0 0; list-style: none;
  border-top: 1px solid var(--line);
  max-width: 560px;
}
.stat-row b { display: block; font-size: 1.24rem; font-weight: 800; letter-spacing: -0.02em; }
.stat-row span {
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--fg-mute);
}

.hero-seal {
  position: absolute; left: 50%; bottom: 15%;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 16px;
  font-family: var(--mono); font-size: 0.66rem;
  letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--fg-dim); white-space: nowrap; opacity: 0;
}
.seal-k { color: var(--cyan); font-weight: 700; }

/* ═══════════ BEAT 1 · THE COST ═══════════════════════════════
   A ledger of wasted seconds. Instrument type, hairline rules,
   and a strike that is drawn rather than faded.
──────────────────────────────────────────────────────────────── */
.cost {
  max-width: min(860px, 100%);
  /* same trap as .tap-grid: .wrap sets margin-inline: auto, which re-centres
     this and pushes the second column under the subject */
  margin-left: 0;
  margin-right: auto;
}
.cost-head {
  display: flex; flex-wrap: wrap; gap: 18px;
  align-items: flex-end; justify-content: space-between;
  padding-bottom: 20px; border-bottom: 1px solid var(--line);
}
.cost-head .chapter-tag { margin-bottom: 0; }
.cost-clock { display: flex; align-items: baseline; gap: 7px; }
.cost-clock .clock-val {
  font-family: var(--mono); font-weight: 700;
  font-size: clamp(1.6rem, 3.2vw, 2.4rem);
  letter-spacing: -0.02em; color: var(--red);
  font-feature-settings: 'tnum';
}
.cost-clock .clock-unit { font-family: var(--mono); font-size: 0.9rem; color: var(--red); }
.cost-clock .clock-label {
  font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--fg-mute); margin-left: 6px;
}

.cost-list { list-style: none; margin: 0; padding: 0; }
.cost-row {
  position: relative;
  display: grid; align-items: center;
  grid-template-columns: 30px auto 1fr auto;
  gap: clamp(10px, 2vw, 22px);
  padding: clamp(12px, 1.9vh, 20px) 0;
  border-bottom: 1px solid var(--line);
  opacity: 0;
  transform: translateX(calc((1 - var(--arrive, 0)) * -14px));
}
.cost-mark {
  font-family: var(--mono); font-size: 1.3rem; font-weight: 700; line-height: 1;
  color: var(--red);
  opacity: var(--strike, 0);
  transform: scale(calc(0.6 + 0.4 * var(--strike, 0)));
}
.cost-step {
  position: relative;
  font-size: clamp(1.6rem, 4vw, 2.9rem);
  font-weight: 800; letter-spacing: -0.03em; line-height: 1;
  color: var(--fg);
  transition: color 400ms var(--ease);
}
/* the strike is drawn across the word, not faded in */
.cost-step::after {
  content: ''; position: absolute;
  left: -2px; right: -2px; top: 54%;
  height: max(2px, 0.055em);
  background: var(--red);
  transform: scaleX(var(--strike, 0));
  transform-origin: left;
}
.cost-row.is-gone .cost-step { color: var(--fg-mute); }
.cost-rule {
  height: 1px;
  background: repeating-linear-gradient(to right,
    var(--line-2) 0 3px, transparent 3px 8px);
}
.cost-t {
  font-family: var(--mono); font-size: 0.68rem;
  letter-spacing: 0.14em; color: var(--fg-mute);
  font-feature-settings: 'tnum';
}
.cost-row.is-gone .cost-t { color: rgba(255,59,48,0.75); }

.cost-foot {
  display: grid; gap: 12px;
  margin-top: clamp(26px, 4vh, 44px);
  max-width: 52ch;
}
.cost-line {
  font-size: clamp(1rem, 1.35vw, 1.2rem);
  line-height: 1.62; color: var(--fg-dim);
  opacity: 0;
}
.cost-line--dim { color: var(--fg-mute); font-size: clamp(0.92rem, 1.1vw, 1.02rem); }

/* ═══════════ 03 · ONE TAP ═══════════ */
.tap-grid {
  display: grid; gap: clamp(30px, 3.4vw, 54px);
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
  align-items: center;
  /* the stage needs the right third of the frame — the subject lives there
     during this beat and must not sit on top of the readout */
  max-width: 900px;
  /* .wrap sets margin-inline: auto, which re-centres this and pushes it back
     under the subject. Pin it left explicitly. */
  margin-left: 0;
  margin-right: auto;
}
@media (min-width: 1500px) { .tap-grid { max-width: 1000px; } }
.tap-copy > * + * { margin-top: 22px; }
/* This column carries the most of any beat — headline, paragraph, both
   platforms, the clock and the bar — and the beat is pinned, so it has one
   viewport and no more. A notch down on the display size is what buys the
   platform rows their room without shrinking anything else on the page. */
.tap-copy h2 { font-size: clamp(2rem, 3.7vw, 3.2rem); }
.tap-clock {
  display: flex; align-items: baseline; gap: 8px;
  margin-top: 22px;
}
.clock-val {
  font-family: var(--mono); font-weight: 700;
  font-size: clamp(2.4rem, 5vw, 3.6rem);
  letter-spacing: -0.03em; color: var(--fg);
  font-feature-settings: 'tnum';
}
.clock-unit { font-family: var(--mono); font-size: 1.1rem; color: var(--blue); }
.clock-label {
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--fg-mute); margin-left: 8px;
}
/* Per-platform reality, set as a definition list because that is what it is */
/* Side by side: the two platforms are a comparison, and reading them as one
   is the whole point of putting them here. */
.platforms {
  margin: 18px 0 0; padding: 0;
  display: grid; grid-template-columns: 1fr 1fr; gap: 20px;
}
.platforms > div { padding-top: 9px; border-top: 1px solid var(--line); }
.platforms dt {
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--blue); margin-bottom: 5px;
}
.platforms dd {
  margin: 0; font-size: 0.82rem; line-height: 1.5; color: var(--fg-dim);
}
@media (max-width: 900px), (prefers-reduced-motion: reduce) {
  .platforms { grid-template-columns: 1fr; gap: 10px; }
}

.pipe-bar { height: 2px; background: var(--line); margin-top: 18px; max-width: 320px; }
.pipe-bar-fill { height: 100%; background: var(--blue); transform-origin: left; transform: scaleX(0); }

.pipe { position: relative; border-top: 1px solid var(--line); }
.pipe-row {
  position: relative;
  display: grid; align-items: baseline;
  grid-template-columns: 62px 128px minmax(0, 1fr);
  gap: 16px;
  padding: 18px 4px;
  border-bottom: 1px solid var(--line);
  opacity: 0.5;   /* see the note in sections.js: the old 0.28 vanished on light */
}
.pipe-t { font-family: var(--mono); font-size: 0.66rem; color: var(--fg-mute); }
.pipe-k {
  font-family: var(--mono); font-size: 0.7rem; font-weight: 700;
  letter-spacing: 0.13em; color: var(--fg-dim);
  transition: color 300ms var(--ease);
}
.pipe-v {
  font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.03em;
  color: var(--fg-mute);
  overflow-wrap: anywhere;
}
.pipe-row.is-live .pipe-k { color: var(--blue); }
.pipe-row.is-live::before {
  content: ''; position: absolute; left: -1px; top: 0; bottom: 0;
  width: 2px; background: var(--blue);
}
/* The step that isn't on the clock: it happens when the network allows, so it
   sits off the timeline rather than pretending to a timestamp. */
.pipe-row--later { border-bottom: 0; }
.pipe-row--later .pipe-t { font-style: italic; opacity: 0.7; }
.pipe-row.is-done .pipe-k { color: var(--cyan); }
.pipe-row.is-done::before { background: var(--cyan); }
.pipe-row.is-done .pipe-v { color: var(--fg-dim); }
.pipe-playhead {
  position: absolute; left: 0; right: 0; height: 1px;
  background: linear-gradient(to right, var(--cyan), rgba(103,232,249,0));
  box-shadow: 0 0 12px rgba(103,232,249,0.6);
  opacity: 0; z-index: 2;
}

/* ═══════════ 04 · DEVICE ═══════════ */
/* No panel. One was tried on 2026-09-12 to make this readable while the
   camera was still exploded behind it; it did not fix the mess, because
   the mess was two things happening at once, not contrast. The fix is
   in the timeline: the camera gets its own stretch of the beat, and
   this arrives after the parts have closed and the labels have gone. */
.dev-copy {
  position: absolute; z-index: 5;
  left: clamp(20px, 7vw, 110px); top: 50%;
  width: min(46%, 520px);
  transform: translateY(-50%);
  opacity: 0;
}
.dev-copy > * + * { margin-top: 20px; }
.dev-copy .row { margin-top: 30px; }

/* ═══════════ 05 · GUARDIAN CIRCLE ═══════════ */
.guard-canvas { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; pointer-events: none; }
.circle-grid {
  position: relative; z-index: 2;
  display: grid; gap: clamp(38px, 5vw, 70px);
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr);
  align-items: center;
}
.circle-copy > * + * { margin-top: 22px; }
.note {
  display: flex; flex-direction: column; gap: 7px;
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-left: 2px solid var(--blue);
  border-radius: 4px;
  background: rgba(46,134,255,0.04);
  font-size: 0.88rem; color: var(--fg-dim);
  max-width: 46ch;
}
/* the three lock screens the alerts land on — drawn as hardware, not cards.
   The screen sleeps at reduced brightness; the arriving pulse (.is-lit,
   toggled by the canvas choreography) is what wakes each backlight. */
.phones {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(12px, 1.8vw, 26px);
  align-items: end; justify-items: center;
  /* small resting headroom — the real room is made in motion: the phones
     dip (translateY, driven by makeGuardians) as the camera arrives above */
  margin-top: clamp(24px, 4vh, 48px);
}
.phone-slot { margin: 0; display: grid; gap: 13px; justify-items: center; width: 100%; }
.phone-slot > figcaption {
  font-size: 0.6rem; letter-spacing: 0.18em; color: var(--fg-mute);
}
.phone {
  position: relative; width: 100%; max-width: 196px;
  aspect-ratio: 9 / 18.6;
  border-radius: clamp(26px, 2.6vw, 38px);
  padding: 6px;
  background: linear-gradient(155deg, #46516A 0%, #171C28 38%, #0D111A 62%, #333D52 100%);
  box-shadow: 0 34px 80px rgba(0,0,0,0.6), inset 0 1px 1px rgba(255,255,255,0.22);
}
/* the owner's phone reads nearest */
.phone-slot:nth-child(2) .phone { max-width: 216px; }
/* volume / power buttons on the rim */
.phone::before, .phone::after {
  content: ''; position: absolute; width: 2.5px; border-radius: 2px;
  background: #39435A;
}
.phone::before { left: -2.5px; top: 21%; height: 13%; }
.phone::after { right: -2.5px; top: 24%; height: 9%; }
.phone-screen {
  position: relative; width: 100%; height: 100%;
  border-radius: clamp(21px, 2.2vw, 32px);
  overflow: hidden;
  background:
    radial-gradient(130% 85% at 50% -6%, rgba(46,134,255,0.34), transparent 62%),
    radial-gradient(90% 70% at 82% 104%, rgba(103,232,249,0.14), transparent 58%),
    linear-gradient(180deg, #0C1322 0%, #05070C 78%);
  display: flex; flex-direction: column; align-items: center;
  padding: clamp(32px, 4.2vh, 46px) 8px 18px;
  filter: brightness(0.42) saturate(0.75);
  transition: filter 700ms var(--ease);
}
.phone:has(.guard-card.is-lit) .phone-screen { filter: none; }
.phone-island {
  position: absolute; top: 11px; left: 50%; transform: translateX(-50%);
  width: 31%; height: 13px; border-radius: 999px;
  background: #04060A; z-index: 2;
  box-shadow: inset 0 0 3px rgba(255,255,255,0.14);
}
.phone-lock { width: 11px; height: 11px; color: rgba(232,238,248,0.7); margin-bottom: 7px; }
.phone-date { font-size: 0.56rem; color: rgba(232,238,248,0.72); letter-spacing: 0.04em; }
.phone-time {
  font-size: clamp(1.6rem, 2.3vw, 2.2rem); font-weight: 300;
  line-height: 1.12; color: #EAF0FA; letter-spacing: 0.01em;
}
.phone .guard-card {
  margin-top: clamp(12px, 2vh, 22px); width: 100%;
  display: flex; align-items: flex-start; gap: 8px;
  padding: 9px 10px; border-radius: 14px;
  background: rgba(96,112,138,0.32);
  border: 1px solid rgba(255,255,255,0.09);
  box-shadow: 0 10px 26px rgba(0,0,0,0.35);
}
.gc-icon {
  flex: none; width: 25px; height: 25px; border-radius: 7px;
  background: linear-gradient(140deg, #4C95FF, #1C63D8);
  display: grid; place-items: center;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.3);
}
.gc-icon svg { width: 14px; height: 14px; }
.gc-body { display: grid; gap: 2px; min-width: 0; text-align: left; }
.gc-body header {
  display: flex; justify-content: space-between; gap: 8px;
  font-family: var(--mono); font-size: 0.5rem; letter-spacing: 0.14em;
  color: rgba(232,238,248,0.6); text-transform: uppercase;
}
.gc-body strong {
  font-size: 0.68rem; font-weight: 600; line-height: 1.35; color: #EAF0FA;
}
.gc-sub { font-size: 0.58rem; line-height: 1.45; color: rgba(232,238,248,0.62); }
.phone-home {
  position: absolute; bottom: 7px; left: 50%; transform: translateX(-50%);
  width: 32%; height: 3.5px; border-radius: 999px;
  background: rgba(255,255,255,0.32);
}
.guard-card {
  display: grid; gap: 6px;
  padding: 16px 18px;
  border: 1px solid var(--line-2);
  border-radius: 14px;
  background: rgba(14,20,30,0.82);
  backdrop-filter: blur(10px);
  box-shadow: 0 18px 44px rgba(0,0,0,0.5);
  opacity: 0;
}
.guard-card header {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--mono); font-size: 0.6rem;
  letter-spacing: 0.2em; color: var(--fg-mute);
}
.guard-card strong { font-size: 0.98rem; font-weight: 600; letter-spacing: -0.01em; }
.guard-card .mono { color: var(--fg-mute); }

/* ═══════════ 06 · CHAIN OF CUSTODY ═══════════
   One comparison, read left to right: the photo you already take, and the
   record GOTCHA turns it into. Plain claims, no jargon above the fold. */
.vs-head { margin-bottom: clamp(26px, 4.5vh, 50px); max-width: 640px; }
.vs-head .body { margin-top: 14px; }
.versus {
  display: grid; gap: clamp(16px, 2vw, 26px);
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
}
.vs-panel {
  border: 1px solid var(--line-2);
  border-radius: 18px;
  padding: clamp(22px, 2.6vw, 32px);
  background: linear-gradient(165deg, rgba(16,22,33,0.92), rgba(9,13,20,0.92));
  display: flex; flex-direction: column; gap: 18px;
}
.vs-panel ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 14px; }
.vs-panel li {
  position: relative; padding-left: 30px;
  font-size: 0.95rem; line-height: 1.6; color: var(--fg-dim);
}
.vs-panel li::before {
  position: absolute; left: 0; top: 1px;
  font-family: var(--mono); font-weight: 700;
}
.vs-panel--plain .tech-k { color: var(--fg-mute); }
.vs-panel--plain li::before { content: '✕'; color: rgba(224,109,88,0.85); }
.vs-panel--record {
  border-color: rgba(46,134,255,0.45);
  background: linear-gradient(165deg, rgba(18,32,54,0.94), rgba(9,13,20,0.92));
  box-shadow: 0 24px 60px rgba(10,30,70,0.35);
}
.vs-panel--record li { color: var(--fg); }
.vs-panel--record li::before { content: '✓'; color: #67E8F9; }
.vs-foot {
  margin-top: clamp(22px, 3.5vh, 36px);
  display: flex; flex-wrap: wrap; gap: 18px; align-items: center;
}
.vs-foot a { color: inherit; }
@media (max-width: 900px), (prefers-reduced-motion: reduce) {
  .versus { grid-template-columns: 1fr; }
}

/* ═══════════ 07 · PRICING ═══════════ */
#pricing { text-align: center; }
#pricing .chapter-tag { justify-content: center; }
.plans {
  display: grid; gap: 18px; margin-top: clamp(44px, 7vh, 76px);
  grid-template-columns: repeat(auto-fit, minmax(258px, 1fr));
  text-align: left;
}
.plan {
  display: flex; flex-direction: column; gap: 16px;
  padding: 30px 26px;
  border: 1px solid var(--line-2);
  border-radius: 20px;
  background: rgba(12,17,26,0.7);
}
.plan--hero {
  border-color: rgba(46,134,255,0.55);
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(46,134,255,0.14), transparent 60%),
    rgba(12,17,26,0.9);
  box-shadow: 0 26px 70px rgba(46,134,255,0.14);
}
.price { display: flex; align-items: baseline; gap: 6px; }
.price b { font-size: 2.1rem; font-weight: 800; letter-spacing: -0.03em; }
.price span { font-family: var(--mono); font-size: 0.72rem; color: var(--fg-mute); }
.plan ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 10px; flex: 1; }
.plan li {
  position: relative; padding-left: 22px;
  font-size: 0.9rem; color: var(--fg-dim);
}
.plan li::before {
  content: ''; position: absolute; left: 2px; top: 8px;
  width: 8px; height: 8px; border-radius: 50%;
  border: 1px solid var(--blue);
}
.plan--hero li::before { background: rgba(46,134,255,0.4); }
.plan-note { color: var(--fg-mute); }

/* ═══════════ GET ═══════════ */
#get {
  background: radial-gradient(120% 80% at 50% 110%, rgba(46,134,255,0.16), transparent 62%);
  text-align: center;
}
.get-in { display: grid; gap: 22px; justify-items: center; }
.get-in h2 { max-width: 22ch; }

/* Store-badge download buttons: icon left, two-line label right. Both
   platforms get the same treatment — the badge itself is the emphasis. */
.store-btn {
  display: inline-flex; align-items: center; gap: 13px;
  padding: 13px 30px 13px 22px;
  border-radius: 16px;
  border: 1px solid var(--line-2);
  background: linear-gradient(160deg, rgba(150,180,225,0.09), rgba(150,180,225,0.03));
  color: var(--fg);
  transition: transform 240ms var(--ease), border-color 240ms var(--ease),
              background 240ms var(--ease), box-shadow 240ms var(--ease);
}
.store-btn svg { width: 26px; height: 26px; flex: none; }
.store-btn .sb-txt {
  display: flex; flex-direction: column; align-items: flex-start;
  line-height: 1.2; text-align: left;
}
.store-btn .sb-k {
  font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--fg-dim);
}
.store-btn .sb-v { font-size: 1.08rem; font-weight: 650; letter-spacing: -0.01em; }
.store-btn:hover {
  transform: translateY(-2px);
  border-color: rgba(150,180,225,0.55);
  background: linear-gradient(160deg, rgba(150,180,225,0.14), rgba(150,180,225,0.05));
  box-shadow: 0 14px 36px rgba(10,20,40,0.5);
}

/* ═══════════ FOOTER ═══════════ */
.foot { border-top: 1px solid var(--line); padding: clamp(56px, 8vh, 90px) 0 34px; }
.foot-in {
  display: grid; gap: clamp(34px, 5vw, 70px);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
  align-items: start;
}
.foot-brand { display: grid; gap: 10px; align-content: start; }
.foot-cols { display: grid; gap: 26px; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.foot-cols div { display: grid; gap: 9px; align-content: start; }
.foot-cols a { font-size: 0.9rem; color: var(--fg-dim); transition: color 200ms var(--ease); }
.foot-cols a:hover { color: var(--fg); }
.foot-base {
  display: flex; flex-wrap: wrap; gap: 14px; justify-content: space-between;
  margin-top: clamp(40px, 6vh, 70px); padding-top: 22px;
  border-top: 1px solid var(--line);
}
.foot-credit {
  display: flex; justify-content: center; text-align: center;
  padding-top: 20px; padding-bottom: 30px;
  color: var(--fg-mute);
}
.foot-credit a {
  color: var(--fg-mute); border-bottom: 1px solid var(--line-2);
  padding-bottom: 1px; transition: color 200ms var(--ease), border-color 200ms var(--ease);
}
.foot-credit a:hover { color: var(--blue); border-bottom-color: var(--blue); }

/* ═══════════ RESPONSIVE ═══════════ */
@media (max-width: 1080px) { .rail { display: none; } }

/* ════════════════════════════════════════════════════════════════
   THE PHONE LAYOUT

   Desktop is a pinned scroll story: eight beats, each holding the frame
   still for two or three screens while one continuous 3D shot plays over
   them, the copy in a column on the left and the subject parked beside it
   on the right. That composition depends on width, and a phone has none.

   Three attempts at translating it failed in the same way, so the
   translation is the mistake. What a pinned beat does on a phone is take
   the scroll away: the thumb moves and the picture does not, for three
   screens at a time, with the subject stuck in a strip at the top of all
   eight of them. And forcing each beat into one viewport meant setting the
   copy at 0.86rem with 6px rows to make it fit, which is how the type came
   to look cheap.

   So the phone does not get the film. It gets the same story told the way
   a phone reads: the page scrolls, and things move when your thumb moves.

     · Nothing is pinned. Every beat is an ordinary section, at least one
       screen tall, that scrolls past like any other page.
     · No beat has to fit one screen. The ones with a six-row timeline or
       two comparison panels are simply taller, and scroll — which is what
       buys the type back its size and spacing.
     · The subject gets a real slot in the layout (`.mslot`, injected by
       story.js) rather than floating in the fixed layer. It scrolls with
       its section like a picture would, and cannot land on the copy
       because the copy is laid out underneath it.
     · Where the DOM is the thing worth looking at — the pipeline, the
       three lock screens, the two panels — the slot is small or absent and
       the subject fades out entirely rather than lurking behind the text.

   The subject still morphs continuously with scroll; it is still the same
   one object from the mark to the camera to the node and home. It just is
   not holding the frame hostage while it does it.
════════════════════════════════════════════════════════════════ */
@media (max-width: 900px), (prefers-reduced-motion: reduce) {
  .nav-links, .nav-cta { display: none; }
  .nav-burger { display: flex; }

  /* ── the beat, unpinned ── */
  .beat { min-height: 0; }
  .beat .hold {
    position: relative;          /* not sticky: the thumb moves the picture */
    min-height: 100svh;
    align-content: center;
    padding: calc(var(--nav-h, 58px) + 18px) 0 78px;
  }
  /* the flow sections were never pinned */
  .beat--flow { padding: clamp(72px, 10vh, 110px) 0; }

  /* The subject's place in the layout. story.js sets `--slot` per beat and
     injects the element; height 0 means this beat does not show it. */
  .mslot {
    height: var(--slot, 0px);
    margin-bottom: var(--slot-gap, 26px);
    pointer-events: none;
  }
  .mslot[style*="--slot: 0"] { margin-bottom: 0; }

  /* ── type, at a size worth reading ──
     Every one of these was compressed to force a beat into a single
     viewport. Nothing has to fit any more. */
  h1 { font-size: clamp(2.3rem, 10.5vw, 3rem); }
  h2 { font-size: clamp(2rem, 9vw, 2.6rem); }
  .lede, .body { font-size: 1rem; line-height: 1.68; }
  .chapter-tag { margin-bottom: 20px; }
  .eyebrow, .chapter-tag { font-size: 0.64rem; letter-spacing: 0.17em; }

  .wrap { padding-inline: 22px; }

  .hero-intro > * + * { margin-top: 22px; }
  .hero-intro h1 { max-width: 16ch; }
  .hero-intro .row { margin-top: 28px; }
  .stat-row { flex-wrap: wrap; gap: 18px 30px; margin-top: 30px; padding-top: 22px; }
  .stat-row b { font-size: 1.18rem; }
  .stat-row span { font-size: 0.58rem; }

  .cost-row { grid-template-columns: 24px auto 1fr; gap: 12px; padding: 15px 0; }
  .cost-t { display: none; }        /* the clock already carries the number */
  .cost-head { padding-bottom: 18px; }
  .cost-foot { margin-top: 24px; }

  .tap-grid, .circle-grid, .foot-in { grid-template-columns: minmax(0, 1fr); }
  .tap-grid { gap: 30px; }
  .tap-copy > * + * { margin-top: 20px; }
  .tap-clock { margin-top: 20px; }
  .clock-val { font-size: 2.3rem; }
  .platforms { margin-top: 18px; gap: 14px; }
  .platforms dd { font-size: 0.86rem; line-height: 1.55; }
  .pipe-row { grid-template-columns: 58px 1fr; gap: 4px 12px; padding: 14px 2px; }
  .pipe-v { grid-column: 1 / -1; font-size: 0.72rem; line-height: 1.5; }
  .pipe-k { font-size: 0.68rem; }
  .pipe-t { font-size: 0.64rem; }

  .circle-grid { gap: 30px; }
  .phone, .phone-slot:nth-child(2) .phone { max-width: 104px; }
  .phones { margin-top: 10px; gap: 10px; }
  .phone-slot { gap: 9px; }
  .phone-screen { padding: 19px 5px 12px; }
  .phone-time { font-size: 1.1rem; }
  .phone-date { font-size: 0.44rem; }
  .phone-island { height: 9px; }
  .phone .guard-card { margin-top: 8px; padding: 5px; gap: 4px; border-radius: 9px; }
  .gc-body strong { font-size: 0.48rem; line-height: 1.25; }
  .gc-sub { font-size: 0.42rem; line-height: 1.3; }
  .gc-body strong, .gc-sub, .gc-body header { overflow-wrap: anywhere; }
  .gc-body header { font-size: 0.42rem; letter-spacing: 0.06em; }
  .gc-icon { width: 14px; height: 14px; border-radius: 5px; }
  .gc-icon svg { width: 8px; height: 8px; }
  .phone-slot > figcaption { font-size: 0.5rem; letter-spacing: 0.1em; }
  .note { padding: 16px 18px; font-size: 0.9rem; }

  .vs-head { margin-bottom: 24px; }
  .vs-panel { padding: 22px 20px; gap: 14px; border-radius: 16px; }
  .vs-panel li { font-size: 0.92rem; line-height: 1.6; padding-left: 28px; }
  .vs-panel ul { gap: 13px; }
  .versus { gap: 16px; }
  .vs-foot { margin-top: 26px; gap: 16px; }

  .plan { padding: 28px 24px; gap: 16px; }
  .plans { gap: 16px; }

  /* The beat footers and the running clock are frame furniture: they made
     sense pinned to a held frame. Nothing is held now, and on a scrolling
     page they are two more things crossing the copy. */
  /* The beat footers, the running clock and the sealed strip are all frame
     furniture: they were pinned to a frame that was being held still. On a
     page that scrolls they are three more things crossing the copy, and the
     sealed strip does not even fit — 456px of nowrap on a 390px screen, the
     one thing giving the document a horizontal scrollbar. Nothing is lost:
     the timeline's last row already says SEALED, on your device, offline. */
  .beat-foot, .hud, .hero-seal { display: none; }

  /* Leader lines point from a label in the margin to a part of the
     subject; a phone has no margin, so the lines go and the callouts
     become a caption under the slot they describe. The mark's three go
     entirely — with no line they name nothing, and the hero copy two
     inches below is already saying it. */
  .stage-leaders { display: none; }
  .tech-label--mark { display: none; }
  .stage-labels {
    position: absolute; inset: auto 22px auto 22px;
    top: var(--labels-top, 0px);
    display: grid; grid-template-columns: 1fr 1fr;
    gap: 8px 14px; align-content: start;
  }
  .tech-label {
    position: static; transform: translateX(var(--slide, 0px));
    text-align: left; align-items: flex-start; max-width: none;
  }
  .tech-k { font-size: 0.58rem; letter-spacing: 0.14em; }
  .tech-v { font-size: 0.58rem; line-height: 1.45; }

  /* Absolutely placed on desktop so it can sit beside the exploded
     camera; here it is just the copy under the slot. */
  .dev-copy {
    position: static; width: auto; transform: none;
    text-align: center; opacity: 1;
  }
  .dev-copy > * + * { margin-top: 20px; }
  .dev-copy .row { margin-top: 28px; justify-content: center; }
  .dev-copy .body { margin-inline: auto; }

  .foot-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* no-WebGL: the copy has to survive on its own */
.is-nogl .stage-canvas, .is-nogl .stage-leaders, .is-nogl .stage-labels { display: none; }
.is-nogl .dev-copy, .is-nogl .hero-intro { opacity: 1 !important; transform: none !important; }

@media (prefers-reduced-motion: reduce) {
  .dev-copy, .hero-intro, .guard-card, .tech-label { opacity: 1 !important; transform: none !important; }
}

/* a card that the pulse has reached */
.guard-card.is-lit {
  border-color: rgba(46,134,255,0.5);
  box-shadow: 0 18px 44px rgba(0,0,0,0.5), 0 0 0 1px rgba(46,134,255,0.18),
              0 0 30px rgba(46,134,255,0.1);
}

/* staggered rise for grid items (pricing, platform) */
.stg {
  opacity: 0;
  transform: translateY(38px);
  transition: opacity 700ms var(--ease), transform 700ms var(--ease);
}
.stg.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .stg { opacity: 1; transform: none; transition: none; }
}

/* ═══════════ THEME TOGGLE ═══════════ */
.nav-theme {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; margin-right: 14px; flex: none;
  border-radius: 999px; border: 1px solid var(--line-2);
  background: transparent; color: var(--fg); cursor: pointer;
  transition: border-color 240ms var(--ease), background 240ms var(--ease);
}
.nav-theme:hover { border-color: rgba(150,180,225,0.5); background: rgba(150,180,225,0.07); }
.nav-theme svg { width: 15px; height: 15px; }
.nav-theme .ic-moon { display: none; }
html[data-theme="light"] .nav-theme .ic-sun { display: none; }
html[data-theme="light"] .nav-theme .ic-moon { display: block; }

/* ═══════════ LIGHT THEME SURFACES ═══════════
   Variables in base.css flip the field; these override the few surfaces
   that hard-code their dark glass. The phones and their lock screens stay
   dark on purpose — they are hardware, not page chrome. */
html[data-theme="light"] .nav.is-condensed { background: rgba(242,247,255,0.78); }
html[data-theme="light"] .menu { background: rgba(242,247,255,0.97); }
/* sheets of morning glass: white with a deep-blue drop shadow */
html[data-theme="light"] .vs-panel {
  background: linear-gradient(168deg, rgba(255,255,255,0.95) 0%, rgba(244,249,255,0.9) 100%);
  box-shadow: 0 2px 0 rgba(28,70,190,0.06), 0 24px 52px rgba(20,55,150,0.15);
}
html[data-theme="light"] .vs-panel--record {
  background: linear-gradient(168deg, #E9F1FF 0%, #D3E3FF 60%, #C4DAFF 100%);
  border-color: rgba(28,99,216,0.6);
  box-shadow: 0 2px 0 rgba(28,99,216,0.12), 0 28px 60px rgba(28,99,216,0.26);
}
html[data-theme="light"] .vs-panel--record li { color: var(--fg); }
html[data-theme="light"] .vs-panel--record li::before { color: #1C63D8; }
html[data-theme="light"] .plan {
  background: rgba(255,255,255,0.88);
  box-shadow: 0 20px 44px rgba(20,55,150,0.13);
}
html[data-theme="light"] .plan--hero {
  background: linear-gradient(172deg, #E9F1FF 0%, #D3E3FF 60%, #C4DAFF 100%);
  box-shadow: 0 26px 56px rgba(28,99,216,0.28);
}
html[data-theme="light"] .store-btn {
  background: linear-gradient(160deg, rgba(255,255,255,0.96), rgba(240,246,255,0.92));
  box-shadow: 0 14px 30px rgba(20,55,150,0.15);
}
html[data-theme="light"] .note { background: rgba(46,134,255,0.08); }
/* ── one blue, floor to ceiling ──
   The sky is a single hue played at every volume: saturated ultramarine
   in the upper corners, a bright azure heart of light, ice, then white.
   Nothing else gets a colour — consistency IS the design. */
html[data-theme="light"] body {
  background:
    repeating-linear-gradient(0deg,
      rgba(28,70,190,0.05) 0, rgba(28,70,190,0.05) 1px, transparent 1px, transparent 56px),
    repeating-linear-gradient(90deg,
      rgba(28,70,190,0.05) 0, rgba(28,70,190,0.05) 1px, transparent 1px, transparent 56px),
    radial-gradient(34% 30% at 86% 4%, rgba(255,255,255,0.85), transparent 60%),
    radial-gradient(52% 42% at 84% 0%, rgba(83,168,255,0.5), transparent 66%),
    radial-gradient(56% 44% at 2% 2%, rgba(28,80,255,0.52), transparent 64%),
    radial-gradient(72% 52% at 50% 116%, rgba(120,180,255,0.35), transparent 64%),
    linear-gradient(176deg,
      #6E97FF 0%, #94B8FF 24%, #C6DCFF 50%, #E7F1FF 74%, #F2F7FF 100%);
  background-attachment: fixed;
}
/* the italic voice: one blue, deep to bright. */
html[data-theme="light"] em, html[data-theme="light"] .serif {
  background: linear-gradient(115deg, #1B3BEA 0%, #2E86FF 55%, #38BDF2 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ═══════════ THE LIST ═══════════════════════════════════════
   The waitlist section. A flow section after the last beat, so it
   needs its own surface — the stage is fixed behind the whole
   document, and without a background the film shows through the
   form. `#pricing` does the same thing for the same reason.
──────────────────────────────────────────────────────────────── */
.waitlist { background: var(--ink); position: relative; z-index: 1; }
/* `.wrap` centres itself, so narrowing it centres this block while every
   other section on the page starts at the left gutter. Keep the measure,
   drop the centring. */
.wl-in { max-width: 620px; margin-inline: 0; }
.waitlist h2 { margin-bottom: 18px; }
.waitlist .body { margin-bottom: 30px; }

.wl-form { margin-top: 8px; }
.wl-row { display: grid; gap: 14px; }
@media (min-width: 620px) { .wl-row { grid-template-columns: 1fr 1.05fr; } }

.wl-field { display: block; }
.wl-label {
  display: block; margin-bottom: 7px;
  font-size: 0.82rem; font-weight: 600; color: var(--fg-dim);
}
.wl-field input, .wl-field select {
  width: 100%;
  /* 16px minimum. Anything smaller and iOS Safari zooms the whole page
     in when the field takes focus, which on a phone reads as the site
     breaking. */
  font: 500 16px/1.4 var(--sans);
  color: var(--fg);
  background: var(--ink-2);
  border: 1px solid var(--line-2);
  border-radius: 12px;
  padding: 14px 15px;
  min-height: 50px;
  appearance: none;
  transition: border-color 200ms var(--ease);
}
.wl-field select {
  /* appearance:none removes the native arrow, so draw one */
  background-image: linear-gradient(45deg, transparent 50%, var(--fg-mute) 50%),
                    linear-gradient(135deg, var(--fg-mute) 50%, transparent 50%);
  background-position: calc(100% - 20px) 50%, calc(100% - 15px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 40px;
}
.wl-field input:focus, .wl-field select:focus {
  border-color: var(--blue); outline: none;
}
.wl-field input[aria-invalid="true"] { border-color: var(--red); }
.wl-field input::placeholder { color: var(--fg-mute); }

.wl-form > .btn { margin-top: 18px; width: 100%; }
@media (min-width: 620px) { .wl-form > .btn { width: auto; } }
.wl-form > .btn[disabled] { opacity: 0.6; pointer-events: none; }

.wl-note {
  margin-top: 16px; color: var(--fg-mute);
  font-size: 0.85rem; line-height: 1.6;
}
.wl-note a { color: var(--fg-dim); text-decoration: underline; }

.wl-msg { margin-top: 16px; font-size: 0.93rem; line-height: 1.55; display: none; }
.wl-msg.is-shown { display: block; }
.wl-msg.is-ok { color: var(--ok); }
.wl-msg.is-err { color: var(--red); }

/* Off-screen rather than display:none — a bot that skips hidden fields
   still fills this one, and no human ever reaches it. */
.wl-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ═══════════ MOBILE USABILITY LAYER ═══════════════════════════
   The film is built for a large screen and stays that way. What
   follows only applies below the breakpoint, where a phone has
   neither the room for the choreography nor the patience for it.
──────────────────────────────────────────────────────────────── */
@media (max-width: 900px), (prefers-reduced-motion: reduce) {
  /* An action, at all. `.nav-cta` was hidden here, which left a phone
     with a theme switch and a hamburger as its only controls — the one
     thing a visitor might want to do was behind a menu, and the hero's
     own buttons sit below the fold under the 3D. */
  .nav-cta {
    display: inline-flex;
    --pad: 9px 16px;
    font-size: 0.8rem;
    margin-left: auto;
  }
  .nav-brand { margin-right: 0; }
}

/* Under ~560px the header runs out of room: brand + theme + CTA +
   burger stop fitting on one line. The theme switch is what goes —
   it is a preference, and the CTA is the reason the page exists. */
@media (max-width: 560px) {
  .nav-theme { display: none; }
  .nav-cta { --pad: 9px 13px; font-size: 0.76rem; }
}

/* ── The short version ────────────────────────────────────────
   Phone only. The film explains GOTCHA over eight screens, which
   works when the screens are large and you are scrolling with a
   mouse. On a phone the first question — what IS this — should be
   answered before the story starts, not by it. Hidden above the
   breakpoint, where the film does this job properly. */
.tldr { display: none; }
@media (max-width: 900px), (prefers-reduced-motion: reduce) {
  .tldr {
    display: block;
    position: relative; z-index: 1;
    background: var(--ink);
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    padding: 34px 0 38px;
  }
  .tldr h2 {
    font-size: 1.34rem; letter-spacing: -0.02em; line-height: 1.25;
    margin-bottom: 18px;
  }
  .tldr-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 16px; }
  .tldr-list li {
    display: grid; grid-template-columns: 22px 1fr; gap: 12px;
    font-size: 0.97rem; line-height: 1.6; color: var(--fg-dim);
  }
  .tldr-list b { color: var(--fg); font-weight: 700; }
  .tldr-list li::before {
    content: '✓'; font-family: var(--mono); color: var(--blue);
    font-size: 0.85rem; line-height: 1.75;
  }
  .tldr .row { margin-top: 26px; }
  .tldr .btn { width: 100%; }
  .tldr-note { display: block; margin-top: 14px; color: var(--fg-mute); }
}

/* ── FAQ ──────────────────────────────────────────────────────
   `<details>` is the browser's own disclosure widget: no script,
   keyboard-operable and labelled for screen readers for free, and
   find-in-page opens it. */
/* Over the fixed stage, so it needs its own surface — see the
   note on `.waitlist` above. */
.faq-sec { background: var(--ink); position: relative; z-index: 1; }
.faq-in { max-width: 74ch; }
.faq { display: grid; margin-top: 30px; }
.faq details { border-top: 1px solid var(--line); }
.faq details:last-of-type { border-bottom: 1px solid var(--line); }
.faq summary {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 19px 0;
  font-weight: 600; font-size: 1rem;
  cursor: pointer; list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: '+'; font-family: var(--mono); font-size: 1.2rem;
  color: var(--blue); flex: none;
}
.faq details[open] summary::after { content: '−'; }
.faq details > p {
  padding-bottom: 22px; margin: 0;
  color: var(--fg-dim); font-size: 0.96rem; line-height: 1.7;
}

/* ═══════════ PHONE / DESKTOP SPLIT ═══════════════════════════
   Below the breakpoint the film is not shown, not started, and not
   downloaded (see the <template> in index.html). The beats are
   hidden outright rather than reflowed: their copy is written as
   captions for choreography — the pipeline rows, the wasted-seconds
   clock, the labels that get drawn onto the subject — and without
   the subject they read as fragments. More to the point, most of
   that content is `opacity: 0` in this stylesheet and is lit by
   story.js; with the film never starting, hiding it is the only
   honest option. `.phone-page` says the same things in sentences.
──────────────────────────────────────────────────────────────── */
.phone-page { display: none; }

@media (max-width: 900px), (prefers-reduced-motion: reduce) {
  .phone-page { display: block; }

  /* the film and its furniture */
  .beat, .stage, .hud, .rail { display: none !important; }

  .pp { padding: 40px 0; border-top: 1px solid var(--line); }
  .pp:first-child { border-top: 0; }
  .pp h2 {
    font-size: 1.5rem; letter-spacing: -0.022em; line-height: 1.2;
    margin-bottom: 18px;
  }

  /* `--nav-h` is set nowhere, so this fell back to 58px against a nav
     that is ~74px tall — the app icon sat underneath it. Measured, not
     guessed, with room to breathe. */
  .pp-hero { padding-top: 104px; padding-bottom: 44px; }
  .pp-icon { width: 58px; height: 58px; border-radius: 14px; margin-bottom: 22px; }
  .pp-hero h1 { font-size: clamp(2.2rem, 11vw, 2.9rem); margin-bottom: 18px; }
  .pp-lede { font-size: 1.04rem; line-height: 1.62; color: var(--fg-dim); }
  .pp-cta { width: 100%; margin-top: 26px; }
  .pp-note { display: block; margin-top: 14px; line-height: 1.55; }
  .pp-body { font-size: 1rem; line-height: 1.68; color: var(--fg-dim); }

  /* The steps are cards now — see the PHONE block further down. The
     list version that lived here used `.pp-steps li`, which is more
     specific than `.pp-step` and quietly beat it: two numbers per
     row, one inside the card and one outside. Deleted rather than
     out-specified. */

  /* ── the comparison ── */
  .pp-vs { display: grid; gap: 14px; margin-top: 22px; }
  .pp-col {
    padding: 20px; border: 1px solid var(--line); border-radius: 14px;
    background: var(--ink-2);
  }
  .pp-col--good { border-color: var(--line-2); }
  .pp-col-k { display: block; margin-bottom: 14px; }
  .pp-col:not(.pp-col--good) .pp-col-k { color: var(--fg-mute); }
  .pp-col ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 11px; }
  .pp-col li {
    display: grid; grid-template-columns: 18px 1fr; gap: 10px;
    font-size: 0.94rem; line-height: 1.55; color: var(--fg-dim);
  }
  .pp-col li::before {
    content: '✗'; font-family: var(--mono); color: var(--fg-mute); line-height: 1.5;
  }
  .pp-col--good li::before { content: '✓'; color: var(--ok); }
  .pp-fine { display: block; margin-top: 18px; line-height: 1.6; }

  /* ── spec rows ── */
  .pp-spec { list-style: none; margin: 22px 0 0; padding: 0; }
  .pp-spec li {
    display: flex; justify-content: space-between; gap: 16px;
    padding: 12px 0; border-bottom: 1px solid var(--line);
    font-size: 0.93rem; color: var(--fg-dim);
  }
  .pp-spec li span:last-child {
    font-family: var(--mono); font-size: 0.76rem; text-align: right; color: var(--fg);
  }

  /* ── price ── */
  .pp-amount { display: flex; align-items: baseline; gap: 8px; margin-bottom: 20px; }
  .pp-amount b { font-size: 2.5rem; font-weight: 800; letter-spacing: -0.03em; }
  .pp-amount span { color: var(--fg-mute); font-size: 0.95rem; }
  .pp-plan { list-style: none; margin: 0; padding: 0; display: grid; gap: 11px; }
  .pp-plan li {
    display: grid; grid-template-columns: 18px 1fr; gap: 10px;
    font-size: 0.95rem; line-height: 1.55; color: var(--fg-dim);
  }
  .pp-plan li::before {
    content: '✓'; font-family: var(--mono); color: var(--blue); line-height: 1.5;
  }

  /* The FAQ and the waitlist are shared with the desktop page, and both
     are `data-reveal` — which starts at opacity 0 and is lit by the
     bundle. The bundle is not here, so light them from CSS. */
  [data-reveal] { opacity: 1 !important; transform: none !important; }
}

/* The light theme's sky, calmed down for a phone.
   The full version opens on #6E97FF with two saturated radial bodies in
   the top corners — on a wide screen the copy sits to one side of all
   that, but on a phone the headline and the `--fg-dim` paragraph sit
   directly on top of it and lose contrast. Same sky, less weather: the
   deep end of the ramp goes, the corner bodies go, the grid stays. */
@media (max-width: 900px), (prefers-reduced-motion: reduce) {
  html[data-theme="light"] body {
    background:
      repeating-linear-gradient(0deg,
        rgba(28,70,190,0.045) 0, rgba(28,70,190,0.045) 1px, transparent 1px, transparent 56px),
      repeating-linear-gradient(90deg,
        rgba(28,70,190,0.045) 0, rgba(28,70,190,0.045) 1px, transparent 1px, transparent 56px),
      radial-gradient(60% 26% at 50% 0%, rgba(120,170,255,0.30), transparent 70%),
      linear-gradient(176deg, #DCE9FF 0%, #E9F1FF 38%, #F2F7FF 100%);
    background-attachment: fixed;
  }
}

/* ═══════════ PHONE PAGE — the look ═══════════════════════════
   A page of nothing but paragraphs gives the eye nowhere to stop.
   What stops it here is the product itself: real screenshots of the
   app, in a device frame, on a real street in Selangor. Everything
   else is rhythm — a dark band of figures, two surfaced feature
   sections, a card around the price.
──────────────────────────────────────────────────────────────── */
@media (max-width: 900px), (prefers-reduced-motion: reduce) {
  /* ── the figures ──
     This was a near-black slab, on the theory that the scroll wanted a
     beat of contrast. On a page whose whole surface is one continuous
     sky, it read as a hole cut in it — and it was the only element on
     the phone page that belonged to no family.

     Two other routes were considered and rejected: brand-blue would
     have competed with the one blue that means "press me", and plain
     figures on the sky (the desktop hero's treatment) lose the sense
     of a distinct strip. This is the record card's material — paper,
     hairline, mono — so the page now has one card language instead of
     a card and a slab. */
  .pp-band { background: none; border-top: 0; padding: 8px 0 28px; }
  .pp-stats {
    list-style: none; margin: 0; padding: 0;
    display: grid; grid-template-columns: repeat(3, 1fr);
    border: 1px solid var(--line-2);
    border-radius: 16px;
    background: color-mix(in srgb, var(--paper) 82%, transparent);
    box-shadow: 0 20px 44px -30px rgba(16,26,48,0.4);
    overflow: hidden;
  }
  .pp-stats li {
    display: grid; gap: 5px; align-content: center;
    padding: 18px 8px;
    text-align: center;
  }
  .pp-stats li + li { border-left: 1px solid var(--line); }
  .pp-stats b {
    font-family: var(--mono); font-size: 1.08rem; font-weight: 700;
    color: var(--fg); letter-spacing: -0.01em;
  }
  /* One accent, on the figure that is the product's actual claim. */
  .pp-stats li:first-child b { color: var(--blue); }
  .pp-stats span {
    font-size: 0.6rem; line-height: 1.4; letter-spacing: 0.04em;
    color: var(--fg-mute);
  }

  /* ── surfaced feature sections ── */
  .pp-feature {
    background: color-mix(in srgb, var(--paper) 62%, transparent);
    border-top: 1px solid var(--line);
  }
  .pp-feature .tech-k { display: block; margin-bottom: 12px; }
  .pp-feature h2 { margin-bottom: 4px; }
  .pp-feature .pp-body { margin-top: 22px; }

  /* ── the price, as a card ── */
  .pp-price { padding-bottom: 46px; }
  .pp-card {
    padding: 26px 22px 28px;
    border: 1px solid var(--blue);
    border-radius: 20px;
    background: color-mix(in srgb, var(--paper) 78%, transparent);
    box-shadow: 0 20px 44px -22px rgba(46,134,255,0.5);
  }
  .pp-card .tech-k { display: block; margin-bottom: 14px; }
  .pp-card .pp-cta { margin-top: 24px; }

  /* a touch more air between the big blocks */
  .pp { padding: 46px 0; }
  /* the nav is fixed, so a jump must stop short of it */
  .pp, #faq, #waitlist { scroll-margin-top: 84px; }
  .pp h2 { font-size: 1.62rem; }
  /* ...and restate the hero's clearance, because the shorthand above
     resets padding-top and this block is the later one. */
  .pp-hero { padding-top: 104px; }
}

/* ═══════════ 08 / 09 ON DESKTOP ══════════════════════════════
   Both were flat `var(--ink)` panels — in the light theme that is a
   near-white slab, which after seven beats of sky reads as the page
   having ended and a different site having started. They are reading
   surfaces and still need to be legible over the fixed stage, so:
   translucent, not opaque, and blurred rather than painted.

   The waitlist was also a single column against a 1280px page, which
   left the right-hand half of the last screen empty. It now carries
   the record beside the form — the one image that says what you are
   joining a list for.
──────────────────────────────────────────────────────────────── */
@media (min-width: 901px) and (prefers-reduced-motion: no-preference) {
  .faq-sec, .waitlist {
    background: color-mix(in srgb, var(--ink) 72%, transparent);
    -webkit-backdrop-filter: blur(18px) saturate(130%);
    backdrop-filter: blur(18px) saturate(130%);
    border-top: 1px solid var(--line);
  }
  @supports not (backdrop-filter: blur(1px)) {
    .faq-sec, .waitlist { background: var(--ink); }
  }

  /* FAQ: the title holds the left, the questions take the width they
     need on the right, instead of one narrow column down one side. */
  .faq-in {
    max-width: none;
    display: grid; grid-template-columns: 0.85fr 1.15fr;
    gap: clamp(40px, 5vw, 84px); align-items: start;
  }
  .faq-head { position: sticky; top: 120px; }
  .faq { margin-top: 0; }

  /* Waitlist: one centred composition, not two things pinned to
     opposite edges.

     Spread across the full 1280px the form sat against the left margin
     and the card against the right, with a third of the screen of
     nothing between them — two objects that never became a layout. The
     block is now narrower than the page and centred, the two halves are
     close enough to read together, and both are larger for it. */
  .wl-in {
    max-width: 1020px; margin-inline: auto;
    display: grid; grid-template-columns: 1fr 0.88fr;
    gap: clamp(36px, 4vw, 60px); align-items: center;
  }
  .wl-copy { max-width: none; }
  .wl-copy h2 { font-size: clamp(2.4rem, 3.6vw, 3.4rem); }
  .wl-aside { justify-self: stretch; width: 100%; }

  /* bigger, now that they are not fighting for the width */
  .wl-field input, .wl-field select { font-size: 17px; min-height: 56px; }
  .wl-form > .btn { --pad: 17px 34px; font-size: 1rem; }
  .rec-rows > div { padding: 13px 0; }
  .rec-rows dd { font-size: 0.9rem; }

  /* the section was also twice as tall as its contents */
  .waitlist { padding-block: clamp(80px, 9vh, 120px); }
  .wl-aside-note { display: block; margin-top: 16px; line-height: 1.6; }
}
.wl-aside { display: none; }
@media (min-width: 901px) and (prefers-reduced-motion: no-preference) { .wl-aside { display: block; } }

/* ═══════════ PHONE — the mark, and the record card ═══════════
   Two pieces of design carry this page. The mark is the product's
   own object, alive on the first screen. The record card is the
   product's claim, drawn in the site's type rather than pasted in
   as a screenshot of a dark app — which reads as a foreign object
   on a light page and dates the moment the UI changes.
──────────────────────────────────────────────────────────────── */
.rec {
  border: 1px solid var(--line-2);
  border-radius: 18px;
  background: color-mix(in srgb, var(--paper) 82%, transparent);
  box-shadow: 0 24px 50px -30px rgba(16,26,48,0.45);
  overflow: hidden;
}
.rec-head {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--blue) 9%, transparent);
}
.rec-seal {
  font-family: var(--mono); font-size: 0.62rem; font-weight: 700;
  letter-spacing: 0.18em; color: #fff;
  background: linear-gradient(140deg, #4C95FF, #1C63D8);
  padding: 5px 10px; border-radius: 999px;
  box-shadow: 0 6px 16px -6px rgba(46,134,255,0.8);
}
.rec-id {
  font-size: 0.6rem; letter-spacing: 0.1em; color: var(--fg-mute);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rec-rows { margin: 0; padding: 4px 18px 6px; }
.rec-rows > div {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; padding: 11px 0;
  border-bottom: 1px solid var(--line);
}
.rec-rows > div:last-child { border-bottom: 0; }
.rec-rows dt {
  font-size: 0.82rem; color: var(--fg-mute); flex: none;
}
.rec-rows dd {
  margin: 0; text-align: right; font-size: 0.86rem; color: var(--fg);
  font-weight: 500;
}
.rec-rows dd.mono { font-size: 0.74rem; letter-spacing: 0.02em; }

@media (max-width: 900px), (prefers-reduced-motion: reduce) {
  /* ── the mark ──
     A block, not a pinned viewport. Height is fixed so the copy below
     never moves while the canvas sizes itself. */
  .pp-stage {
    position: relative;
    /* Was clamp(230px, 46vw, 300px). At 300px the mark pushed the
       headline most of the way down the first screen, so the answer to
       "what is this" arrived after a scroll. The mark still leads; it
       just no longer owns the screen. */
    height: clamp(170px, 36vw, 215px);
    margin-bottom: 4px;
    opacity: 0; transition: opacity 700ms var(--ease);
  }
  .pp-stage.is-ready { opacity: 1; }
  .pp-canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
  /* No WebGL: the block collapses rather than leaving a hole. */
  .pp-stage.is-nogl { display: none; }

  .pp-hero { padding-top: 88px; }
  .pp-hero h1 { margin-bottom: 18px; }

  /* ── steps as cards ──
     The numbered list was four paragraphs with a number beside them.
     As cards each step is a thing you can land on. */
  .pp-steps { display: grid; gap: 12px; counter-reset: none; }
  .pp-step {
    display: grid; grid-template-columns: 1fr; gap: 6px;
    padding: 20px;
    border: 1px solid var(--line);
    border-radius: 16px;
    background: color-mix(in srgb, var(--paper) 66%, transparent);
  }
  .pp-step::before { content: none; }
  .pp-n {
    font-family: var(--mono); font-size: 0.68rem; font-weight: 700;
    letter-spacing: 0.16em; color: var(--blue);
  }
  .pp-step b { color: var(--fg); font-weight: 700; font-size: 1.02rem; }
  .pp-step .pp-t { color: var(--fg-dim); font-size: 0.95rem; line-height: 1.6; }

  .rec { margin-top: 24px; }
  .pp-feature .pp-body { margin-top: 22px; }

  /* ── five seats ── */
  .pp-seats {
    list-style: none; margin: 24px 0 0; padding: 0;
    display: flex; gap: 8px;
  }
  .pp-seats li {
    flex: 1; text-align: center;
    padding: 12px 0; border-radius: 12px;
    border: 1px dashed var(--line-2);
    font-family: var(--mono); font-size: 0.68rem; color: var(--fg-mute);
  }
  .pp-seats .is-owner {
    border-style: solid; border-color: var(--blue);
    background: color-mix(in srgb, var(--blue) 12%, transparent);
    color: var(--fg); font-weight: 700;
  }
}

/* The phone layout is also what a reduce-motion visitor gets, and they
   may be on a 27-inch screen. Cap the column so it stays a column. */
@media (min-width: 901px) {
  .phone-page { max-width: 760px; margin-inline: auto; }
  .phone-page .pp-stage { height: 280px; }
}

/* The way past the film, on the first screen. */
.hero-skip {
  display: inline-block; margin-top: 20px;
  color: var(--fg-mute);
  border-bottom: 1px solid var(--line-2);
  padding-bottom: 3px;
  transition: color 200ms var(--ease), border-color 200ms var(--ease);
}
.hero-skip:hover { color: var(--fg); border-color: var(--blue); }

/* ═══════════ THE STORES ═══════════════════════════════════════
   The app went live on 2026-09-06, which made "join the waitlist"
   the wrong ask and "not released yet" an untrue sentence. The
   primary action everywhere is now downloading it; the waitlist
   stays, but it belongs to GOTCHA-CAM, which really is unreleased.
──────────────────────────────────────────────────────────────── */
.stores { display: flex; flex-wrap: wrap; gap: 12px; }
.stores--stack { margin-top: 24px; }

@media (max-width: 900px), (prefers-reduced-motion: reduce) {
  /* Two full-width rows on a phone: side by side they shrink below a
     comfortable tap target and the labels start truncating. */
  .stores { flex-direction: column; gap: 10px; margin-top: 26px; }
  .stores .store-btn { width: 100%; justify-content: center; }
}

/* ═══════════ THE PIPELINE, ON LIGHT ═══════════════════════════
   Beat 03's rows were built for the dark theme and never re-checked
   against the light one, which is the default. Three things were
   working together to make them unreadable: the row's pending state
   sat at 26% opacity, the type used `--fg-mute` (44% ink), and a
   completed step's label used `--cyan` — a colour chosen to glow on
   near-black, which on near-white is barely a tint. The percentages
   multiply, so the visible result was around a tenth of full ink.
──────────────────────────────────────────────────────────────── */
html[data-theme="light"] .pipe-t,
html[data-theme="light"] .pipe-v { color: var(--fg-dim); }
html[data-theme="light"] .pipe-k { color: var(--fg); }
/* a teal that survives a white background, in place of the glow cyan */
html[data-theme="light"] .pipe-row.is-done .pipe-k { color: #0B6E86; }
html[data-theme="light"] .pipe-row.is-done::before { background: #0B6E86; }
html[data-theme="light"] .pipe-row.is-done .pipe-v { color: var(--fg); }
html[data-theme="light"] .pipe-row.is-live .pipe-k { color: #1C63D8; }
