/* ==========================================================================
   scrub.css — Apple-style scroll-scrubbed hero
   Contract classes: .scrub / .scrub__sticky / .scrub__canvas /
                      .scrub__poster / .scrub__overlay
   Relies on existing root tokens (defined elsewhere, not redeclared here):
   --bg --text --muted --accent --border --maxw
   ========================================================================== */

/* Scope box-sizing locally so the component never causes horizontal
   overflow (e.g. at 360px viewports) without redefining global box-sizing
   rules that may already exist in the rest of the site. */
.scrub,
.scrub *,
.scrub *::before,
.scrub *::after {
  box-sizing: border-box;
}

/* --------------------------------------------------------------------
   Base (mobile-first): natural document flow, no scrubbing, no sticky.
   This is also the permanent state below the 900px breakpoint — the
   scrub effect is a desktop-only enhancement per product decision, so
   mobile never pays for the frame sequence at all.
   -------------------------------------------------------------------- */
.scrub {
  position: relative;
  height: auto;
}

.scrub__sticky {
  position: relative; /* positioning context for the absolutely placed
                          canvas/poster even when not sticky */
  height: auto;
  padding-block: 96px;
  display: flex;
  align-items: center;
  overflow: hidden;
  isolation: isolate; /* keep the internal z-index stack self-contained */
}

/* Scrim behind the overlay text so copy stays legible over a moving
   (or static poster) image. Always present — mobile shows a poster
   image behind text too, so it needs the same legibility treatment. */
.scrub__sticky::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(
    to top,
    rgba(0, 0, 0, 0.72) 0%,
    rgba(0, 0, 0, 0.35) 45%,
    rgba(0, 0, 0, 0.05) 100%
  );
}

/* Canvas is hidden by default — it only ever appears once JS confirms
   desktop width + reduced-motion is not set + a 2d context is usable. */
.scrub__canvas {
  display: none;
}

.scrub__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}

/* JS adds this once the first canvas frame has actually painted.
   Scoped inside the desktop media query below so it can never hide the
   poster on mobile or under reduced-motion even if JS misfires. */

.scrub__overlay {
  position: relative;
  z-index: 2;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: 24px;
  width: 100%;
  color: var(--text);
}

/* --------------------------------------------------------------------
   Desktop enhancement: >= 900px only. Sticky pin + scrub canvas.
   Section height capped at 180vh (not a 300vh sticky trap) — 80vh of
   scroll runway to scrub through, 100vh pinned viewport.
   -------------------------------------------------------------------- */
@media (min-width: 900px) {
  .scrub {
    height: 180vh;
  }

  .scrub__sticky {
    position: sticky;
    top: 0;
    height: 100vh;
    padding-block: 0;
  }

  .scrub__canvas {
    display: block;
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    z-index: 0;
  }

  .scrub__poster.is-hidden {
    opacity: 0;
  }
}

/* --------------------------------------------------------------------
   Reduced motion: collapse to a single static poster frame in normal
   document flow. No sticky pin, no scrubbing, no animation of any
   kind — this overrides the desktop rules above regardless of
   viewport width, because motion sensitivity is not a viewport
   property. Scoped to .scrub descendants so it never reaches into
   unrelated parts of the page.
   -------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .scrub {
    height: auto !important;
  }

  .scrub__sticky {
    position: static !important;
    height: auto !important;
    padding-block: 96px !important;
  }

  .scrub__canvas {
    display: none !important;
  }

  .scrub__poster,
  .scrub__poster.is-hidden {
    opacity: 1 !important;
  }

  .scrub,
  .scrub * {
    animation: none !important;
    transition: none !important;
  }
}

/* Integration override: the data-centre frame is brightest through its
   centre vanishing point, which is exactly where the lede sits. Weight the
   scrim toward the text column so copy stays legible at every frame,
   without flattening the image on the right. */
.scrub__sticky::after {
  background:
    linear-gradient(to right,
      rgba(10,10,10,.92) 0%,
      rgba(10,10,10,.80) 32%,
      rgba(10,10,10,.45) 58%,
      rgba(10,10,10,.35) 100%),
    linear-gradient(to bottom,
      rgba(10,10,10,.75) 0%,
      rgba(10,10,10,.25) 22%,
      rgba(10,10,10,.25) 74%,
      var(--bg) 100%) !important;
}

/* ============================================================
   HERO — name only. Jan: the bio/metrics "looks bad" in the hero.
   The opening is now a cinematic title card; identity detail
   lives further down and contact stays permanently in the nav.
   ============================================================ */

.hero-name {
  font-size: clamp(3rem, 9vw, 7rem);
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1;
  margin: 0;
}

.hero-cue {
  margin-top: 28px;
  font-size: .72rem;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--muted);
}
.hero-cue::after {
  content: "";
  display: block;
  width: 1px;
  height: 44px;
  margin-top: 14px;
  background: linear-gradient(to bottom, var(--accent), transparent);
}

/* A longer scrub for the Scout Club intro so it breathes — still well under
   the 180vh ceiling per section that Codex set, and the sticky releases
   straight into the detail section so the two read as one movement. */
.scrub--tall { }
@media (min-width: 900px) {
  .scrub--tall { height: 180vh; }
}

/* The detail section sits flush against the scrub above it — no top padding,
   so there is no visible seam where the video hands off to the page. */
.scene--flush {
  min-height: 0;
  padding-block: 0 120px;
  align-items: flex-start;
}

/* Both scrub sections end on darkness; kill any residual separation. */
.scrub + .scene--flush { margin-top: -1px; }

/* ============================================================
   SCRIM — relaxed for the title-card hero.
   The earlier scrim was weighted for a dense text column. With
   only a name over the frame, that much darkness hid the 4K
   footage entirely and the transition read as a dead black
   stretch instead of a continuous shot. Let the video carry it.
   ============================================================ */
.scrub__sticky::after {
  background:
    linear-gradient(to right,
      rgba(10,10,10,.72) 0%,
      rgba(10,10,10,.42) 38%,
      rgba(10,10,10,.10) 70%,
      rgba(10,10,10,.05) 100%),
    linear-gradient(to bottom,
      rgba(10,10,10,.55) 0%,
      rgba(10,10,10,.06) 26%,
      rgba(10,10,10,.06) 70%,
      rgba(10,10,10,.85) 100%) !important;
}

/* Shorten the hero so the shot resolves into Scout Club sooner —
   less empty scrolling between the two movements. */
@media (min-width: 900px) {
  .scrub#hero { height: 150vh; }
}

/* Brightness back up: these frames are the subject now, not wallpaper. */
.scrub__canvas { filter: saturate(1.12) contrast(1.04); }

/* The hero frames are now rendered by us: already dark, already vignetted,
   and the code must stay legible. A scrim on top only muds it. */
.scrub#hero .scrub__sticky::after {
  background: linear-gradient(to bottom,
    rgba(10,10,10,.55) 0%,
    rgba(10,10,10,0) 18%,
    rgba(10,10,10,0) 72%,
    rgba(10,10,10,.90) 100%) !important;
}
.scrub#hero .scrub__canvas { filter: none; }
