/* ==========================================================================
   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-sub {
  display: inline-block;
  margin: 24px 0 0;
  padding: 10px 22px;
  font-size: clamp(0.95rem, 2.2vw, 1.25rem);
  font-weight: 500;
  letter-spacing: .05em;
  color: var(--text);
  background: rgba(10, 10, 10, 0.45);
  border: 1px solid rgba(34, 211, 238, 0.5);
  border-radius: 999px;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow:
    0 0 24px rgba(34, 211, 238, 0.15),
    inset 0 0 12px rgba(34, 211, 238, 0.06);
}

.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);
}

/* ============================================================
   PROJECT 01 — condensed card. The scrubbed Scout Club intro is
   retired (Jan, 2026-08-29): one normal-flow section that slides
   UP OVER the still-pinned hero on desktop, so the hero's scroll
   runway is spent watching the card arrive instead of a pause.
   ============================================================ */
.scene--project {
  position: relative;
  z-index: 3; /* above the hero's sticky stack */
  min-height: 0;
  padding-block: 72px 96px;
  align-items: flex-start;
  background:
    radial-gradient(1100px 480px at 82% -8%, rgba(34, 211, 238, 0.07), transparent 60%),
    var(--surface);
  border-top: 1px solid var(--border-bright);
  border-radius: 28px 28px 0 0;
  box-shadow: 0 -30px 70px rgba(0, 0, 0, 0.65);
}

@media (min-width: 900px) {
  /* Pull the card up into the hero's scrub runway: it starts rising
     while the frames are still playing and covers the pinned hero
     before it unpins. Mobile keeps normal flow (no sticky there). */
  .scene--project { margin-top: -35vh; }
}

@media (prefers-reduced-motion: reduce) {
  .scene--project { margin-top: 0; }
}

/* The section itself sits on --surface, so cards inside it need the next
   elevation step to keep their depth. */
.scene--project .card {
  background: var(--surface-2);
}

/* ============================================================
   TOOL CARDS — "AI as the studio". Logo chip + brand-colored
   hover glow. Marks are inline SVG (site rule: no third-party
   requests), sized by the chip.
   ============================================================ */
.toolcard__head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 12px;
}

.toolcard__head .card__title {
  margin-bottom: 0;
}

.toolcard__logo {
  width: 46px;
  height: 46px;
  flex: 0 0 46px;
  display: grid;
  place-items: center;
  border-radius: 12px;
  border: 1px solid var(--border-bright);
  transition: box-shadow 0.3s ease, border-color 0.3s ease;
}

.toolcard__logo svg {
  width: 30px;
  height: 30px;
  display: block;
}

.toolcard__logo--hf {
  background: rgba(34, 211, 238, 0.06);
}
.toolcard__logo--hf svg {
  border-radius: 8px; /* the gradient tile IS the mark; round it with the chip */
}

.toolcard__logo--claude {
  background: rgba(217, 119, 87, 0.08);
}

.toolcard--hf:hover {
  border-color: rgba(34, 211, 238, 0.5);
  box-shadow:
    0 18px 40px rgba(0, 0, 0, 0.45),
    0 0 32px rgba(34, 211, 238, 0.14);
}
.toolcard--hf:hover .toolcard__logo {
  border-color: rgba(34, 211, 238, 0.55);
  box-shadow: 0 0 18px rgba(34, 211, 238, 0.25);
}

.toolcard--claude:hover {
  border-color: rgba(217, 119, 87, 0.55);
  box-shadow:
    0 18px 40px rgba(0, 0, 0, 0.45),
    0 0 32px rgba(217, 119, 87, 0.16);
}
.toolcard--claude:hover .toolcard__logo {
  border-color: rgba(217, 119, 87, 0.6);
  box-shadow: 0 0 18px rgba(217, 119, 87, 0.28);
}

/* ============================================================
   ANNOTATED FIGURE — a product screenshot with callout chips
   and pointer lines overlaid. Positions are % of the image box,
   which is aspect-locked by the img width/height attributes, so
   they hold at every viewport. Below 760px the overlay would be
   unreadable: chips and lines hide, a numbered legend shows.
   ============================================================ */
.annot {
  position: relative;
  margin: 44px 0 0;
}

.annot picture,
.annot img {
  display: block;
  width: 100%;
  border-radius: 12px;
  border: 1px solid var(--border-bright);
}

.annot__lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.annot__lines line {
  stroke: rgba(34, 211, 238, 0.8);
  stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
}

.annot__lines circle {
  fill: var(--accent);
}

.annot__tag {
  position: absolute;
  max-width: 220px;
  padding: 6px 10px;
  font-size: 0.72rem;
  line-height: 1.4;
  color: var(--text);
  background: rgba(10, 10, 10, 0.78);
  border: 1px solid rgba(34, 211, 238, 0.45);
  border-radius: 8px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.annot__cap {
  margin-top: 12px;
  font-size: 0.75rem;
  color: var(--muted);
}

.annot__legend {
  display: none;
  margin: 14px 0 0;
  padding-left: 20px;
  font-size: 0.85rem;
  line-height: 1.6;
  color: var(--muted);
}

.annot__legend li { margin-bottom: 6px; }

@media (max-width: 760px) {
  .annot__lines,
  .annot__tag { display: none; }
  .annot__legend { display: block; }
  .annot__cap { display: none; }
}

/* ============================================================
   PROOF STRIP + LEDGER — recruiter-facing replacement for the
   old six-status-card grid (2026-08-29). Numbers first, then a
   mechanism → what-it-proves two-column ledger.
   ============================================================ */
.proof-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 24px;
  margin: 36px 0 40px;
  padding: 20px 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.proof-stat { display: flex; flex-direction: column; gap: 5px; }

.proof-num {
  font-size: clamp(1.5rem, 4vw, 2.2rem);
  font-weight: 700;
  color: var(--accent);
  line-height: 1;
  letter-spacing: -0.02em;
}

.proof-label {
  font-size: 0.8rem;
  color: var(--muted);
  line-height: 1.45;
}

.ledger {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.ledger-row {
  display: grid;
  grid-template-columns: 1.3fr 1fr;
  gap: 24px;
  padding: 18px 20px;
  background: var(--surface);
  transition: background 0.15s ease;
}

.ledger-row:hover { background: var(--surface-2); }

.ledger-what {
  font-size: 0.95rem;
  line-height: 1.55;
  color: var(--text);
}

.ledger-what .eyebrow {
  margin-bottom: 6px;
  font-size: 0.68rem;
}

.ledger-proves {
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--muted);
  align-self: center;
}

@media (max-width: 560px) {
  .ledger-row { grid-template-columns: 1fr; gap: 10px; }
  .ledger-proves {
    border-top: 1px dashed var(--border);
    padding-top: 10px;
  }
}

/* ============================================================
   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; }
