/* AD ENGINEER — page shell.
   Governed by DESIGN.md: one paper world, Archivo + IBM Plex Mono, signal red
   as the only accent, zero gradients, square corners, two shadows.
   Values live in tokens.css. Nothing here redefines a token. */

/* ---- the scrollbar ------------------------------------------------------
   Part of the brand, not chrome to leave to the OS: paper track, ink thumb,
   square (--r-0, DESIGN.md §5), red on grab. scrollbar-gutter keeps the space
   reserved so locking the page below cannot shift the layout sideways. */
html {
  scrollbar-gutter: stable;
  scrollbar-width: thin;                       /* Firefox + standard */
  scrollbar-color: var(--ink) var(--paper-2);
}
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track {
  background: var(--paper-2);
  border-left: var(--hairline) solid var(--line);
}
::-webkit-scrollbar-thumb {
  background: var(--ink);
  border-radius: var(--r-0);
  border: 3px solid var(--paper-2);            /* inset, so it reads as a bar on paper */
}
::-webkit-scrollbar-thumb:hover,
::-webkit-scrollbar-thumb:active { background: var(--red); }
::-webkit-scrollbar-corner { background: var(--paper-2); }

/* the opening film holds the page still until it has played (see main.js) */
html.locked, html.locked body { overflow: hidden; }

/* ---- nav: transparent over the film, a paper band after it ------------
   The wordmark is not here — the giant hero logo shrinks into this corner
   and becomes it (see .hero-logo). */
.nav {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  display: flex; justify-content: flex-end; align-items: center;
  padding: var(--s-4) var(--gutter);
}
.nav .links { display: flex; gap: var(--s-6); align-items: center; }
.nav .links a {
  text-decoration: none; color: rgba(244, 241, 234, .85);
  font-size: var(--text-label); font-weight: 600;
  text-transform: uppercase; letter-spacing: var(--track-label);
}
.nav .links a:hover { color: var(--red); }
.nav.scrolled { background: var(--paper); border-bottom: var(--hairline) solid var(--line); }
.nav.scrolled .links a { color: var(--ink-60); }
.nav.scrolled .links a:hover { color: var(--red); }

/* ---- the film ---------------------------------------------------------
   Two stacked videos: the intro plays once, the loop runs forever underneath
   it. The loop file joins itself — tail cross-dissolved into head — so the
   plain loop attribute never lands on a jump, and never plays anything
   backwards. */
.film { position: relative; height: 100vh; min-height: 560px; overflow: hidden; }
.film-stage { position: absolute; inset: 0; overflow: hidden; }
.film video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center;
}
.film video[data-loop] { opacity: 0; }
.film.resting video[data-loop] { opacity: 1; }
.film.resting video[data-intro] { opacity: 0; }
/* 0.9s, not 0.5s: measured PSNR between the intro's last frame and the loop's
   first is 19dB — same scene, shoe a little higher. A longer dissolve absorbs
   it; the shot is near-static so nothing is lost to the fade. */
.film video[data-intro] { transition: opacity .9s linear; }

/* ---- copy over footage sits on NOTHING (DESIGN.md §4) -----------------
   No plate, no card, no scrim, no blur. The words are set straight onto the
   frame in paper-cream. If a spot stops being quiet, the fix is the footage,
   not a background. */

/* the giant logo — the hero headline that shrinks into the header corner as
   you scroll (JS scrubs transform only; layout never changes) */
.hero-logo-wrap { margin: 0; }
.hero-logo {
  position: fixed; top: 14px; left: var(--gutter); z-index: 101;
  color: var(--paper); text-decoration: none;
  font-size: clamp(3rem, 9vw, 9.5rem); font-weight: 800; line-height: .95;
  letter-spacing: .01em; text-transform: uppercase; white-space: nowrap;
  transform-origin: left top;
  opacity: 0; transition: opacity .6s var(--ease-out), color .3s var(--ease-out);
}
/* once docked on the paper nav band, cream would vanish — flip to ink */
.hero-logo.docked { color: var(--ink); }
.hero-logo-text { display: block; }
.film.resting .hero-logo { opacity: 1; }
.film.resting .hero-logo .hero-logo-text { animation: riseIn .8s var(--ease-out) .1s both; }

.hero { position: absolute; inset: 0; z-index: 2; pointer-events: none; color: var(--paper); }
.hero a, .hero button { pointer-events: auto; }

.hero-tag { position: absolute; left: var(--gutter); top: calc(14px + 14vh - 2.6rem); color: var(--paper); }
.hero-right {
  position: absolute; right: var(--gutter); top: 44%;
  max-width: 34ch; font-size: clamp(1.05rem, 1.55vw, 1.5rem); line-height: 1.5;
}
.hero-foot {
  position: absolute; left: var(--gutter); bottom: 9vh;
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-3);
  max-width: 30ch;
}
.hero-foot .label { color: var(--red); }
.hero-foot .micro { color: rgba(244, 241, 234, .62); }
.hero-foot .dotted { border: 0; border-top: 1px dotted rgba(244, 241, 234, .5);
  width: 100%; margin: 0; }
.play { position: absolute; right: var(--gutter); bottom: 6vh; }
/* centered without transform — fadeUp animates transform and would undo it */
.scrollcue {
  position: absolute; left: 0; right: 0; bottom: var(--s-5);
  margin-inline: auto; width: max-content;
  color: rgba(244, 241, 234, .62); text-decoration: none;
}
.scrollcue:hover { color: var(--paper); }
/* buttons on the film invert: paper fill, ink label */
.film .btn { background: var(--paper); color: var(--ink); }
.film .btn:hover { background: var(--sheet); }

/* copy stays out of the way until the film comes to rest */
.hero[data-reveal] > * { opacity: 0; }
.hero[data-reveal] { opacity: 0; transition: opacity .6s var(--ease-out); }
.film.resting .hero[data-reveal] { opacity: 1; }
.film.resting .hero[data-reveal] .hero-tag   { animation: fadeUp .6s var(--ease-out) .3s both; }
.film.resting .hero[data-reveal] .hero-right { animation: fadeUp .7s var(--ease-out) .45s both; }
.film.resting .hero[data-reveal] .hero-foot  { animation: fadeUp .7s var(--ease-out) .6s both; }
.film.resting .hero[data-reveal] .play       { animation: fadeUp .6s var(--ease-out) .75s both; }
.film.resting .hero[data-reveal] .scrollcue  { animation: fadeUp .6s var(--ease-out) .9s both; }
@keyframes stampIn {
  0%   { opacity: 0; transform: scale(2) rotate(-10deg); }
  55%  { opacity: 1; transform: scale(.94) rotate(-1deg); }
  100% { opacity: 1; transform: none; }
}
@keyframes riseIn {
  from { opacity: 0; transform: translateY(30px); clip-path: inset(0 0 100% 0); }
  to   { opacity: 1; transform: none; clip-path: inset(0 0 -12% 0); }
}
@keyframes fadeUp {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}

/* ---- generic section furniture ---------------------------------------- */
.sec { padding: 16vh var(--gutter); border-top: var(--hairline) solid var(--line); }
.inner { max-width: 1080px; margin: 0 auto; display: flex; flex-direction: column; gap: var(--s-5); }
.sec .display { font-size: clamp(2rem, 3.6vw, 3.1rem); line-height: 1.04; }
.lede { max-width: 48ch; color: var(--ink-60); }

.steps { list-style: none; margin: var(--s-5) 0 0; padding: var(--s-6) 0 0;
  border-top: var(--hairline) solid var(--line-strong);
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-6); }
.steps li { display: flex; flex-direction: column; gap: var(--s-2); }
.steps .micro { color: var(--red); }
.steps h3 { margin: 0; font-size: 1.05rem; font-weight: 640; letter-spacing: -.01em; }
.steps p { color: var(--ink-60); font-size: var(--text-small); }

/* ---- dark showcase sections --------------------------------------------
   Flat --dark, the tear world as a stage (DESIGN.md §1). No image, no
   gradient, no vignette — any light or atmosphere must arrive inside the
   footage placed on it. Copy sits directly on the field in cream (§4). */
.sec-photo { position: relative; min-height: 100vh; display: grid; place-items: center;
  padding: 16vh var(--gutter); overflow: hidden; }
.stage-sec, .work-sec { background: var(--dark); }

/* ---- the analysis: pinned film + an HTML grid that grows out of it ------
   Tall section, sticky stage. The film is full-bleed cover so its own dark
   field IS the background — the HTML tiles sit straight on it. */
/* The height is ENTRANCE TRAVEL and nothing else. Chapters advance on
   GESTURES, not on scroll distance, so every viewport here would be dead
   scrolling — it was 320vh once, i.e. 220vh of nothing, then exactly 100vh.
   The 80vh above one viewport is what the window opens across, and main.js
   reads it back off this box rather than restating the number. */
.analysis { position: relative; height: 180vh; background: var(--paper); }
/* ---- the entrance: a film window that opens on the paper page ----------
   The section arrives as a small centred frame and opens to full bleed as you
   scroll — the section appears in the middle of the screen, expands, and then
   takes the whole space. It is a clip, not a scrim: DESIGN.md §3 bans painted
   fades, and inset() is a shape, so the paper around the window is the real
   page rather than something drawn over the film.
   `--open` is written per scroll by main.js, already eased — see openStage().
   It defaults to 1 so a page whose JS never ran shows the section OPEN rather
   than as a permanent slit. `--shut` is the complement, written once here
   because all three of the properties below count DOWN from open.
   The closed frame is --frame-y/--frame-x in from each edge: 40vh x 44vw, a
   16:9 frame at desktop ratios, so the window that opens is the same shape as
   the film inside it.
   The corners round while it opens and reach square at rest — DESIGN.md §5
   allows exactly that, and only that. Square corners at the 624px start size
   read as a hole cut in the paper; a radius makes the same rectangle read as a
   pane of glass being drawn back. It MUST land on 0: a full-bleed stage still
   carrying 14px would round the corners of the screen itself.

   `--rise` is what keeps the frame STILL on the way in. This box is sticky, so
   before it catches it rides the page at 1:1 and then stops dead the instant it
   pins — the bottom edge went from -1px per px of scroll to 0 in one frame,
   which was the edgiest moment in the section and the reason it did not read as
   premium. main.js writes the box's own distance from the top of the screen
   (clamped to the frame inset) and it is subtracted from the top inset and
   added to the bottom, which holds the visible window at the CENTRE OF THE
   VIEWPORT while the box slides underneath it. So the frame never travels: it
   materialises in place, dead centre, and then opens. Zero at rest, where these
   collapse back to the symmetric insets.
   The inset fractions are kept as bare numbers because main.js needs the same
   number to know how far out to start the hold, and reads it back off this
   element — a copy over there would drift the first time this is tuned. */
.analysis-stage {
  position: sticky; top: 0; height: 100vh; overflow: hidden;
  --open: 1;
  --shut: calc(1 - var(--open));
  --frame-vh: 30;
  --frame-y: calc(var(--frame-vh) * 1vh);
  --frame-x: 28vw;
  --rise: 0px;
  /* how present the frame is, 0..1: main.js fades it in across the hold, so it
     appears in place instead of a hard-edged rectangle sliding up past the
     bottom of the screen. Its own opacity, not a wash painted on the page —
     §3 bans the gradient, and .beat has ridden opacity since the first build.
     1 by default, same reason as --open. */
  --near: 1;
  opacity: var(--near);
  clip-path: inset(calc(var(--shut) * var(--frame-y) - var(--rise))
                   calc(var(--shut) * var(--frame-x))
                   calc(var(--shut) * var(--frame-y) + var(--rise))
                   calc(var(--shut) * var(--frame-x))
                   round calc(var(--shut) * var(--r-reveal)));
}
.analysis-stage > video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center;
  /* the frame widens and the camera pulls back at the same time, so the film
     opens rather than a box merely growing. Identity at --open: 1, and it rides
     the same eased scalar, so it decelerates into place with the frame. */
  transform: scale(calc(1 + var(--shut) * .14));
}
/* the reverse pass sits on top and only shows while rewinding. No fade — the
   frames match exactly, so a cut is invisible and a fade would be the only
   thing you would notice. */
.analysis-stage > video[data-analysis-rewind] { opacity: 0; z-index: 2; }
.analysis-stage > video[data-analysis-rewind].on { opacity: 1; }

/* ---- the ads panel -----------------------------------------------------
   A full 100vh sheet that slides in from the right as clip 4's particles
   disperse. It is flat --dark, the colour the film ends on, so the handover
   has no visible edge. One transform, so it runs on the compositor. */
.adspanel {
  position: absolute; inset: 0; z-index: 5; background: var(--dark);
  display: flex; align-items: center;
  transform: translateX(100%);
  transition: transform .95s var(--ease-inout);
  will-change: transform;
}
.analysis.landed .adspanel { transform: none; }

/* the row inside it: scrolls horizontally, card nearest the middle opens up.
   Slots are sized from the film's OWN panel (--slot-w/h, measured in JS), so
   the open card is exactly the frame the film was holding. */
.rail {
  width: 100%; display: flex; gap: 20px; align-items: center;
  padding-inline: var(--rail-pad, 25%);
  overflow-x: auto;
  scrollbar-width: none; overscroll-behavior-x: contain;
  scroll-behavior: smooth;
  /* No scroll-snap: every move is driven to an exact centred position, so snap
     had nothing to add and plenty to fight — it pulled the row a second time
     after ours landed, which is the "shifts left, then slides to centre" jerk.
     overflow-anchor off for the same reason: the focused card changes size, and
     anchoring would silently correct scrollLeft underneath us. */
  overflow-anchor: none;
}
.rail::-webkit-scrollbar { display: none; }
.rail figure {
  margin: 0; overflow: hidden; position: relative;
  flex: 0 0 var(--mini-w, 190px); height: var(--mini-h, 290px);
  /* Same duration as the scroll glide, same curve — the card growing and the
     row travelling are one motion, so neither finishes ahead of the other. */
  transition: flex-basis .55s var(--ease-out), height .55s var(--ease-out);
  box-shadow: 0 18px 44px rgba(0, 0, 0, .55);
}
.rail img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* the card nearest the centre opens to the film-panel size; the rest stay
   small either side and run off both edges, as in the reference */
.rail figure.focus {
  flex-basis: var(--slot-w, 420px); height: var(--slot-h, 640px);
  outline: var(--hairline) dashed rgba(244, 241, 234, .45); outline-offset: 10px;
}
.rail figure.focus::after {
  content: ''; position: absolute; top: 12px; right: 12px;
  width: 7px; height: 7px; border-radius: var(--r-pill); background: var(--paper);
}

.landed-copy {
  position: absolute; z-index: 6; left: var(--gutter); bottom: 8vh;
  color: var(--paper); opacity: 0; pointer-events: none;
  transition: opacity .6s var(--ease-out) .6s;
}
.landed-copy .label { color: var(--red); display: block; margin-bottom: var(--s-2); }
.landed-copy .micro { color: rgba(244, 241, 234, .6); }
.analysis.landed .landed-copy { opacity: 1; }

/* chapter rail, same language as the old build: ink marks on the right */
.analysis .chapters {
  position: absolute; z-index: 5; right: var(--gutter); top: 50%;
  transform: translateY(-50%);
  display: flex; flex-direction: column; gap: var(--s-3);
  background: none; border: 0; box-shadow: none; padding: 0;
}
.analysis .chapters button {
  background: none; border: 0; padding: var(--s-1) 0 var(--s-1) var(--s-4);
  cursor: pointer; position: relative; opacity: .4;
  transition: opacity .35s var(--ease-out);
}
.analysis .chapters .micro { color: var(--paper); }
.analysis .chapters button::before {
  content: ''; position: absolute; left: 0; top: 50%; width: 10px; height: 1px;
  background: var(--paper); transition: width .35s var(--ease-out), background .35s;
}
.analysis .chapters button:hover { opacity: .8; }
.analysis .chapters button.on { opacity: 1; }
.analysis .chapters button.on::before { width: 22px; background: var(--red); }
.analysis .chapters button.on .micro { color: var(--red); }

/* ---- beats: poster copy on the film's clock ---------------------------
   The layout the reference uses: a small label and a large display line on
   the LEFT, a reading paragraph on the RIGHT, the product living in the gap
   between them. Only opacity animates on .beat, so the children keep their
   own centring transforms. */
/* Opacity is written per frame from the film's clock (see main.js), so there
   is deliberately NO transition here — a CSS tween would fight the scrub and
   lag the rewind. The curve lives in the JS instead. */
/* above the ads panel (z-5): on the last chapter the headline stays readable
   top-left while the carousel fills the middle, as in the reference */
.beat {
  position: absolute; inset: 0; z-index: 6; pointer-events: none;
  color: var(--paper); opacity: 0;
}
.beat .label { color: var(--red); display: block; margin-bottom: var(--s-4); }

.beat-l {
  position: absolute; left: var(--gutter); top: 50%; transform: translateY(-50%);
  max-width: min(38vw, 30rem);
}
/* DESIGN.md §6: the film beats are posters and run one step above a section
   headline — 4.4rem against 3.1rem. Deliberate, and written down. */
.beat-h {
  margin: 0; font-weight: 800; text-transform: uppercase;
  font-size: clamp(2.2rem, 4.6vw, 4.4rem); line-height: .96;
  letter-spacing: -.02em;
}
/* ---- the cinematic arrival (chapters 3–4) ------------------------------
   One keyframe track, three phases: the headline flies in from the right at
   full size, blooms as it lands, then shrinks back onto its resting spot.
   transform-origin sits at the left so "shrink" resolves toward the top-left
   corner rather than toward the middle of the line.
   The rest position IS the normal layout — the animation only ever scales and
   translates away from it — so when it finishes there is nothing to undo.
   DESIGN.md §3: the bloom is permitted only while landing, and must end at
   text-shadow: none. */
.beat.cine .beat-l { top: 17%; bottom: auto; transform: none; max-width: min(52vw, 44rem); }
/* PAUSED on purpose. The keyframes describe the choreography; the film decides
   where in it we are. main.js scrubs this with a negative animation-delay off
   the clip's own progress, so the headline flies in WHILE the soap is being
   analysed and lands on the frame the clip lands on — and runs backwards on
   its own during a rewind. Linear timing: the easing lives in the scrub. */
.beat.cine .beat-h {
  transform-origin: left center;
  white-space: nowrap;
  animation: cineArrive 2.3s linear both paused;
}
.beat.cine .beat-h span { opacity: 1; animation: none; }   /* not typed */
.beat.cine .beat-h::after { display: none; }               /* no caret either */
/* the paragraph is scrubbed too, on the tail of the same clip */
.beat.cine .beat-r { animation: fadeUp 2.3s linear both paused; }

/* The clip's own shape, in four moves:
   in-flight → landed → the analysis blooms → settles into the corner.

   THE BLOOM MUST BE OVER BEFORE THE SHRINK STARTS. Not a taste call — MEASURED.
   .9em on 66px text held at scale(2.7) is a ~161 device-px blur over a
   1567x343 box, and main.js re-scrubs animation-delay every frame, so every
   frame that the scale ALSO changes is a fresh raster of that blur. With the
   bloom live across the 2.7 → 1 shrink (it used to run 60→100%) chapters 3–4
   cost 5–6 longtasks summing 400–530ms and 5–6 dropped video frames per
   transition at CPU 4x — that is the "scroll lags on chapter 3" the visitor
   feels. Rising and falling while the word is HELD at 2.7, back to none by
   72%, is 0 longtasks and 0 gaps>50ms: same glow, same held size, same peak,
   and the shrink still lands on the clip's own last frame. The blur is not the
   problem; the blur plus a changing transform is. (probe-lag10, .verify/jank.mjs) */
@keyframes cineArrive {
  0%   { opacity: 0; transform: translateX(46vw) scale(2.7); text-shadow: none; }
  14%  { opacity: 1; }
  45%  { transform: translateX(0) scale(2.7); text-shadow: none; }
  56%  { text-shadow: 0 0 .22em rgba(255,246,230,.9), 0 0 .9em rgba(255,224,178,.55); }
  72%  { transform: translateX(0) scale(2.7); text-shadow: none; }
  100% { transform: translateX(0) scale(1); text-shadow: none; }
}
@media (prefers-reduced-motion: reduce) {
  .beat.cine .beat-h,
  .beat.cine .beat-r { animation: none; opacity: 1; }
}

/* typing: one span per character, staggered off --c. No timers — the browser
   owns the whole thing, and it restarts by re-adding .type. */
.beat-h span { opacity: 0; }
.beat.type .beat-h span {
  animation: keyIn .26s var(--ease-out) both;
  animation-delay: calc(var(--c) * 26ms);
}
@keyframes keyIn {
  from { opacity: 0; transform: translateY(.14em); }
  to   { opacity: 1; transform: none; }
}
/* a caret that runs with the text and stops when it lands */
.beat.type .beat-h::after {
  content: ''; display: inline-block;
  width: .06em; height: .82em; margin-left: .06em;
  background: var(--red); vertical-align: text-bottom;
  animation: caret .5s steps(1) 6;
}
@keyframes caret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .beat-h span { opacity: 1; }
  .beat.type .beat-h span { animation: none; }
  .beat.type .beat-h::after { display: none; }
}
.beat-r {
  /* clears the chapter rail on the right. Measured, not guessed: the rail is
     30px of marks and numbers sitting at the gutter, and at 1440 the paragraph
     ended at exactly the rail's right edge — "light behaves on it" was printed
     under "02". Keep this in step with .chapters if that rail ever widens. */
  position: absolute; right: calc(var(--gutter) + 64px); top: 50%;
  transform: translateY(-50%);
  max-width: 26ch; font-size: clamp(1rem, 1.5vw, 1.4rem); line-height: 1.5;
  color: rgba(244, 241, 234, .8);
}
.beat-note { margin: var(--s-4) 0 0; max-width: 30ch; font-size: var(--text-small);
  color: rgba(244, 241, 234, .7); }

/* the last beat shares the frame with the grid, so its copy drops low-left
   and the paragraph becomes a footnote in the opposite corner */
.beat--wide .beat-l { top: auto; bottom: 11vh; transform: none; }
/* the footnote lives under the copy, not opposite it: on the last beat the
   right-hand side belongs to the grid, and a corner note landed on the tiles */
.footnote { margin-top: var(--s-5); width: 15rem; }
.footnote .micro { color: rgba(244, 241, 234, .55); }
.dotted-fine { border: 0; border-top: 1px dotted rgba(244, 241, 234, .4);
  margin: 0 0 var(--s-2); }
/* the page-wide film grain (tokens.css) sits at 4% and disappears on a dark
   field — the reference's paper grain is clearly visible. Same noise tile,
   stronger dose, under the content so type stays crisp. */
.sec-photo::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: .09;
}
.sec-photo > * { position: relative; z-index: 1; }

.stage-slot { display: flex; flex-direction: column; align-items: center; gap: var(--s-4);
  text-align: center; color: var(--paper); }
.stage-slot .label { color: var(--red); }
.stage-slot .display { font-size: clamp(2rem, 3.6vw, 3.1rem); }
.photo-copy { color: rgba(244, 241, 234, .66); max-width: 44ch; font-size: var(--text-body); }

/* the specimen frame from the reference: dashed hairline, dot marker */
.panel { margin: 0; position: relative;
  width: min(340px, 74vw); padding: 10px;
  border: var(--hairline) dashed rgba(244, 241, 234, .45); }
.panel::after { content: ''; position: absolute; top: 14px; right: 14px;
  width: 8px; height: 8px; border-radius: var(--r-pill); background: var(--paper); }
.panel video { width: 100%; aspect-ratio: 9 / 16; object-fit: cover; display: block; }
.panel .micro { display: block; margin-top: var(--s-3); text-align: center;
  color: rgba(244, 241, 234, .55); }

/* ---- the ask ----------------------------------------------------------- */
.ask { text-align: center; align-items: center; }
.ask .inner { align-items: center; }
.ask .lede { text-align: center; }
.ask .btn { margin-top: var(--s-3); }

footer {
  display: flex; justify-content: space-between; gap: var(--s-5);
  padding: var(--s-7) var(--gutter);
  border-top: var(--hairline) solid var(--line);
}
.foot-col { display: flex; flex-direction: column; gap: var(--s-1); }
.foot-col.right { text-align: right; }
.foot-col a { text-decoration: none; }
.foot-col a:hover { color: var(--red); }

a:focus-visible, .btn:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }

/* ---- phones -------------------------------------------------------------
   A 16:9 film inside a 9:16 window keeps only 26% of the frame. Measured on a
   375px phone: the spec shot lost the whole shoe and every callout, and the
   direction board showed one and a half cards out of nine. Cropping destroys
   the footage, so on phones the film becomes a contained panel at its native
   ratio and the copy drops below it onto paper — where it is ink again, and
   needs nothing behind it. */
@media (max-width: 820px) {
  .nav { position: static; padding: var(--s-3) var(--gutter);
    background: var(--paper); border-bottom: var(--hairline) solid var(--line); }
  .nav .links { gap: var(--s-5); }
  .nav .links a { color: var(--ink-60); }

  .film { height: auto; min-height: 0; overflow: visible; }
  .film-stage { position: relative; inset: auto; width: 100%; aspect-ratio: 16 / 9; }
  .film video { object-position: center; }

  /* the shrink is a desktop pattern: on phones the logo is a static heading
     and the hero copy stacks below the panel as ink on paper */
  .hero-logo { position: static; transform: none !important; color: var(--ink);
    font-size: clamp(2.4rem, 11.5vw, 3.6rem); white-space: normal; }
  .hero-logo-wrap { padding: var(--s-6) var(--gutter) 0; }
  .hero { position: static; pointer-events: auto; color: var(--ink);
    display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-4);
    padding: 0 var(--gutter) var(--s-8);
    border-bottom: var(--hairline) solid var(--line); }
  .hero-tag, .hero-right, .hero-foot, .play { position: static; }
  .hero-tag { color: var(--red); }
  .hero-right { color: var(--ink-60); font-size: var(--text-body); max-width: none; }
  .hero-foot { max-width: none; }
  .hero-foot .micro { color: var(--ink-60); }
  .hero-foot .dotted { border-top-color: rgba(22, 20, 15, .35); }
  .film .btn { background: var(--ink); color: var(--paper); }
  .scrollcue { display: none; }

  .sec { padding: 11vh var(--gutter); }
  .sec-photo { min-height: 0; padding: 14vh var(--gutter); }

  /* phones: no pinning and no cropping. The film shows whole at 16:9, the
     grid becomes an ordinary two-column block beneath it. */
  /* ...and no entrance either — there is no sticky travel to scrub it across,
     and `clip-path` on an auto-height stage would crop the cards' shadows at
     its edges. The dark field comes back because the stacked beats are cream
     copy sitting on the section itself rather than on the film. */
  .analysis { height: auto; background: var(--dark); }
  .analysis-stage { position: static; height: auto; padding-bottom: var(--s-8);
    clip-path: none; }
  .analysis-stage > video { position: static; width: 100%; height: auto;
    aspect-ratio: 16 / 9; transform: none; }
  .rail { position: static !important; width: auto !important; height: auto !important;
    padding: var(--s-6) var(--gutter) 0; }
  /* phones: no chapter pinning (it fights native scroll) and no slide-in —
     the film plays through and the ads are simply a row you swipe. */
  .analysis .chapters { display: none; }
  .adspanel { position: static; transform: none !important; background: none;
    display: block; }
  .rail figure { flex: 0 0 46vw; height: auto; aspect-ratio: 9 / 16; }
  .rail figure.focus { flex-basis: 46vw; height: auto; outline: 0; }
  .rail figure.focus::after { display: none; }
  .landed-copy { position: static; opacity: 1; padding: var(--s-5) var(--gutter) 0; }

  /* Beats stack under the film as a numbered list rather than cross-fading:
     there are no chapters on a phone, so the film-clock opacity never runs —
     it would leave all four at 0. */
  .beat { position: static; inset: auto; opacity: 1 !important; display: block;
    padding: var(--s-6) var(--gutter) 0; }
  .beat-l, .beat-r, .footnote { position: static; transform: none; inset: auto;
    max-width: none; width: auto; text-align: left; }
  .beat-h { font-size: clamp(1.9rem, 8.5vw, 2.6rem); }
  .beat-r { margin-top: var(--s-4); font-size: var(--text-body); }
  .footnote { margin-top: var(--s-5); }
  .steps { margin-top: var(--s-4); padding-top: var(--s-5); gap: var(--s-6); }
  .steps { grid-template-columns: 1fr; }
  .ask .lede { text-align: left; }
  .ask, .ask .inner { text-align: left; align-items: flex-start; }
  footer { flex-direction: column; gap: var(--s-4); }
  .foot-col.right { text-align: left; }
}

/* narrow phones: at 330px the wordmark and the links met with zero gap. The
   site has one call to action anyway — keep that, drop the rest to scrolling. */
@media (max-width: 430px) {
  .nav .links a:not(.nav-cta) { display: none; }
  .nav .links a.nav-cta { color: var(--red); }
}

/* notched phones: keep the band and the footer clear of the cutouts */
@supports (padding: max(0px)) {
  .nav    { padding-left: max(var(--gutter), env(safe-area-inset-left));
            padding-right: max(var(--gutter), env(safe-area-inset-right)); }
  footer  { padding-left: max(var(--gutter), env(safe-area-inset-left));
            padding-right: max(var(--gutter), env(safe-area-inset-right));
            padding-bottom: max(var(--s-7), env(safe-area-inset-bottom)); }
}

/* ---- reduced motion: no film, no shrink — everything static, ink on paper */
@media (prefers-reduced-motion: reduce) {
  .film video { display: none; }
  .film { background: var(--paper-2); }
  .hero[data-reveal], .hero[data-reveal] > * { opacity: 1; animation: none; }
  .hero, .hero-tag, .hero-right { color: var(--ink); }
  .hero-right, .hero-foot .micro { color: var(--ink-60); }
  .hero-logo { position: absolute; top: 12vh; opacity: 1; color: var(--ink); }
  .hero-logo .hero-logo-text { animation: none; }
  .film .btn { background: var(--ink); color: var(--paper); }
  /* the ads panel is pinned open by main.js here — it must simply BE there.
     Left transitionable it slid in over 0.95s on load, which is exactly the
     motion this query exists to remove. */
  .adspanel, .landed-copy, .rail figure { transition: none; }
  /* No entrance, so the 80vh it was scrubbed across is now dead scrolling —
     give the height back. clip-path is belt-and-braces: main.js already pins
     --open at 1, but this query must hold on its own if the JS never runs. */
  .analysis { height: 100vh; background: var(--dark); }
  .analysis-stage { clip-path: none; }
  .analysis-stage > video { transform: none; }
}
