/* ============================================================================
   Cinematic hero — a title card, the film, and the page.

   Three layers, two dissolves, one scrub:

     card    the quote, the archive plate, the lockup      dissolves off ↓
     film    the footage, scrubbed from scroll, with the
             query / answer / reveal overlays on it        dissolves off ↓
     page    the real hero, pinned underneath

   The only cuts in the sequence are those two dissolves. Everything between
   them is the film RUNNING — decelerating into each measured beat and settling
   there, then moving on. Holding a still at each beat instead was tried and is
   wrong: a beat is where the motion arrives, and with no travel between them
   there is nothing for a beat to be the end of.

   The film is one layer, not three. The lockup is part of the frame it resolved
   on, so it dissolves with it rather than peeling off it.

   Only tokens already defined in tokens.css are used here. Radius stays 0.
   ========================================================================== */


/* ---- the track -------------------------------------------------------------
   --cine-track is the whole scroll budget. Retuning it retunes the pace of the
   entire sequence and nothing else — every phase below is a fraction of it.

   --cine-handoff is the length of the FINAL dissolve, and it is not free to
   choose: the hero is pulled up by exactly this much so that it sits pinned
   under the film for that window. It must equal the tail the schedule in
   js/cinematic.js reserves (1 - FILM_END), which is why the script overwrites
   this property with the exact figure on load. The value here is that same
   arithmetic authored by hand, so a page that never reaches the script still
   lands within a few pixels.

   600vh when the film stopped seven times; 460vh now that it stops twice. The
   budget follows the number of rests, not the length of the footage — the
   footage plays at whatever rate the remaining scroll implies, and the rests
   are what actually consume distance. Cutting five of them and keeping 600vh
   would have bought nothing but a longer scroll. */
/* --cine-track is the ONE knob for the film's pace: it is the whole scroll
   budget, every phase below is a fraction of it, and nothing else needs
   retuning when it moves. Bigger = more scroll per frame = a slower film.

   460 -> 340 -> 500. The cut to 340 was made while the film still opened on 77
   frames of blank paper, so shortening the track was really only shortening
   the nothing. With the blank opening removed at source (START in
   js/cinematic.js) the film is 491 frames of continuous content rather than
   573 frames that were 13% empty — and at 340vh those 491 were passing at
   1.44 per vh, half again as fast as the original 1.25. That is why it read
   as rushed.

   500vh puts it at 0.98 frames per vh: slower than the film has ever run, on
   a track only marginally longer than the original 460vh, because none of it
   is now spent on an empty frame. */
.cine-page {
  --cine-track:   500vh;
  --cine-handoff: calc(var(--cine-track) * 0.10);
}

/* The track is in vh, so a tall tablet reaches the same pixel distance with a
   smaller multiple. */
@media (max-width: 1099.98px) {
  .cine-page { --cine-track: 440vh; }
}

.cine {
  position: relative;
  height: var(--cine-track);
  background: var(--paper);
}


/* ---- the stage -------------------------------------------------------------
   fixed, not sticky. A sticky stage releases one viewport before its track
   ends, which hands the last viewport of scroll to a stage that is sliding
   away — and the whole point of the ending is that the film is still square in
   frame while the page underneath is already in position. Fixed holds the
   frame until the moment the track runs out, which is precisely the moment the
   hero's own pin begins. */
.cine__stage {
  position: fixed;
  inset: 0;
  z-index: 2;
  overflow: hidden;
  /* No background. The stage is a window, not a surface — the film layer inside
     it is opaque edge to edge, so nothing shows through while it is running,
     and once it has dissolved there is deliberately nothing left to paint. A
     --paper background here is invisible for the whole sequence and then
     silently eats the ending: the film fades to zero and the stage keeps
     painting solid paper over the hero underneath. */

  /* The film covers the hero for its whole run, so the hero must not take
     clicks through it. Released before the stage is, below. */
  pointer-events: auto;
}

/* Released as soon as the ending is mostly through — by then the page beneath
   is what the visitor is looking at and reaching for, and the last of the
   lockup is a ghost. Waiting for the film to be fully gone makes the hero's
   buttons dead for the length of a dissolve. */
.cine__stage[data-cine-through] { pointer-events: none; }

/* Past the end of the track there is nothing left to show, and an invisible
   full-viewport layer would still hold the film in the a11y tree and keep a
   decoder and its compositing layers alive behind the page. */
.cine__stage[data-cine-done] { visibility: hidden; }

/* The masthead drops in as the lockup resolves and has to ride over the film
   while it does — the reveal frame is still dissolving underneath it. */
.cine-page .site-head {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  z-index: 10;
  transform: translate3d(0, calc((1 - var(--head-in, 1)) * -100%), 0);
  will-change: transform;
}


/* ---- the film --------------------------------------------------------------
   The footage and its three overlays, dissolving as one layer at the end.
   --film-o is written by the loop; the 3% swell is derived from it rather than
   written separately, because a layer that fades without growing reads as a
   light being turned down and one that grows as it goes reads as a dissolve.

   Opaque, so the hero underneath is covered for the whole run and the dissolve
   lands on something solid. */
.cine__film {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: grid;
  place-items: center;
  background: var(--paper);
  opacity: var(--film-o, 1);
  transform: scale(calc(1 + (1 - var(--film-o, 1)) * 0.03));
  will-change: opacity, transform;
}

/* The film surface. The script sizes the backing store to viewport x DPR and
   cover-fits each frame into it, so this only has to fill the stage. The paper
   background matters for the first few hundred milliseconds, before the first
   frame of the sequence has landed. */
.cine__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  background: var(--paper);
}

/* ---- overlay beats ---------------------------------------------------------
   Positioned once, then only opacity / transform / pointer-events are written
   per frame. No layout property is touched inside the rAF loop.

   All three occupy the SAME grid cell so they overlap on one centred spot and
   cross-fade in place. Without this they become three separate grid rows and
   stack down the viewport — the query field ends up pinned to the top edge and
   the lockup to the bottom. */
.cine__layer {
  grid-area: 1 / 1;
  position: relative;
  z-index: 1;
  justify-self: center;
  align-self: center;
  width: min(920px, 88vw);
  opacity: 0;
  pointer-events: none;
  will-change: opacity, transform;
}


/* ---- 01 TITLE CARD ---------------------------------------------------------
   Ink left, the archive plate right — the site's own asymmetric split. No
   divider is drawn: against the ink panel it would be invisible, and the
   black-to-paper edge already does that job.

   The still is frame 116, beat 05, the organised archive. The card shows the
   film's destination first, then dissolves onto the spark it starts from, so
   the sequence reads as a rewind: the promise, then earning it. */
.cine__card-frame {
  position: absolute;
  inset: 0;
  z-index: 2;                 /* above the film, which it dissolves off */
  background: var(--stage-bg);
  opacity: var(--card-o, 1);
  transform: scale(calc(1 + (1 - var(--card-o, 1)) * 0.03));
  will-change: opacity, transform;
}

/* Half and half. This was the site's 62.5/37.5 asymmetric split, which is the
   right proportion for an argument column beside a diagram — but the title card
   is not an argument, it is a poster, and a poster's two halves carry equal
   weight. The ink/paper edge is the composition here, so it goes down the
   middle. */
/* One column, not two. The card is a photograph with type on it now, so the
   copy occupies the frame and is held to the left by its own max-width — the
   picture's dark end — rather than by a grid track. */
.cine__card {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
}

.cine__card-copy {
  align-self: center;
  padding: var(--gutter);
  padding-right: clamp(20px, 2.6vw, 48px);
  /* Held to the picture's dark end. The grid track no longer does this — the
     card is one column now — so the measure does it, which is the better
     reason anyway: the quote is three short lines and wants a narrow column
     wherever it sits. */
  max-width: min(52%, 720px);
}

/* Between the hero and h2 voices — the same uppercase Archivo 800, but it can
   never be --fs-hero because three authored lines have to fit the copy column.

   4.4vw was tuned to a 62.5% column. At 50% the longest line ("YOU CANNOT
   TRACE") measured 646px against 590px of column and broke to four, orphaning
   TRACE on its own line. 3.9vw fits it at every width from the 860px breakpoint
   up, with ~35px to spare at 1440. Re-measure this if the split or the padding
   moves again — the three lines are authored with <br>, so the layout cannot
   absorb a size that does not fit. */
.cine__quote {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(32px, 3.9vw, 60px);
  line-height: .92;
  letter-spacing: var(--ls-h2-lg);
  text-transform: uppercase;
  color: var(--on-dark);
}

/* ---- the typewriter --------------------------------------------------------
   "rumour." types itself in — seven characters, seven steps.

   The reveal is clip-path and NOT an animated width, which is the usual recipe.
   Width from 0 needs a target the animation can reach, and `ch` is a lie in a
   proportional face like Archivo while `100%` resolves against the containing
   block rather than the text. clip-path insets a percentage of the element's
   OWN box, so it is exact at any size with no measurement and no JS. It also
   does not affect layout, so the line never reflows as the word arrives and the
   caret sits still.

   steps(7) is one step per character. They are equal-width slices rather than
   true glyph advances — in a proportional face a step can land mid-letter — but
   over seven characters at this size it reads as typing, and the alternative is
   measuring every glyph at every breakpoint. */
.cine__type {
  display: inline-block;
  clip-path: inset(0 100% 0 0);
  animation: cine-type 1.05s steps(7, end) .55s forwards;
}
@keyframes cine-type { to { clip-path: inset(0 0 0 0); } }

/* Only after the word has landed, which is why it is a sibling: inside the span
   it would be clipped along with the text, and clip-path cannot follow the
   reveal edge. Paper, not red — the card is already at the system's three-red
   budget with the quote rule and the still's own two marks. */
.cine__type-caret {
  display: inline-block;
  width: .06em;
  height: .74em;
  margin-left: .04em;
  background: var(--on-dark);
  vertical-align: baseline;
  opacity: 0;
  animation: cine-type-blink 1.06s steps(1, end) 1.62s infinite;
}
@keyframes cine-type-blink { 0%, 49% { opacity: 1 } 50%, 100% { opacity: 0 } }

.cine__quote-rule {
  display: block;
  width: clamp(96px, 12vw, 168px);
  height: var(--bd-rail);
  background: var(--red);
  margin-top: clamp(20px, 2.6vw, 34px);
}

/* The mark, in the frame's own top-left corner and at poster scale — roughly
   twice the chrome size it carries in the masthead and the footer. It is the
   same lockup file as all three of those, in the paper-on-ink rendition the
   panel needs.

   Absolute, so it is anchored to the corner rather than riding the copy
   column's vertical centring, and above the split so it is never clipped by
   the still. */
.cine__card-mark {
  position: absolute;
  top: clamp(26px, 3.2vw, 52px);
  left: var(--gutter);
  z-index: 2;
}
.cine__card-mark img {
  display: block;
  height: clamp(44px, 5.2vw, 82px);
  width: auto;
}

.cine__card-plate {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: var(--stage-bg);
}
.cine__card-plate picture,
.cine__card-plate img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* One flat fill — the system has no gradients. 60% measured against the
   BRIGHTEST pixels the quote can land on (the document fan intrudes into the
   left band), which puts paper text at 5.3:1 there and far higher over the
   navy where it actually sits. */
.cine__card-plate::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(13, 13, 13, .60);
}

/* Portrait: the quote spans the whole frame rather than sitting on the
   picture's dark end, so the floor has to hold across the whole picture. */
@media (max-aspect-ratio: 1/1) {
  .cine__card-plate::after { background: rgba(13, 13, 13, .70); }
}

/* The copy and the mark ride above the plate. */
.cine__card-copy { position: relative; z-index: 1; }

/* The card's own hint, bottom-left on the ink panel. The stage hint below sits
   under the card in z and only appears once the card has gone. */
.cine__card-hint {
  position: absolute;
  left: var(--gutter);
  bottom: 28px;
  z-index: 1;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--fs-caption);
  letter-spacing: var(--ls-caption);
  text-transform: uppercase;
  color: var(--on-dark-soft);
  pointer-events: none;
}


/* ---- 04 QUERY — the real field, in the system's own square input voice -----
   Sized from --bar-w / --bar-h, which js/cinematic.js computes from the film's
   cover fit: this panel has to COVER the bar the footage draws at frame 413,
   not sit near it. The 12px is bleed, 6px a side, so coverage survives rounding
   and the frame's antialiased edge without needing exact registration.

   Specificity beats the .cine__layer width rules, including the phone override
   below — the footage's own geometry wins over any figure authored in vw.

   The fallbacks are the pre-registration values, for the fraction of a second
   before the script measures. */
.cine__film .cine__layer[data-cine-beat="query"] {
  width: calc(var(--bar-w, min(920px, 88vw)) + 12px);
}

/* ---- 05 ANSWER — the cited sentence, printed ON the source page ------------
   Registered the same way and for the same reason as the query bar above: the
   panel is opaque and must COVER the page the footage draws at frame 500, not
   float over it at an unrelated size. Untracked it took the layer's authored
   920px against the footage's ~443px of document, so the panel was more than
   twice the width of the page it was printed on.

   The 12px is bleed, 6px a side. The translate is not optional — the source
   page sits left of frame centre in both crops, so a correctly sized panel
   still lands wrong without it.

   Floors the width so the sentence stays readable if the cover fit ever makes
   the document very small; below 900px it is released entirely (see below),
   where the layer's own width is the better answer. */
.cine__film .cine__layer[data-cine-beat="answer"] {
  width: calc(var(--doc-w, min(920px, 88vw)) + 12px);
  min-width: min(420px, 92vw);
  transform: translateX(var(--doc-dx, 0px));
}

/* --rule-mid at 20% ink was right when the footage's own hard black bar was
   showing through around this one and doing the structural work. Now that the
   panel covers it, that job is the panel's, and a 20% grey outline on paper is
   not a field on a title card — it is a placeholder. Full ink, matching the
   weight the frame had before. */
.cine__query {
  display: flex;
  align-items: center;
  min-height: calc(var(--bar-h, 60px) + 12px);
  border: 2px solid var(--ink);
  background: var(--paper);
}
.cine__query-text {
  flex: 1;
  font-family: var(--font-body, inherit);
  font-size: clamp(15px, 1.5vw, 20px);
  line-height: 1;
  color: var(--ink);
  padding: 18px 20px;
  white-space: nowrap;
  overflow: hidden;
  /* The panel is sized by the footage's bar, not by this line — if a very
     narrow screen leaves the text short of room, it trims politely. */
  text-overflow: ellipsis;
}
/* The caret is the one moving thing in an otherwise still frame. */
.cine__caret {
  display: inline-block;
  width: 2px;
  height: 1em;
  margin-left: 2px;
  background: var(--ink);
  vertical-align: -0.12em;
  animation: cine-caret 1.06s steps(1, end) infinite;
}
@keyframes cine-caret { 0%, 50% { opacity: 1 } 50.01%, 100% { opacity: 0 } }

/* Square, derived from the bar's own height rather than fixed at 54px — the bar
   is now as tall as the footage's, and a fixed width made it a portrait
   rectangle. Square is also what every other block on this site is.

   The width is stated, NOT aspect-ratio: 1. On a flex item that is both
   `flex: none` and stretched, aspect-ratio resolves against the free width
   first and then stretches the row to match it, so the button sized itself off
   the 780px bar and turned into a 780px square that swallowed the frame.
   Repeating the min-height expression is the same number by construction. */
.cine__query-go {
  flex: none;
  align-self: stretch;
  width: calc(var(--bar-h, 60px) + 12px);
  background: var(--red);
}


/* ---- 05 ANSWER — a marked source passage and the citation pointing at it --- */
.cine__answer {
  background: var(--paper);
  border: 2px solid var(--rule-mid);
  padding: clamp(20px, 3vw, 34px);
}
.cine__passage {
  font-family: var(--font-body, inherit);
  font-size: clamp(15px, 1.6vw, 21px);
  line-height: 1.5;
  color: var(--ink);
  margin: 0;
}
/* Flat solid red laid over the words — the system's marked-passage voice, not
   a soft translucent highlighter. */
.cine__mark {
  background: var(--red);
  color: var(--paper);
  padding: 0 .12em;
}
/* Matches .cite in components.css: a solid red square with the numeral in
   Archivo 800, never a superscript link. */
.cine__cite {
  display: inline-grid;
  place-items: center;
  width: 22px;
  height: 22px;
  margin-left: .35em;
  background: var(--red);
  color: var(--paper);
  font-family: var(--font-display, inherit);
  font-weight: 800;
  font-size: 12px;
  line-height: 1;
  vertical-align: -.25em;
}
.cine__source {
  margin: 14px 0 0;
  font-family: var(--font-body, inherit);
  font-size: 13px;
  letter-spacing: .02em;
  color: var(--ink-faint);
}


/* ---- 06 REVEAL — the lockup resolving out of the signal -------------------- */
.cine__reveal { text-align: center; }

.cine__lockup img {
  display: block;
  margin: 0 auto;
  height: clamp(52px, 6.8vw, 104px);
  width: auto;
}

/* The bar measures the lockup — it draws to exactly the mark's width as the
   reveal beat lands. display:block is load-bearing: this is an <i>, and inline
   boxes ignore width, height and auto margins, so the bar was rendering 0x0 and
   the reveal was landing without its rule. */
.cine__rule {
  display: block;
  height: 6px;
  background: var(--red);
  margin: clamp(16px, 2vw, 26px) auto 0;
  width: 100%;
  transform: scaleX(var(--cine-reveal, 0));
  transform-origin: 50% 50%;
}


/* ---- scroll affordance -----------------------------------------------------
   Lives inside the film, so it dissolves with it and cannot outlive the frame
   it belongs to. The title card carries its own hint on the ink panel; --hint
   holds this one at zero until the card has gone, so the page never asks
   twice, and takes it out again as the reveal lands. */
.cine__hint {
  position: absolute;
  left: 50%;
  bottom: 28px;
  z-index: 2;
  transform: translateX(-50%);
  font-family: var(--font-body, inherit);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  opacity: var(--hint, 1);
  pointer-events: none;
}


/* ---- the handoff -----------------------------------------------------------
   The hero is pulled up into the tail of the track and pinned there for the
   length of the final dissolve, so it is stationary and fully composed while
   the reveal frame fades off it. The ::after gives back exactly what the
   negative margin took, so nothing below this moves.

   That spacer is a real in-flow block and NOT padding-bottom, which is the
   whole trick. A sticky box is constrained to its containing block, and the
   containing block is the parent's CONTENT box — padding is outside it. With
   padding-bottom the wrapper measured 405px taller while the content box still
   ended at the hero's own bottom edge, so the pin had exactly zero travel and
   the hero scrolled straight past under the film. A block child extends the
   content box, and the travel is real.

   z-index below the stage: the film covers it until the film is gone. */
.cine-page .cine-handoff {
  position: relative;
  z-index: 1;
  margin-top: calc(var(--cine-handoff) * -1);
}
.cine-page .cine-handoff::after {
  content: '';
  display: block;
  height: var(--cine-handoff);
}
.cine-page .cine-handoff > .section {
  position: sticky;
  top: 0;
  background: var(--paper);
}

/* The paper fill above is what makes each sticky section opaque against the
   one behind it, so it cannot simply be dropped — but it is a THREE-class
   selector, and it was silently outranking every inverted section's own
   background. Any section that declares itself dark keeps its ground. */
.cine-page .cine-handoff > .section.hero--dark,
.cine-page .cine-handoff > .section.section--dark {
  background: var(--stage-bg);
}

/* The masthead is fixed on this page and the hero is now pinned at top: 0, so
   the hero's own padding is the only thing holding the eyebrow out from under
   it — and the frame the film dissolves onto is exactly that pinned position,
   so anything hidden there is hidden at the one moment the page is introducing
   itself. --head-h is measured by js/cinematic.js rather than assumed: below
   860px the nav wraps to two lines and the masthead grows from 96px to 140px,
   which is 68px more than the hero's own 72px of padding. */
.cine-page .cine-handoff > .section--hero {
  --hero-pad-top: calc(var(--head-h, 96px) + var(--s-3));
}

/* Same measurement, same reason: base.css clears the masthead with a flat
   108px, which is short by a wrapped nav's worth on a phone. */
.cine-page [id] { scroll-margin-top: calc(var(--head-h, 96px) + 12px); }


/* ---- reduced motion --------------------------------------------------------
   Collapse the track to a single viewport and hold the title card. Nobody
   should have to scroll a six-screen track to reach the page — and the card is
   the better resting state than a held frame of the film, because it is the
   one screen that carries the quote, the plate and the wordmark at once.

   The whole film goes down with display:none rather than opacity 0, so the
   query field and the lockup cannot be reached by keyboard behind the card,
   and the video is never fetched at all. */
.cine--static { height: 100vh; }
/* height is load-bearing on every one of these three overrides. The live stage
   is sized by inset:0 against the viewport, which does nothing once it is
   position:relative — and since the film and the card inside it are both
   absolute, the stage collapses to zero and the fallback renders a blank
   screen. */
.cine--static .cine__stage { position: relative; height: 100vh; }
.cine--static .cine__film { display: none; }
.cine--static .cine__card-frame { opacity: 1; transform: none; }
.cine-page .cine--static + .cine-handoff,
html:not(.js) .cine-handoff { margin-top: 0; }
.cine-page .cine--static + .cine-handoff::after,
html:not(.js) .cine-handoff::after { display: none; }
.cine--static ~ .cine-handoff > .section { position: static; }

@media (prefers-reduced-motion: reduce) {
  .cine { height: 100vh; }
  .cine__stage { position: relative; height: 100vh; }
  .cine__caret { animation: none; }
  .cine__film,
  .cine__card-frame { transform: none; }

  /* The punchline still has to be readable, so the word is simply there. */
  .cine__type { clip-path: none; animation: none; }
  .cine__type-caret { display: none; }
}

/* Without JS nothing scrubs and nothing dissolves, so a full-height track would
   be a black card the visitor has to scroll six screens past. Collapse it to
   one viewport and let the card stand as a static opener. */
html:not(.js) .cine { height: 100vh; }
html:not(.js) .cine__stage { position: relative; height: 100vh; }
html:not(.js) .cine__film { display: none; }
html:not(.js) .cine-handoff > .section { position: static; }


@media (max-width: 899.98px) {

  /* The split does not survive a phone in ANY arrangement: side by side the
     halves are slivers, stacked it reads as two fragments — half a screen of
     plate over half a screen of quote (user call: remove the image, carry
     less on mobile). So the card gives up the still entirely: one ink panel,
     the mark in its corner, the quote alone at centre. The film underneath
     opens on the same archive anyway, so the image is deferred, not lost. */
  .cine__card { grid-template-columns: 1fr; grid-template-rows: 1fr; }
  /* The plate is the card's ground at every width, so nothing is dropped —
     but the 52% cap goes. That figure holds the quote on the landscape
     frame's dark left end; the portrait crop has no such end, and 52% of a
     440px phone is a 229px column, which is four or five characters to the
     line at hero size. The quote takes the frame and the scrim carries it. */
  .cine__card-copy  {
    grid-row: 1;
    padding-right: var(--gutter);
    max-width: none;
  }
  /* The three lines are authored with <br>, so the size MUST fit the longest
     one ("YOU CANNOT TRACE" — measured ~10.4em in Archivo 800 at this
     tracking) inside 100vw minus both 24px gutters. That bounds the size at
     (100vw − 48px) / 10.4 ≈ 8.1vw across phone widths; 8vw keeps a hair of
     slack so no width between 280 and 860 ever wraps a fourth line. The old
     32px floor overflowed below ~385px and orphaned TRACE. */
  .cine__quote      { font-size: clamp(22px, 8vw, 52px); }

  /* With the still gone the whole frame is ink, so the mark keeps the
     frame's own top-left corner — the base rule already puts it there. */
}

/* A phone's viewport height makes every vh a much longer gesture, and a flick
   covers more of the track than a wheel notch does, so the same three scrolls
   get less of it. The handoff follows automatically — it is a fraction of the
   track, not a figure of its own.

   400vh was still too much. On an 844px phone that is 3376px, and a thumb
   flick moves ~450-600px, so reaching the page took seven or eight gestures —
   long enough that the film stopped reading as an intro and started reading
   as something stuck. 260vh is ~2200px: four to five flicks, which is the
   count the film was actually authored for.

   The schedule is proportional, so nothing needs retuning with it — the rests
   and the three scrolls keep their share of a shorter track. What changes is
   only how much thumb it costs to get through them. */
@media (max-width: 599.98px) {
  .cine-page { --cine-track: 380vh; }
  .cine__layer { width: 92vw; }        /* query is exempt — it tracks --bar-w */
  .cine__query-text { padding: 14px 15px; }

  /* Both hints clear the nav pill. Below 1000px the pill pins to the bottom
     of the viewport, and it is now present during the film rather than
     arriving with the masthead at the handoff — so a hint sitting at 20-28px
     lands underneath it. Lifted above the pill's height plus its stand-off
     and the safe-area inset. */
  .cine__card-hint,
  .cine__hint { bottom: calc(94px + env(safe-area-inset-bottom, 0px)); }
}

/* Below ~380px the footage's bar is too narrow for the question at any
   readable size, so the panel stops tracking --bar-w and takes the layer
   width instead. Registration only ever demanded COVER, and both boxes are
   centred on the same spot — the crisp DOM bar simply reads a little wider
   than the one the film drew. One size down on the face seals it. */
@media (max-width: 399.98px) {
  .cine__film .cine__layer[data-cine-beat="query"] { width: 92vw; }
  .cine__query-text { font-size: 13px; padding: 12px 12px; }
}

/* On a phone the 9:16 reframe puts the source page at 47% of a 720px frame —
   about 200px of document once cover-fitted, which no sentence reads at. The
   panel gives up registration and takes the layer width, exactly as the query
   bar does below 400px: coverage was always the requirement, and a panel that
   reads a little wider than the page behind it is the lesser fault. */
@media (max-width: 899.98px) {
  .cine__film .cine__layer[data-cine-beat="answer"] {
    width: 92vw;
    min-width: 0;
    transform: none;
  }
}


/* ---- the page rises into the hand-off --------------------------------------
   --page-in is written by js/cinematic.js across the closing dissolve. The
   hero lifts the last 28px into place as the film clears, so the two moves are
   one gesture instead of a cut between them.

   The fallback is 1 everywhere it matters: no JS, reduced motion, and every
   page that is not the film's, so the hero is never left displaced. */
.cine-page .section--hero .hero__inner {
  transform: translate3d(0, calc((1 - var(--page-in, 1)) * 28px), 0);
  opacity: calc(.35 + var(--page-in, 1) * .65);
  will-change: transform, opacity;
}

/* The photograph behind it comes up on the same ramp but travels less — a
   background that moves as far as the type reads as a slide, not a settle. */
.cine-page .section--hero .hero__plate {
  transform: scale(calc(1 + (1 - var(--page-in, 1)) * .04));
  will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
  .cine-page .section--hero .hero__inner,
  .cine-page .section--hero .hero__plate {
    transform: none;
    opacity: 1;
  }
}
