/* ==========================================================================
   cinema-system.css — Apex Holding · Section 9, the design-system elevation
   PURPOSE: the DOM-side craft layer that sits between the finished static site
   and the WebGL cinema layer. Everything the visitor touches — theme, gradient,
   surface, type, button, nav, chapter indicator, footer — earns the same level
   of finish the 3D layer already has.

   LOAD ORDER: LAST. After tokens.css, base.css, components.css, chrome.css,
   motion.css, the world theme, and cinema.css. It is injected by
   assets/js/cinema/chrome-cinema.js (which is a no-op if a <link> to this file
   is already present in the page head), so with JS off not one rule here exists
   and the base site is exactly what it is today.

   RULES THIS FILE OBEYS — the same contract cinema.css signed
     - ADDITIVE ONLY. No existing rule is edited, renamed or overridden away.
       Where an existing element is restyled (.btn, .site-header, .site-footer)
       the change is either a state this file invented (a data-attribute the JS
       adds) or a refinement that cannot remove information.
     - Specificity stays at one or two classes so components.css always wins a
       genuine conflict, and no !important appears anywhere.
     - Colour comes from the per-world --w-* semantic tokens ONLY. There is no
       parallel token set: this file adds --cine-* GRADIENT and MOTION
       composites, every one of which resolves back to a --w-* colour.
     - Every new interactive element is a real control with an accessible name,
       a visible focus ring, a >=44px target and correct ARIA. Nothing here is
       a div pretending to be a button.
     - prefers-reduced-motion is a complete alternate cut, at the bottom.

   CONTENTS
     1.  Composite tokens — gradients, noise, springs
     2.  Theme architecture — the 800ms world retune
     3.  Gradient system — hero wash · card sheen · text gradient
     4.  Surfaces — glass · film grain · self-drawing hairlines
     5.  Type — display tracking, optical size, drop caps, chapter numerals
     6.  Buttons reborn — ring pulse + pressed spring
     7.  The header capsule + scroll-progress ring
     8.  The chapter indicator (I · II · III)
     9.  The footer as rolling film credits
     10. prefers-reduced-motion — the alternate cut
     11. Narrow viewports

   STATE HOOKS written by assets/js/cinema/chrome-cinema.js
     html.cine-system            the layer installed
     html.cine-theme-shift       a world retune is in flight (800ms)
     .site-header[data-cine-condensed="true"]      past the hero
     .site-footer[data-cine-credits="armed"|"rolling"]
     [data-cine-rule].is-drawn   a hairline that has drawn itself
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. COMPOSITE TOKENS
   Namespaced --cine-* so tokens.css keeps sole ownership of its own names.
   Every value below resolves to a --w-* semantic colour, so all of it retunes
   for free the moment a world boundary is crossed.
   -------------------------------------------------------------------------- */

:root {
  /* Spring for one-shot presses: anticipation → action → settle. Damped just
     past 1 so the release reads as a physical return, never a bounce. */
  --cine-ease-spring: cubic-bezier(0.34, 1.32, 0.42, 1);

  /* The world retune. 800ms is the brief's figure and is deliberately longer
     than tokens.css --dur-world (640ms), which stays the switcher's own. */
  --cine-theme-dur: 800ms;

  /* Dither mask. A single fractal-noise tile, inlined as a data URI so it costs
     no request and can never 404 — there are no binary assets in this project.
     Applied at 2–4% over every gradient in section 3 to break the banding a
     wide, low-contrast wash on a dark ground always produces on 8-bit panels. */
  --cine-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");

  /* Glass. 16px is the brief's figure; the inner light border is a 1px inset
     highlight rather than a second border, so it never changes the box size. */
  --cine-glass-blur: 16px;
  --cine-glass-edge: inset 0 1px 0 color-mix(in srgb, var(--w-text) 16%, transparent);
}


/* --------------------------------------------------------------------------
   2. THEME ARCHITECTURE — the whole UI retunes on a world boundary

   There is no second token set. chrome-cinema.js reads the twelve --w-* values
   for the destination world straight out of tokens.css (by measuring a probe
   element carrying that [data-world]), writes them onto <html>, and adds
   .cine-theme-shift for the length of the tween. Because the tokens are
   registered @property colours that inherit from the root, EVERY consumer
   cross-fades together with zero per-component wiring:

     nav + header + capsule   --w-text / --w-text-dim / --w-line / --w-surface
     buttons + chips + cards  --w-accent / --w-accent-soft / --w-glow
     custom cursor            cinema.css already paints it in --w-accent
     ring motif               rings.js already strokes in --w-accent
     selection + scrollbar    base.css already themes both from --w-* tokens
     focus rings              base.css already draws them in --w-focus

   So this block is the ONLY thing section 9.1 needs to add: the timing.
   -------------------------------------------------------------------------- */

html.cine-theme-shift {
  transition:
    --w-base var(--cine-theme-dur) var(--ease-cinematic),
    --w-base-2 var(--cine-theme-dur) var(--ease-cinematic),
    --w-surface var(--cine-theme-dur) var(--ease-cinematic),
    --w-surface-2 var(--cine-theme-dur) var(--ease-cinematic),
    --w-line var(--cine-theme-dur) var(--ease-cinematic),
    --w-text var(--cine-theme-dur) var(--ease-cinematic),
    --w-text-dim var(--cine-theme-dur) var(--ease-cinematic),
    --w-accent var(--cine-theme-dur) var(--ease-cinematic),
    --w-accent-soft var(--cine-theme-dur) var(--ease-cinematic),
    --w-accent-2 var(--cine-theme-dur) var(--ease-cinematic),
    --w-glow var(--cine-theme-dur) var(--ease-cinematic),
    --w-focus var(--cine-theme-dur) var(--ease-cinematic),
    /* Belt and braces for engines without @property, exactly as tokens.css
       does it — and in the SAME declaration, because a second rule on the same
       selector would replace transition-property and kill the token tween. */
    background-color var(--cine-theme-dur) var(--ease-cinematic),
    color var(--cine-theme-dur) var(--ease-cinematic);
}

html.cine-theme-shift body {
  transition:
    background-color var(--cine-theme-dur) var(--ease-cinematic),
    color var(--cine-theme-dur) var(--ease-cinematic);
}


/* --------------------------------------------------------------------------
   3. GRADIENT SYSTEM — three families, one per job, never a default linear

   Every family is built from the world's own accent, glow and ground, is
   directional rather than corner-to-corner, and carries the noise dither on a
   separate layer so the banding a 60%-transparent wash produces is destroyed
   before it reaches the panel.

     .cine-wash      hero wash    — an off-centre horizon glow rising from the
                                    foot of a section, the way light sits in
                                    every one of these worlds
     .cine-sheen     card sheen   — a raking specular band across a surface
     .cine-text-*    text gradient— accent → lifted accent across display type
   -------------------------------------------------------------------------- */

/* --- 3.1 Hero wash ----------------------------------------------------- */

.cine-wash {
  position: relative;
  isolation: isolate;
}

.cine-wash::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* Two off-axis ellipses, not one linear ramp: the light has a source. */
  background-image:
    radial-gradient(
      120% 78% at 18% 108%,
      color-mix(in srgb, var(--w-accent) 26%, transparent) 0%,
      transparent 62%
    ),
    radial-gradient(
      90% 62% at 88% 4%,
      color-mix(in srgb, var(--w-accent-2) 18%, transparent) 0%,
      transparent 58%
    ),
    linear-gradient(
      to top,
      color-mix(in srgb, var(--w-base) 88%, transparent),
      transparent 54%
    );
}

/* The dither. Its own layer so it is never scaled or repositioned with the
   gradient beneath it, and so its blend mode cannot tint the ground. */
.cine-wash::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image: var(--cine-noise);
  background-size: 140px 140px;
  opacity: 0.04;
  mix-blend-mode: overlay;
}

/* --- 3.2 Card sheen ---------------------------------------------------- */

.cine-sheen {
  position: relative;
  isolation: isolate;
  background-image:
    linear-gradient(
      146deg,
      color-mix(in srgb, var(--w-accent) 9%, transparent) 0%,
      transparent 38%,
      transparent 62%,
      color-mix(in srgb, var(--w-accent-2) 7%, transparent) 100%
    ),
    var(--cine-noise);
  /* The noise tile is the LAST layer, so it sits beneath the ramp and needs no
     blend mode — it simply perturbs the ground the ramp is drawn over. */
  background-size: auto, 140px 140px;
  background-blend-mode: normal, soft-light;
}

/* The raking band. Travels once on engagement, then rests. */
.cine-sheen::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  background-image: linear-gradient(
    104deg,
    transparent 34%,
    color-mix(in srgb, var(--w-accent-soft) 14%, transparent) 50%,
    transparent 66%
  );
  opacity: 0;
  transform: translateX(-14%);
  transition:
    opacity var(--cine-dur-ui) var(--ease-silk),
    transform var(--cine-dur-scene) var(--ease-cinematic);
}

.cine-sheen:hover::before,
.cine-sheen:focus-within::before {
  opacity: 1;
  transform: translateX(14%);
}

/* --- 3.3 Text gradient -------------------------------------------------- */

.cine-text-gradient {
  /* The fallback IS the accent, so if background-clip is unsupported or the
     paint is dropped the headline is still a legible, AA-contrast colour. */
  color: var(--w-accent);
}

@supports (-webkit-background-clip: text) or (background-clip: text) {
  .cine-text-gradient {
    background-image: linear-gradient(
      168deg,
      var(--w-accent-soft) 0%,
      var(--w-accent) 46%,
      color-mix(in srgb, var(--w-accent) 82%, var(--w-text)) 100%
    );
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    /* Both ends of the ramp are the accent family, so the darkest stop still
       clears AA against every world ground at display size. */
  }
}


/* --------------------------------------------------------------------------
   4. SURFACES — glass, grain, self-drawing hairlines
   -------------------------------------------------------------------------- */

/* --- 4.1 Restrained glass ----------------------------------------------- */

.cine-glass {
  border: 1px solid var(--w-line);
  background-color: color-mix(in srgb, var(--w-surface) 62%, transparent);
  box-shadow: var(--shadow-2), var(--cine-glass-edge);
}

/* Blur only where the engine can actually composite it. Without support the
   surface stays a legible tinted panel rather than a washed-out one. */
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .cine-glass {
    background-color: color-mix(in srgb, var(--w-surface) 48%, transparent);
    -webkit-backdrop-filter: blur(var(--cine-glass-blur)) saturate(1.18);
    backdrop-filter: blur(var(--cine-glass-blur)) saturate(1.18);
  }
}

/* --- 4.2 Global film grain ---------------------------------------------
   One fixed layer at 2.6%, above the DOM story so it unifies text, surfaces
   and the WebGL canvas into a single photographed image, and below the
   letterbox (150), the header (200) and every dialog. Never interactive,
   never announced, and `contain: strict` keeps it off every layout path. */

.cine-grain {
  position: fixed;
  inset: 0;
  z-index: 140;
  pointer-events: none;
  contain: strict;
  background-image: var(--cine-noise);
  background-size: 140px 140px;
  opacity: 0.026;
  mix-blend-mode: overlay;
}

/* Eight discrete positions, not a smooth drift: real film grain jumps per
   frame. steps() keeps it on one compositor layer with no interpolation. */
html.cine-system .cine-grain {
  animation: cine-grain-shift 720ms steps(1, end) infinite;
}

@keyframes cine-grain-shift {
  0%   { background-position:   0px   0px; }
  12%  { background-position: -18px   9px; }
  25%  { background-position:  11px -22px; }
  37%  { background-position: -27px -13px; }
  50%  { background-position:  23px  17px; }
  62%  { background-position:  -9px  26px; }
  75%  { background-position:  19px  -7px; }
  87%  { background-position: -14px -25px; }
  100% { background-position:   0px   0px; }
}

/* --- 4.3 Hairlines that draw themselves ---------------------------------
   [data-cine-rule] is an opt-in decorative element. It is 1px tall, has no
   text content, and carries no information a sighted reader loses if the
   observer never fires — so the resting state can safely be scaleX(0).
   chrome-cinema.js adds .is-drawn on entry and has a failsafe that draws
   every rule unconditionally after 3s. */

[data-cine-rule] {
  display: block;
  block-size: 1px;
  inline-size: 100%;
  background-image: linear-gradient(
    to right,
    color-mix(in srgb, var(--w-accent) 55%, transparent),
    var(--w-line) 62%,
    transparent
  );
  transform: scaleX(0);
  transform-origin: 0% 50%;
  transition: transform var(--cine-dur-scene) var(--ease-cinematic);
}

[data-cine-rule].is-drawn {
  transform: scaleX(1);
}

/* No script at all: the rule is simply present and drawn. */
html:not(.cine-system) [data-cine-rule] {
  transform: scaleX(1);
}


/* --------------------------------------------------------------------------
   5. TYPE
   The brand faces and the fluid scale in tokens.css are untouched — this adds
   three opt-in refinements and nothing else. Restraint is the whole point: a
   drop cap and an oversized numeral are earned by a world's story, not applied
   because they are available.
   -------------------------------------------------------------------------- */

/* Optical size + tightened tracking for display type. font-optical-sizing only
   does anything on a variable face; the local() stacks in tokens.css may or may
   not resolve to one, so it degrades to exactly nothing. */
.cine-display {
  font-family: var(--font-display);
  letter-spacing: -0.028em;
  line-height: var(--lh-tight);
  font-optical-sizing: auto;
  text-wrap: balance;
}

@supports (font-variation-settings: "opsz" 48) {
  .cine-display {
    /* Larger optical size = finer hairlines and tighter fit, which is what a
       didone wants at headline scale. Ignored by static faces. */
    font-variation-settings: "opsz" 48;
  }
}

/* Drop cap. Chapter openers only. Never on a list, never on short copy. */
.cine-dropcap::first-letter {
  float: inline-start;
  margin-inline-end: var(--space-3);
  margin-block-start: 0.06em;
  font-family: var(--font-display);
  font-size: 3.4em;
  line-height: 0.82;
  color: var(--w-accent);
  /* Optical alignment: a didone cap sits proud of the text block's left edge. */
  margin-inline-start: -0.04em;
}

/* Oversized chapter numeral. Decorative in the visual sense but NOT hidden —
   it is a real heading label, so it stays in the accessible name of whatever
   it labels. Use with a numeral, not a word. */
.cine-numeral {
  display: block;
  font-family: var(--font-display);
  font-size: var(--step-6);
  line-height: 0.86;
  letter-spacing: var(--track-tight);
  color: color-mix(in srgb, var(--w-accent) 42%, transparent);
  font-variant-numeric: lining-nums;
}


/* --------------------------------------------------------------------------
   6. BUTTONS REBORN
   components.css owns the box, the three skins and the drawing rule. This adds
   exactly two things: a ring that pulses out of the edge on engagement, and a
   press that springs rather than snaps. The drawing rule (::after) is left
   completely alone; the pulse uses the free ::before pseudo-element.
   -------------------------------------------------------------------------- */

html.cine-system .btn::before {
  content: "";
  position: absolute;
  inset: -1px;
  border: 1px solid var(--w-accent);
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
}

html.cine-system .btn:hover::before,
html.cine-system .btn:focus-visible::before {
  animation: cine-ring-pulse 1500ms var(--ease-silk) infinite;
}

/* One ring at a time, expanding and dissolving — the site's ring motif in its
   smallest form. Transform + opacity only, so it never leaves the compositor. */
@keyframes cine-ring-pulse {
  0%   { opacity: 0.55; transform: scale(1); }
  62%  { opacity: 0;    transform: scale(1.11); }
  100% { opacity: 0;    transform: scale(1.11); }
}

/* The spring. Redeclared in full — same five properties components.css
   animates, same durations — so nothing is dropped, only the transform curve
   changes and only while the button is NOT held down. */
html.cine-system .btn:not(:active) {
  transition:
    transform 340ms var(--cine-ease-spring),
    background-color var(--dur-micro) var(--ease-out-soft),
    border-color var(--dur-micro) var(--ease-out-soft),
    color var(--dur-micro) var(--ease-out-soft),
    box-shadow var(--dur-micro) var(--ease-out-soft);
}

html.cine-system .btn:active {
  transform: translateY(0) scale(0.972);
  transition-duration: 90ms;
}

html.cine-system .icon-btn:active {
  transform: scale(0.93);
  transition-duration: 90ms;
}

html.cine-system .icon-btn:not(:active) {
  transition:
    transform 300ms var(--cine-ease-spring),
    background-color var(--dur-micro) var(--ease-out-soft),
    border-color var(--dur-micro) var(--ease-out-soft),
    color var(--dur-micro) var(--ease-out-soft);
}


/* --------------------------------------------------------------------------
   7. THE HEADER CAPSULE
   Past the hero, the full-width bar condenses into a floating glass capsule
   carrying the scroll-progress ring. chrome-cinema.js only ever writes
   data-cine-condensed; every pixel of the change is here.

   Deliberately scoped to >=52em. Below that, chrome.css gives the header a
   second row so the nav keeps its 44px targets, and a centred capsule would
   have to take that space back. The mobile header is already correct.
   -------------------------------------------------------------------------- */

/* The ring lives in the header at all times so condensing costs no layout —
   only opacity and scale change. It is decorative: the information it carries
   (how far down the page you are) is already carried by the scrollbar, so it
   is aria-hidden and never announced. */
.cine-capsule-ring {
  /* Reuse the ring machinery cinema.css section 11 already ships, and feed it
     the --scroll-progress chrome.js is ALREADY writing on <html>. No second
     scroll listener, no duplicated maths, no per-frame JS at all. */
  --cine-ring-progress: var(--scroll-progress, 0);

  display: inline-grid;
  place-items: center;
  flex: none;
  inline-size: 1.75rem;
  block-size: 1.75rem;
  opacity: 0;
  transform: scale(0.7);
  transition:
    opacity var(--cine-dur-ui) var(--ease-silk),
    transform var(--cine-dur-ui) var(--ease-silk);
}

.cine-capsule-ring > svg {
  grid-area: 1 / 1;
}

/* Scroll position must be immediate. cinema.css gives .cine-ring circle a
   560ms ease for the loader; here that would read as lag. */
.cine-capsule-ring .cine-ring circle {
  transition: none;
}

.cine-capsule-ring .cine-ring {
  opacity: 0.85;
}

/* The decorative motif ringMarkup() supplies strokes in currentColor. Held at
   hairline weight so the progress arc stays the thing the eye reads. */
.cine-capsule-ring .cinema-ring-motif {
  color: var(--w-line);
  fill: none;
}

@media (min-width: 52em) {
  .site-header[data-cine-condensed="true"] {
    inset-block-start: var(--space-4);
    inset-inline-start: 50%;
    inset-inline-end: auto;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    inline-size: max-content;
    max-inline-size: calc(100vw - var(--gutter) * 2);
    padding-inline: var(--space-4);
    border: 1px solid var(--w-line);
    border-radius: var(--radius-pill);
    background-color: color-mix(in srgb, var(--w-surface) 48%, transparent);
    box-shadow: var(--shadow-2), var(--cine-glass-edge);
    transform: translateX(-50%);
    transition:
      background-color var(--cine-dur-ui) var(--ease-silk),
      border-color var(--cine-dur-ui) var(--ease-silk),
      border-radius var(--cine-dur-ui) var(--ease-silk),
      padding var(--cine-dur-ui) var(--ease-silk),
      inset-block-start var(--cine-dur-ui) var(--ease-cinematic);
  }

  @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .site-header[data-cine-condensed="true"] {
      -webkit-backdrop-filter: blur(var(--cine-glass-blur)) saturate(1.18);
      backdrop-filter: blur(var(--cine-glass-blur)) saturate(1.18);
    }
  }

  .site-header[data-cine-condensed="true"] .cine-capsule-ring {
    opacity: 1;
    transform: scale(1);
  }

  /* The hairline rail hands its job to the ring rather than stretching itself
     around a pill. Nothing is lost — the same number, drawn better. */
  .site-header[data-cine-condensed="true"] .scroll-progress {
    opacity: 0;
    transition: opacity var(--cine-dur-ui) var(--ease-silk);
  }

  /* The mark keeps a comfortable optical gap inside the pill. */
  .site-header[data-cine-condensed="true"] .site-header__mark {
    padding-inline-end: var(--space-1);
  }
}


/* --------------------------------------------------------------------------
   8. THE CHAPTER INDICATOR — I · II · III
   A real <nav aria-label="Chapters"> containing an ordered list of real links
   to the three story worlds. It is navigation and is marked up as navigation.

   Note on landmarks: chrome.js keeps the header as the single PRIMARY nav on
   purpose. This is a second, differently-labelled navigation landmark, which is
   exactly what the pattern calls for — a screen-reader user gets "Chapters" and
   "Primary" as two distinct, correctly named regions, not one ambiguous one.

   Each link's accessible name is the world's full name; the numeral is the
   visual shorthand and is aria-hidden so nobody is read "I Greppo Winery".
   The name is revealed on hover AND focus-within, is never display:none, and
   is present for assistive technology at all times.
   -------------------------------------------------------------------------- */

.cine-chapters {
  position: fixed;
  inset-inline-end: var(--space-5);
  inset-block-start: 50%;
  z-index: var(--z-sticky);
  transform: translateY(-50%);
}

/* Any modal dialog owns the screen. visibility (not opacity) so the links
   leave the tab order entirely while a panel or the switcher is open. */
html.is-scroll-locked .cine-chapters {
  visibility: hidden;
  opacity: 0;
  transition:
    opacity var(--cine-dur-micro) var(--ease-silk),
    visibility 0s linear var(--cine-dur-micro);
}

.cine-chapters__list {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.cine-chapters__item {
  margin: 0;
}

/* The connecting hairline between chapters. Decorative, empty, no content. */
.cine-chapters__item + .cine-chapters__item::before {
  content: "";
  display: block;
  inline-size: 1px;
  block-size: var(--space-3);
  margin-inline-start: auto;
  margin-inline-end: 1.3125rem;
  background-color: var(--w-line);
}

.cine-chapters__link {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
  /* 44px in both axes, always. */
  min-block-size: 2.75rem;
  min-inline-size: 2.75rem;
  padding-inline: var(--space-2);
  color: var(--w-text-dim);
  text-decoration: none;
  border-radius: var(--radius-pill);
  transition: color var(--cine-dur-micro) var(--ease-silk);
}

.cine-chapters__link:hover,
.cine-chapters__link:focus-visible {
  color: var(--w-text);
}

/* base.css owns the global :focus-visible ring; this only guarantees the ring
   has room to breathe around a pill on a busy ground. */
.cine-chapters__link:focus-visible {
  outline-offset: 3px;
}

.cine-chapters__numeral {
  display: grid;
  place-items: center;
  flex: none;
  inline-size: 1.75rem;
  block-size: 1.75rem;
  border: 1px solid transparent;
  border-radius: 50%;
  font-family: var(--font-utility);
  font-size: var(--step--1);
  letter-spacing: 0.04em;
  color: currentColor;
  transition:
    border-color var(--cine-dur-micro) var(--ease-silk),
    background-color var(--cine-dur-micro) var(--ease-silk),
    color var(--cine-dur-micro) var(--ease-silk);
}

.cine-chapters__link:hover .cine-chapters__numeral,
.cine-chapters__link:focus-visible .cine-chapters__numeral {
  border-color: var(--w-accent);
  color: var(--w-accent-soft);
}

.cine-chapters__name {
  font-family: var(--font-utility);
  font-size: var(--step--1);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  white-space: nowrap;
  opacity: 0;
  transform: translateX(6px);
  transition:
    opacity var(--cine-dur-micro) var(--ease-silk),
    transform var(--cine-dur-micro) var(--ease-silk);
}

.cine-chapters__link:hover .cine-chapters__name,
.cine-chapters__link:focus-visible .cine-chapters__name,
.cine-chapters__item:focus-within .cine-chapters__name {
  opacity: 1;
  transform: translateX(0);
}

/* The chapter you are standing in, and the chapter currently filling the
   screen. Styled identically on purpose — but only [aria-current] is announced,
   because only that one is genuinely the current page. data-cine-active tracks
   a [data-world-zone] scrolling past, which changes the palette, not the URL.
   The name stays open in both, so the state never depends on hovering. */
.cine-chapters__link[aria-current="page"],
.cine-chapters__link[data-cine-active="true"] {
  color: var(--w-text);
}

.cine-chapters__link[aria-current="page"] .cine-chapters__numeral,
.cine-chapters__link[data-cine-active="true"] .cine-chapters__numeral {
  border-color: var(--w-accent);
  background-color: color-mix(in srgb, var(--w-accent) 16%, transparent);
  color: var(--w-accent-soft);
}

.cine-chapters__link[aria-current="page"] .cine-chapters__name,
.cine-chapters__link[data-cine-active="true"] .cine-chapters__name {
  opacity: 1;
  transform: translateX(0);
}


/* --------------------------------------------------------------------------
   9. THE FOOTER AS ROLLING FILM CREDITS
   The closing beat. Not a marquee and not an infinite loop — a single roll, in
   the same order a credit block reads, played once when the footer is reached
   and then at rest and fully legible forever after.

   chrome-cinema.js adds data-cine-credits="armed" (columns held below the
   line), then "rolling" on entry, then indexes each block with
   --cine-credit-index so the stagger is 70ms per line. It NEVER removes,
   reorders or rewords a single node chrome.js wrote.

   CONTENT SAFETY: the held state exists only while the attribute says "armed",
   which only script can write, and script also guarantees "rolling" arrives —
   via IntersectionObserver, via a 3s failsafe, or immediately if the browser
   has no IntersectionObserver. The footer can never be left invisible.
   -------------------------------------------------------------------------- */

.site-footer[data-cine-credits] {
  position: relative;
  overflow: hidden;
}

/* A faint light bloom rising off the foot of the page, dithered like every
   other gradient in section 3. Purely atmospheric. */
.site-footer[data-cine-credits]::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    radial-gradient(
      140% 90% at 50% 118%,
      color-mix(in srgb, var(--w-accent) 15%, transparent) 0%,
      transparent 64%
    ),
    var(--cine-noise);
  background-size: auto, 140px 140px;
  background-blend-mode: normal, soft-light;
}

.cine-credits__rule {
  max-inline-size: var(--maxw-wide);
  margin-inline: auto;
  /* Sits in the footer's own gutter so it lines up with the columns below. */
  margin-block-start: 0;
}

.site-footer[data-cine-credits] .site-footer__inner,
.site-footer[data-cine-credits] .site-footer__legal {
  position: relative;
}

.site-footer[data-cine-credits="armed"] [data-cine-credit] {
  opacity: 0;
  transform: translateY(1.5rem);
}

.site-footer[data-cine-credits] [data-cine-credit] {
  transition:
    opacity var(--cine-dur-ui) var(--ease-silk),
    transform var(--cine-dur-scene) var(--ease-cinematic);
  transition-delay: calc(var(--cine-credit-index, 0) * 70ms);
}

.site-footer[data-cine-credits="rolling"] [data-cine-credit] {
  opacity: 1;
  transform: translateY(0);
}


/* --------------------------------------------------------------------------
   10. prefers-reduced-motion — THE COMPLETE ALTERNATE CUT
   Not a degradation. Every value this layer conveys through motion is conveyed
   statically here, with zero content loss:
     - the world retune is an honest instant swap, not a cross-fade
     - buttons keep their hover and press STATES, they simply stop pulsing
     - the capsule still condenses (that is layout, and it is useful), it just
       does not animate into place; the ring still draws to the real fraction
     - every hairline is drawn
     - the credits are a normal, static, fully legible footer
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html.cine-theme-shift,
  html.cine-theme-shift body {
    transition: none;
  }

  .cine-sheen::before {
    transition: none;
  }

  html.cine-system .cine-grain {
    animation: none;
  }

  [data-cine-rule] {
    transform: scaleX(1);
    transition: none;
  }

  html.cine-system .btn::before {
    display: none;
  }

  html.cine-system .btn:hover::before,
  html.cine-system .btn:focus-visible::before {
    animation: none;
  }

  html.cine-system .btn:not(:active),
  html.cine-system .btn:active,
  html.cine-system .icon-btn:not(:active),
  html.cine-system .icon-btn:active {
    transform: none;
    transition: none;
  }

  .cine-capsule-ring,
  .cine-capsule-ring .cine-ring circle {
    transition: none;
  }

  @media (min-width: 52em) {
    .site-header[data-cine-condensed="true"] {
      transition: none;
    }

    .site-header[data-cine-condensed="true"] .scroll-progress {
      /* The rail stays, because without a transition the ring appearing and
         the rail vanishing would read as a jump cut. Both are correct. */
      opacity: 1;
      transition: none;
    }
  }

  .cine-chapters,
  .cine-chapters__link,
  .cine-chapters__numeral,
  .cine-chapters__name {
    transition: none;
  }

  /* The chapter names stand open: with no reveal animation, a hover-only name
     would be information conveyed by motion and nothing else. */
  .cine-chapters__name {
    opacity: 1;
    transform: none;
  }

  /* An ordinary footer. No roll, no hold, no delay — every block at rest. */
  .site-footer[data-cine-credits="armed"] [data-cine-credit],
  .site-footer[data-cine-credits] [data-cine-credit] {
    opacity: 1;
    transform: none;
    transition: none;
    transition-delay: 0s;
  }
}


/* --------------------------------------------------------------------------
   11. NARROW VIEWPORTS
   The capsule is already gated to >=52em in section 7. The chapter indicator
   retires below 62em: on a phone it would sit on top of the sound toggle and
   the founder seal, and it carries NO unique content — all three worlds are in
   the header nav and again in the footer. Removing a duplicate affordance is
   not content loss; crowding a 375px screen is a real one.
   -------------------------------------------------------------------------- */

@media (max-width: 62em) {
  .cine-chapters {
    display: none;
  }
}

@media (max-width: 40em) {
  .cine-dropcap::first-letter {
    font-size: 2.8em;
  }
}
