/* ============================================================
   3. Cinematic opening — scroll film
   ============================================================ */

.film {
  position: relative;
  height: var(--film-scroll);
  background: var(--ink-900);
  --scrim: 0.42;
  /* The strip that follows rides over this, so the film owns the lower
     layer explicitly rather than relying on paint order. */
  z-index: 0;
}

.film__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  background: var(--ink-900);
  isolation: isolate;
}

/* --- media --- */
.film__media {
  position: absolute;
  inset: 0;
}
.film__video,
.film__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 58% 50%;
  /* Exit parallax. While the impact strip rides up over the pinned stage,
     the frame drifts a tenth as far and pushes in slightly. That difference
     in speed is the whole effect: the strip reads as a layer moving in
     front of the film rather than a section arriving after it.

     --film-exit is written by film.js, 0 → 1 across the overlap. It is
     never set on the static fallback, so this resolves to no movement. */
  transform: translate3d(0, calc(var(--film-exit, 0) * -9vh), 0)
    scale(calc(1 + var(--film-exit, 0) * 0.045));
}

/* The stage recedes as it is covered — a frame that stays at full
   brightness under an incoming layer reads as two pages, not as depth. */
.film__stage::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 9;
  pointer-events: none;
  background: var(--ink-900);
  opacity: calc(var(--film-exit, 0) * 0.5);
}
.film__poster {
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.film.is-fallback .film__poster {
  opacity: 1;
}

/* Slow, near-imperceptible settle while the film loads */
.film__media {
  transform: scale(1.04);
  transition: transform 2400ms var(--ease-out), opacity 1200ms var(--ease);
  opacity: 0;
}
.film.is-ready .film__media {
  transform: scale(1);
  opacity: 1;
}

/* --- scrim --- */
.film__scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
      to right,
      rgba(5, 7, 10, 0.86) 0%,
      rgba(5, 7, 10, 0.46) 42%,
      rgba(5, 7, 10, 0.06) 78%
    ),
    linear-gradient(
      to bottom,
      rgba(5, 7, 10, 0.72) 0%,
      rgba(5, 7, 10, 0) 32%,
      rgba(5, 7, 10, 0) 58%,
      rgba(5, 7, 10, 0.78) 100%
    );
  opacity: var(--scrim);
  transition: opacity 420ms linear;
}
/* Base vignette keeps edges from clipping to white on bright frames.
   Deliberately plain alpha compositing — a blend mode over a playing
   video forces an expensive readback on every frame. */
.film__vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(
    118% 88% at 60% 45%,
    rgba(5, 7, 10, 0) 38%,
    rgba(5, 7, 10, 0.5) 100%
  );
}

/* --- copy --- */
@keyframes film-intro {
  from {
    opacity: 0;
    transform: translate3d(0, 14px, 0);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.film__copy {
  /* `backwards`, not `both`: a forwards fill would pin opacity and transform
     to the animation's end values and leave the exit rules below with
     nothing to say. */
  animation: film-intro 1100ms var(--ease-out) 260ms backwards;
  position: absolute;
  inset: 0;
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;

  /* Clears on --film-outro, which finishes as the strip starts moving. The
     copy block is close to half the stage tall, so fading it against the
     incoming strip would always catch a line mid-word — it has to be gone
     before the strip is on screen at all. */
  opacity: calc(1 - var(--film-outro, 0));
  transform: translate3d(0, calc(var(--film-outro, 0) * -6vh), 0);
}
.film__hud,
.film__timeline {
  opacity: calc(1 - var(--film-outro, 0));
}

.beat {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: calc(var(--nav-h) + 3vh) var(--gutter) 16vh;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  will-change: opacity, transform;
}
.beat.is-live {
  visibility: visible;
}
.beat--top {
  justify-content: flex-start;
  padding-top: calc(var(--nav-h) + 9vh);
}
.beat--lower {
  justify-content: flex-end;
  padding-bottom: 13vh;
}
.beat__inner {
  max-width: min(64rem, 72vw);
}
.beat__eyebrow {
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--blue-soft);
  font-weight: 500;
  margin-bottom: 1.5rem;
}
/* No leading rule here any more: the eyebrow now carries the chapter label,
   which already contains an em dash. Two dashes in a row at similar weight
   read as noise rather than as structure. */
.beat__headline {
  font-size: var(--fs-beat);
  line-height: 1.03;
  letter-spacing: -0.035em;
  font-weight: 500;
  color: #fff;
  text-wrap: balance;
  max-width: 15ch;
  text-shadow: 0 2px 40px rgba(0, 0, 0, 0.4);
}
.beat--wide .beat__headline {
  max-width: 18ch;
}
.beat__support {
  margin-top: 1.75rem;
  font-size: var(--fs-lead);
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.8);
  max-width: 38ch;
  letter-spacing: -0.011em;
}
.beat__still {
  display: none;
}

/* --- beat 07: the reveal --- */
/* The closing frame is the brightest place any long copy has to live, so
   the beat carries its own left-hand scrim rather than darkening the
   whole film. It fades in and out with the beat. */
.beat--reveal {
  isolation: isolate;
}
.beat--reveal::before {
  content: "";
  position: absolute;
  /* full-bleed: the copy container is capped at --maxw, but the scrim has to
     reach the real edges of the viewport or it leaves a visible seam */
  top: 0;
  bottom: 0;
  left: 50%;
  width: 100vw;
  transform: translateX(-50%);
  z-index: -1;
  background: linear-gradient(
    100deg,
    rgba(5, 7, 10, 0.93) 0%,
    rgba(5, 7, 10, 0.82) 32%,
    rgba(5, 7, 10, 0.34) 58%,
    rgba(5, 7, 10, 0) 76%
  );
}
.beat--reveal .beat__inner {
  max-width: min(62ch, 100%);
}
.reveal-id {
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: #fff;
  font-weight: 500;
  margin-bottom: 1.5rem;
}
.reveal-h1 {
  font-size: clamp(1.875rem, 3.1vw, 3.5rem);
  line-height: 1.1;
  letter-spacing: -0.032em;
  font-weight: 500;
  color: #fff;
  max-width: 22ch;
  text-wrap: balance;
}
.reveal-copy {
  margin-top: 1.75rem;
  max-width: 46ch;
  color: rgba(255, 255, 255, 0.82);
  font-size: var(--fs-body);
}
.reveal-copy p + p {
  margin-top: 0.9rem;
}
.reveal-copy strong {
  color: #fff;
  font-weight: 500;
}
.reveal-cred {
  margin-top: 1.75rem;
  padding-top: 1.25rem;
  border-top: 1px solid rgba(255, 255, 255, 0.18);
  font-size: var(--fs-small);
  color: rgba(255, 255, 255, 0.9);
  max-width: 52ch;
  line-height: 1.5;
}
.reveal-ctas {
  margin-top: 2rem;
}

/* --- HUD: the scroll cue --- */
.film__hud {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 0 var(--gutter) 2.25rem;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  pointer-events: none;
}
.film__timeline {
  position: absolute;
  left: var(--gutter);
  right: var(--gutter);
  bottom: 0;
  height: 1px;
  background: rgba(255, 255, 255, 0.16);
  max-width: calc(var(--maxw) - var(--gutter) * 2);
  margin-inline: auto;
}
.film__timeline i {
  position: absolute;
  inset: 0;
  background: #fff;
  transform: scaleX(var(--p, 0));
  transform-origin: left;
}

.film__cue {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  color: rgba(255, 255, 255, 0.62);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}

/* A mouse outline with a wheel that travels down inside it, and an arrow
   that follows a beat later. Two elements on one loop, so the gesture reads
   in a glance without a word of instruction. */
.film__mouse {
  width: 22px;
  height: 34px;
  border: 1px solid currentColor;
  border-radius: 11px;
  display: block;
  position: relative;
}
.film__mouse i {
  position: absolute;
  left: 50%;
  top: 7px;
  width: 2px;
  height: 6px;
  margin-left: -1px;
  border-radius: 1px;
  background: currentColor;
  animation: cue-wheel 2.4s var(--ease-out) infinite;
}
.film__cue-arrow {
  display: block;
  width: 18px;
  height: 18px;
  animation: cue-arrow 2.4s var(--ease-out) infinite;
}
.film__cue-arrow svg {
  width: 100%;
  height: 100%;
  display: block;
}
@keyframes cue-wheel {
  0%,
  12% {
    transform: translateY(0);
    opacity: 0;
  }
  28% {
    opacity: 1;
  }
  62% {
    transform: translateY(11px);
    opacity: 0;
  }
  100% {
    transform: translateY(11px);
    opacity: 0;
  }
}
@keyframes cue-arrow {
  0%,
  30% {
    transform: translateY(-4px);
    opacity: 0;
  }
  52% {
    opacity: 0.9;
  }
  78%,
  100% {
    transform: translateY(3px);
    opacity: 0;
  }
}
.film.is-scrolled .film__cue {
  opacity: 0;
  transform: translateY(8px);
}
@media (prefers-reduced-motion: reduce) {
  .film__mouse i,
  .film__cue-arrow {
    animation: none;
  }
  .film__cue-arrow {
    opacity: 0.9;
  }
}

@media (max-width: 900px) {
  .film__stage {
    /* svh avoids the sticky stage resizing with mobile browser chrome */
    height: 100svh;
  }
  .beat__inner {
    max-width: 100%;
  }
  .beat__headline,
  .beat--wide .beat__headline {
    max-width: 13ch;
  }
  .beat__support {
    max-width: 30ch;
    font-size: 1.0625rem;
  }
  .reveal-h1 {
    max-width: 16ch;
  }
  .film__scrim {
    background: linear-gradient(
      to bottom,
      rgba(5, 7, 10, 0.8) 0%,
      rgba(5, 7, 10, 0.35) 34%,
      rgba(5, 7, 10, 0.45) 62%,
      rgba(5, 7, 10, 0.88) 100%
    );
  }
  .film__cue {
    display: none;
  }
}

@media (max-width: 640px) {
  /* The reveal carries the most copy of any beat; on a phone it has to
     fit one viewport without shrinking below a readable size. */
  .beat--reveal {
    padding-bottom: 9vh;
  }
  .reveal-id {
    margin-bottom: 1rem;
  }
  .reveal-h1 {
    font-size: 1.75rem;
    line-height: 1.14;
    max-width: 20ch;
  }
  .reveal-copy {
    margin-top: 1.1rem;
    font-size: 0.9375rem;
    line-height: 1.5;
  }
  .reveal-copy p + p {
    margin-top: 0.6rem;
  }
  .reveal-cred {
    margin-top: 1.1rem;
    padding-top: 0.9rem;
    font-size: 0.8125rem;
  }
  .reveal-ctas {
    margin-top: 1.25rem;
    gap: 0.6rem;
  }
  .reveal-ctas .btn {
    min-height: 46px;
    padding: 0.7rem 1.15rem;
    font-size: 0.875rem;
  }
  .film--static .beat--reveal {
    min-height: auto;
  }
}

/* ============================================================
   Static fallback — reduced motion, no JS, or video unavailable
   ============================================================ */
.film--static {
  height: auto;
}
.film--static .film__stage {
  position: static;
  height: auto;
  overflow: visible;
}
.film--static .film__media,
.film--static .film__vignette,
.film--static .film__hud,
.film--static .film__timeline,
.film--static .film__scrim,
.film--static .film__stage::after {
  display: none;
}
.film--static .film__copy {
  position: static;
  padding-inline: 0;
  max-width: none;
}
.film--static .beat {
  position: relative;
  inset: auto;
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: none;
  min-height: 82svh;
  padding: calc(var(--nav-h) + 7vh) var(--gutter) 7vh;
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  justify-content: flex-end;
  isolation: isolate;
}
.film--static .beat__still {
  display: block;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: -2;
}
.film--static .beat::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to bottom,
    rgba(5, 7, 10, 0.72) 0%,
    rgba(5, 7, 10, 0.42) 40%,
    rgba(5, 7, 10, 0.88) 100%
  );
}
.film--static .beat__inner {
  max-width: min(64rem, 80vw);
}
.film--static .beat--reveal .beat__inner {
  max-width: min(62ch, 100%);
}

@media (max-width: 900px) {
  .film--static .beat {
    min-height: 72svh;
  }
}

html.no-js .film {
  height: auto;
}
html.no-js .film__stage {
  position: static;
  height: auto;
}
html.no-js .beat {
  position: relative;
  inset: auto;
  opacity: 1;
  visibility: visible;
  min-height: auto;
  padding: 10vh var(--gutter);
  max-width: var(--maxw);
  margin-inline: auto;
}
html.no-js .film__media,
html.no-js .film__hud,
html.no-js .film__timeline,
html.no-js .film__scrim {
  display: none;
}
