/* film-world-skin — layout for the single-film hero.

   scroll-world-skin.css already styles the TYPE (.sw-copy__eyebrow/__title/__body/
   __tags, .sw-btn) and owns the brand tokens. It does NOT position the stage: those
   rules live inside scrub-engine.js, which the film hero does not load. So everything
   structural is declared here, and nothing here restates a type or colour decision. */

.fw-root { position: relative; }
.fw-track { position: relative; z-index: 1; pointer-events: none; }

/* The stage paints the ROBOT-FREE master plate (m00-beeby-lake, no baked mascot) while
   the hero is at rest. The film's own frame 0 already contains the flying robot, so
   showing it under the waving mascot put two robots on screen at once. The film is
   therefore held back until launch — the mascot waves alone, lifts off, and the film
   fades in with him already airborne. */
.fw-stage {
  position: fixed; inset: 0; z-index: 10;
  /* ?v=2 — the first poster was the film's own frame 0, which has the flying robot
     baked in. Bumping the query is what actually evicts it from the CSS image cache. */
  background: var(--sw-bg) url("/scroll/hero-film-poster.jpg?v=2") center / cover no-repeat;
  pointer-events: none;
  overflow: hidden;
}

/* The film sits under a left-weighted scrim so copy holds contrast over bright
   campus footage. Opacity waits for the first painted frame (.has-film) — on iOS a
   seeked-but-never-played muted video is blank, and revealing on metadata flashes. */
.fw-video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  /* A cut, not a dissolve. Measured at .45s and .22s: the film's baked flying robot
     double-exposes against the robot-free poster plate for the whole fade, and the two
     plates are different camera positions, so it reads as several ghost robots. 90ms is
     short enough that no frame shows both and long enough to avoid a decode flash. */
  transition: opacity .09s linear;
}
/* Needs BOTH: decoded (has-film) and FLOWN. `is-launched` fired the moment the takeoff
   started, so the film's baked robot faded up while the mascot was still on screen at
   partial opacity — two translucent robots in the same frame, the "ghost robots" bug.
   `is-flown` lands after the mascot is display:none, so only one robot ever paints.
   The fade also drops to .22s: even one robot arriving at 50% alpha reads as a ghost. */
.fw-root.has-film.is-flown .fw-video { opacity: 1; }

.fw-stage::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  width: min(58vw, 820px);
  background: linear-gradient(90deg,
    var(--sw-bg) 0%,
    color-mix(in srgb, var(--sw-bg) 80%, transparent) 34%,
    color-mix(in srgb, var(--sw-bg) 38%, transparent) 62%,
    transparent 100%);
}

/* ── MASCOT INTRO ────────────────────────────────────────────────────────────────
   Waving on frame 0, then one takeoff the instant the user scrolls. Both clips are
   VP9 with alpha, so no matte or blend mode is needed. */
.fw-mascot {
  position: absolute; z-index: 4;
  right: clamp(24px, 11vw, 190px); bottom: 0;
  height: min(56vh, 460px);
  pointer-events: none;
  transition: transform .85s cubic-bezier(.3, 0, .2, 1), opacity .5s ease .35s;
}
.fw-mascot__clip { height: 100%; width: auto; display: block; }
.fw-mascot__lift { display: none; }

.fw-root.is-launched .fw-mascot { transform: translate3d(6vw, -78vh, 0) scale(.62); opacity: 0; }
.fw-root.is-launched .fw-mascot__wave { display: none; }
.fw-root.is-launched .fw-mascot__lift { display: block; }
.fw-root.is-flown .fw-mascot { display: none; }

/* ── COPY ────────────────────────────────────────────────────────────────────────
   Cards stack in one fixed layer and are driven entirely by the engine (opacity +
   translate per frame), so every scroll pass raises them in and lets them fall back
   out. No scroll-linked CSS animation, so there is one source of truth. */
.fw-copylayer { position: fixed; inset: 0; z-index: 20; pointer-events: none; }
/* Cards are fixed layers inside the site's inner scroller. On iOS a touch that lands on a
   fixed layer scrolls the page root, not that inner scroller, so a thumb on the copy
   rubber-banded the root at 0 while the engine's scroller never moved (beacon log,
   2026-09-07). The copy takes no touches. Only the CTA links do, and only while the
   card is in. The hint is fixed too and takes none. */
.fw-copy { pointer-events: none; }
.fw-copy.is-in .fw-copy__cta a, .fw-copy.is-in .fw-copy__cta button { pointer-events: auto; }
.fw-hint { pointer-events: none; }

.fw-copy {
  position: absolute;
  left: clamp(18px, 5vw, 74px);
  top: 50%;
  width: min(46vw, 620px);
  /* Vertical centring uses the `translate` property, NOT transform, because the engine
     overwrites transform every frame. The two compose, so centring survives. */
  translate: 0 -50%;
  transform: translate3d(0, 34px, 0);
  opacity: 0;
  will-change: opacity, transform;
  transition: opacity .18s linear;
}

/* Ezra stop (segment index 3): copy reads from the TOP of the frame so it clears the
   robot below. `translate`, never transform — the engine owns transform per frame. */
.fw-copy[data-seg="3"] { top: clamp(64px, 10vh, 130px); translate: 0 0; }

.fw-copy__cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 26px; }
.fw-copy__cta .sw-btn {
  display: inline-flex; align-items: center;
  padding: 12px 20px; font-size: .92rem; text-decoration: none;
}
.sw-btn--ghost { border: var(--cain-rule) solid rgba(255,255,255,.5); color: #fff; background: none; }

/* ── ROUTE DOTS ──────────────────────────────────────────────────────────────── */
/* Right rail, vertically centred and inset far enough that it never collides with the
   site's own top nav or the Get-matched CTA. */
.fw-route {
  position: fixed; right: clamp(14px, 2.4vw, 30px); top: 50%;
  z-index: 40; transform: translateY(-50%);
  display: flex; flex-direction: column; align-items: flex-end;
  gap: 20px; padding: 18px 0;
}
/* Grows leftward off the right rail. scaleX, not width, so the active dot does not
   relayout the rail on every section change. */
.fw-route__dot {
  position: relative;
  width: 34px; height: var(--cain-rule);
  background: rgba(255,255,255,.34);
  border: 0; padding: 0; cursor: pointer;
  transform: scaleX(.647); transform-origin: 100% 50%;
  transition: background .25s, transform .25s;
}
.fw-route__dot.is-on { background: var(--sw-accent); transform: scaleX(1); }

/* The section name rides the active bar. This is where the old "01 / 05" counter went —
   it used to sit above the eyebrow and is now hidden (scroll-world-skin.css). Text comes
   from the aria-label the engine already sets, so no engine change and no duplicate copy.
   Only the active bar shows one, and the active bar is scaleX(1), so no text distortion. */
.fw-route__dot::after {
  content: attr(aria-label);
  position: absolute; right: calc(100% + 14px); top: 50%;
  transform: translateY(-50%);
  white-space: nowrap;
  font-family: var(--sw-font-body); font-size: .84rem; font-weight: 500;
  letter-spacing: .09em; color: rgba(255,255,255,.92);
  text-shadow: 0 1px 14px rgba(20,16,16,.9);
  opacity: 0; transition: opacity .25s;
}
.fw-route__dot.is-on::after { opacity: 1; }

.fw-hint {
  position: fixed; left: 50%; bottom: 26px; z-index: 30;
  transform: translateX(-50%);
  font-family: var(--sw-font-body);
  font-size: .76rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--sw-ink-soft);
  transition: opacity .3s;
}

/* Same contract as scroll-world: once the hero track leaves the viewport, every fixed
   child must stop painting or it floats over the rest of the page. */
.fw-root.is-offscreen > .fw-stage,
.fw-root.is-offscreen > .fw-copylayer,
.fw-root.is-offscreen > .fw-route,
.fw-root.is-offscreen > .fw-hint { display: none; }

@media (max-width: 860px) {
  /* The phone cut's poster, the same robot-free plate in the cut's centre window. */
  .fw-stage { background-image: url("/scroll/hero-film-poster-m.jpg"); }
  /* Copy sits above the mascot, not on it. The mascot is bottom: 0 at min(40vh, 330px)
     tall, so the card's bottom edge clears its top by 18px. */
  .fw-copy { width: min(88vw, 560px); top: auto; translate: none; transform: translate3d(0, 34px, 0); }
  /* Every card, the third included. The base skin anchors the third card with a
     three-class selector, so this one matches that weight or Ezra's CTA lands on the
     rail in the bottom-left corner. */
  .fw-copylayer .fw-copy, .fw-copylayer .fw-copy:nth-child(3) { translate: none; top: auto; bottom: calc(min(40vh, 330px) + 18px); }
  .fw-stage::after {
    width: 100%;
    background: linear-gradient(0deg,
      var(--sw-bg) 6%,
      color-mix(in srgb, var(--sw-bg) 74%, transparent) 42%,
      transparent 78%);
  }
  .fw-mascot { right: 6vw; height: min(40vh, 330px); }
  /* The copy card is bottom-anchored on phones, right where the mascot waves, and the
     base skin gives each card a .94 gradient of its own. That gradient sat on the robot.
     The stage scrim above, under the mascot, already darkens the full width here, so the
     card keeps a light local lift and the robot reads at nearly full colour. */
  .fw-copylayer .fw-copy::before {
    background: linear-gradient(0deg, rgba(20,16,16,.42) 6%, rgba(20,16,16,.2) 46%, transparent 100%);
  }
  /* The rail used to be hidden here. It now carries the section name that replaced the
     "01 / 05" counter, so hiding it would delete that label on phones. Bars shrink and
     the copy is bottom-anchored, so the rail sits in empty space. */
  /* With the copy lifted above the mascot, a mid-height right rail lands in the copy's
     lines and its label collides with the body text. On phones the rail moves to the
     bottom-left corner, the one empty spot: copy above, mascot bottom-right, hint centred.
     Bars grow rightward and the section name reads to the right of the active bar. */
  .fw-route {
    top: auto; bottom: 64px; left: 14px; right: auto; transform: none;
    align-items: flex-start; gap: 14px;
  }
  .fw-route__dot { width: 22px; transform-origin: 0 50%; }
  .fw-route__dot::after { font-size: .76rem; right: auto; left: calc(100% + 10px); }
}

@media (prefers-reduced-motion: reduce) {
  .fw-mascot, .fw-copy { transition: none; }
}
