/* ==========================================================================
   Avatars — the futuristic layer.

   Three set pieces, each earning its weight:
     1  .deck     3D perspective carousel of live avatar panels
     2  .wall     scroll-scrubbed portrait wall, panels arriving out of Z
     3  .holo     pointer-tilted capability cards

   Everything below moves only `transform` and `opacity`, which the compositor
   can do without touching layout. The one rule that matters on a page this
   heavy: no animation may read or write layout properties on scroll.

   Markup ships VISIBLE. GSAP hides `[data-reveal]` only after it has confirmed
   it loaded — so a blocked CDN degrades to a plain, complete page rather than
   a blank one.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Ambient field. A single fixed layer behind the dark sections: two slow
   yellow auroras and a grid that recedes. Cheap — it is one element, painted
   once, translated on scroll.
   -------------------------------------------------------------------------- */
.fx-field {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  overflow: hidden;
  contain: strict;
}
.fx-field::before,
.fx-field::after {
  content: ""; position: absolute; border-radius: 50%;
  filter: blur(90px); opacity: 0.30;
  will-change: transform;
}
.fx-field::before {
  width: 46vmax; height: 46vmax; top: -14vmax; left: -10vmax;
  background: radial-gradient(circle, var(--y-yellow) 0%, transparent 68%);
  animation: fx-drift-a 34s var(--ease-inout) infinite alternate;
}
.fx-field::after {
  width: 38vmax; height: 38vmax; bottom: -12vmax; right: -8vmax;
  background: radial-gradient(circle, var(--y-red) 0%, transparent 68%);
  opacity: 0.18;
  animation: fx-drift-b 44s var(--ease-inout) infinite alternate;
}
@keyframes fx-drift-a { to { transform: translate3d(14vw, 10vh, 0) scale(1.18); } }
@keyframes fx-drift-b { to { transform: translate3d(-12vw, -8vh, 0) scale(1.12); } }

/* The receding grid. Perspective on a flat element is what makes a floor. */
.fx-grid {
  position: absolute; inset: 62% -50% -30% -50%;
  background-image:
    linear-gradient(to right,  rgb(240 198 74 / 0.10) 1px, transparent 1px),
    linear-gradient(to bottom, rgb(240 198 74 / 0.10) 1px, transparent 1px);
  background-size: 64px 64px;
  transform: perspective(340px) rotateX(62deg);
  transform-origin: 50% 0;
  mask-image: linear-gradient(to bottom, transparent 0%, #000 34%, #000 100%);
  animation: fx-grid-run 7s linear infinite;
}
@keyframes fx-grid-run { to { background-position: 0 64px; } }

/* Sections sit above the ambient field.
   The NAV is deliberately excluded. It carries z-index: var(--z-nav) (100) in
   site.css so that dropdowns escape the page, and listing it here dropped it to
   1 — putting the header in the same stacking layer as every section below it.
   The section that came later in the DOM then painted straight over the user
   menu, which rendered correctly at full size and was simply buried. */
.section, .hero, .footer { position: relative; z-index: 1; }

/* --------------------------------------------------------------------------
   1 · THE DECK — 3D perspective carousel

   Panels are absolutely stacked on one another and pushed apart purely in
   transform space, so switching the active card never reflows anything. The
   position class is set by JS; CSS owns every visual state, which keeps the
   script down to "which index is active".
   -------------------------------------------------------------------------- */
.deck {
  position: relative;
  min-height: clamp(26rem, 58vw, 40rem);
  perspective: 1600px;
  perspective-origin: 50% 46%;
  margin-block: var(--s-8);
}
.deck__stage {
  position: absolute; inset: 0;
  transform-style: preserve-3d;
  display: grid; place-items: center;
}
.deck__panel {
  position: absolute;
  width: clamp(14rem, 26vw, 20rem);
  aspect-ratio: 9 / 16;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--surface-1);
  border: 1px solid var(--surface-line);
  box-shadow: var(--shadow-card);
  transform-style: preserve-3d;
  /* The house curve. Long settle is what stops a carousel feeling mechanical. */
  transition: transform var(--dur-slow) var(--ease-out),
              opacity   var(--dur-slow) var(--ease-out),
              filter    var(--dur-slow) var(--ease-out),
              box-shadow var(--dur-slow) var(--ease-out);
  will-change: transform, opacity;
  cursor: pointer;
}
.deck__panel video,
.deck__panel img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}

/* Positional states. -2 … +2 relative to the active panel. */
.deck__panel[data-pos="0"] {
  transform: translate3d(0, 0, 120px) rotateY(0deg) scale(1);
  opacity: 1; filter: none; z-index: 5;
  box-shadow: var(--glow-yellow), var(--shadow-card);
}
.deck__panel[data-pos="-1"] { transform: translate3d(-58%, 0, -110px) rotateY( 32deg) scale(0.88); opacity: .72; filter: saturate(.7) brightness(.72); z-index: 4; }
.deck__panel[data-pos="1"]  { transform: translate3d( 58%, 0, -110px) rotateY(-32deg) scale(0.88); opacity: .72; filter: saturate(.7) brightness(.72); z-index: 4; }
.deck__panel[data-pos="-2"] { transform: translate3d(-102%, 0, -300px) rotateY( 44deg) scale(0.76); opacity: .34; filter: saturate(.4) brightness(.5); z-index: 3; }
.deck__panel[data-pos="2"]  { transform: translate3d( 102%, 0, -300px) rotateY(-44deg) scale(0.76); opacity: .34; filter: saturate(.4) brightness(.5); z-index: 3; }
/* Anything further back is parked out of sight rather than removed, so the
   panel keeps its video element and never has to reload on the way in. */
.deck__panel[data-pos="far"] {
  transform: translate3d(0, 0, -620px) scale(0.6);
  opacity: 0; z-index: 1; pointer-events: none;
}

.deck__cap {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: var(--s-8) var(--s-4) var(--s-4);
  background: linear-gradient(to top, rgb(13 13 15 / 0.94) 12%, transparent 100%);
  color: var(--y-white);
  opacity: 0; transition: opacity var(--dur) var(--ease-out);
}
.deck__panel[data-pos="0"] .deck__cap { opacity: 1; }
.deck__name { font-family: var(--font-display); font-size: var(--fs-h3); line-height: var(--lh-snug); margin: 0; }
.deck__role { font-size: var(--fs-xs); letter-spacing: var(--tracking-eyebrow); text-transform: uppercase; color: var(--y-yellow); margin: var(--s-1) 0 0; }

/* A live badge, because "this is running right now" is the whole pitch. */
.deck__live {
  position: absolute; top: var(--s-3); left: var(--s-3);
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: var(--s-1) var(--s-3);
  border-radius: var(--radius-pill);
  background: rgb(13 13 15 / 0.72);
  backdrop-filter: blur(8px);
  font-size: var(--fs-xs); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--y-white);
  opacity: 0; transition: opacity var(--dur) var(--ease-out);
}
.deck__panel[data-pos="0"] .deck__live { opacity: 1; }
.deck__live::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: var(--y-red);
  animation: deck-pulse 1.8s var(--ease-inout) infinite;
}
@keyframes deck-pulse { 50% { opacity: .28; transform: scale(.7); } }

.deck__nav {
  position: absolute; bottom: calc(-1 * var(--s-4)); left: 50%;
  transform: translateX(-50%);
  display: flex; gap: var(--s-3); align-items: center; z-index: 6;
}
.deck__dot {
  width: 8px; height: 8px; padding: 0; border: 0; border-radius: 50%;
  background: var(--surface-line); cursor: pointer;
  transition: width var(--dur) var(--ease-out), background var(--dur) var(--ease-out);
}
.deck__dot[aria-current="true"] { width: 26px; border-radius: var(--radius-pill); background: var(--y-yellow); }

.deck__arrow {
  position: absolute; top: 50%; z-index: 6;
  translate: 0 -50%;
  width: 2.75rem; height: 2.75rem; border-radius: 50%;
  display: grid; place-items: center;
  background: rgb(22 22 26 / 0.82); color: var(--y-white);
  border: 1px solid var(--surface-line); cursor: pointer;
  backdrop-filter: blur(10px);
  transition: transform var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.deck__arrow:hover { transform: scale(1.1); border-color: var(--y-yellow); }
.deck__arrow--prev { left: var(--s-2); }
.deck__arrow--next { right: var(--s-2); }

/* --------------------------------------------------------------------------
   2 · THE WALL — scroll-scrubbed portrait field

   Pinned while the portraits arrive out of Z. GSAP drives the transform; CSS
   only sets the resting state and the frame.
   -------------------------------------------------------------------------- */
.wall {
  position: relative;
  min-height: 78vh;
  perspective: 1200px;
  display: grid; place-items: center;
}
.wall__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: clamp(var(--s-3), 1.6vw, var(--s-6));
  width: 100%;
  transform-style: preserve-3d;
}
.wall__cell {
  aspect-ratio: 3 / 4;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--surface-line);
  background: var(--surface-1);
  will-change: transform, opacity;
}
.wall__cell img { width: 100%; height: 100%; object-fit: cover; display: block; }
.wall__copy {
  position: absolute; inset: auto 0 auto 0; top: 50%; translate: 0 -50%;
  text-align: center; pointer-events: none; z-index: 3;
  mix-blend-mode: normal;
}
.wall__copy h2 {
  font-size: var(--fs-hero); line-height: var(--lh-tight);
  letter-spacing: var(--tracking-hero); margin: 0;
  text-shadow: 0 8px 48px rgb(13 13 15 / 0.92);
}

/* --------------------------------------------------------------------------
   3 · HOLO — pointer-tilted capability cards
   The tilt itself is applied by JS as a CSS variable, so a device with no
   pointer simply never sets it and the card sits flat.
   -------------------------------------------------------------------------- */
.holo {
  position: relative;
  padding: var(--s-6);
  border-radius: var(--radius-lg);
  background: linear-gradient(160deg, var(--surface-1), var(--surface-0));
  border: 1px solid var(--surface-line);
  transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg));
  transform-style: preserve-3d;
  transition: transform var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
  overflow: hidden;
  height: 100%;
}
.holo:hover { border-color: rgb(240 198 74 / 0.42); }
/* The sheen tracks the pointer. Pure decoration, so it is the first thing that
   goes on a coarse pointer. */
.holo::before {
  content: ""; position: absolute; inset: -1px;
  background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 50%),
              rgb(240 198 74 / 0.14), transparent 62%);
  opacity: 0; transition: opacity var(--dur) var(--ease-out);
  pointer-events: none;
}
.holo:hover::before { opacity: 1; }
.holo__kicker {
  font-size: var(--fs-xs); letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase; color: var(--accent-ink); margin: 0 0 var(--s-3);
}
.holo__title { font-size: var(--fs-h3); line-height: var(--lh-snug); margin: 0 0 var(--s-3); }
.holo__body  { color: var(--text-mid); margin: 0 0 var(--s-4); }
.holo__saves {
  margin: 0; padding-top: var(--s-3);
  border-top: 1px solid var(--surface-line);
  font-size: var(--fs-sm); color: var(--text-lo);
}
.holo__saves strong { color: var(--y-yellow); font-weight: 500; }

@media (hover: none) {
  .holo { transform: none; }
  .holo::before { display: none; }
}

/* --------------------------------------------------------------------------
   The door chooser — the first thing anyone sees on `/`.
   -------------------------------------------------------------------------- */
.doors {
  display: grid; gap: var(--s-6);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
}
.door {
  position: relative; display: block;
  padding: clamp(var(--s-6), 3vw, var(--s-10));
  border-radius: var(--radius-lg);
  border: 1px solid var(--surface-line);
  background: linear-gradient(160deg, var(--surface-1), var(--surface-0));
  overflow: hidden; text-decoration: none; color: inherit;
  transform: perspective(1000px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateZ(0);
  transition: transform var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
  min-height: clamp(19rem, 30vw, 24rem);
  display: flex; flex-direction: column; justify-content: flex-end;
}
.door::before {
  content: ""; position: absolute; inset: -1px;
  background: radial-gradient(520px circle at var(--mx, 50%) var(--my, 50%),
              rgb(240 198 74 / 0.16), transparent 60%);
  opacity: 0; transition: opacity var(--dur) var(--ease-out); pointer-events: none;
}
.door:hover { border-color: rgb(240 198 74 / 0.5); }
.door:hover::before { opacity: 1; }
.door__num {
  position: absolute; top: var(--s-6); right: var(--s-6);
  font-family: var(--font-mono); font-size: var(--fs-xs);
  color: var(--text-lo); letter-spacing: 0.1em;
}
.door__eyebrow {
  font-size: var(--fs-xs); letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase; color: var(--accent-ink); margin: 0 0 var(--s-3);
}
.door__title {
  font-size: var(--fs-h2); line-height: var(--lh-snug);
  letter-spacing: -0.02em; margin: 0 0 var(--s-3);
}
.door__body { color: var(--text-mid); margin: 0 0 var(--s-5); max-width: 42ch; }
.door__go {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-weight: 500; color: var(--y-yellow);
}
.door__go svg { transition: transform var(--dur) var(--ease-out); }
.door:hover .door__go svg { transform: translateX(5px); }
/* The media plate behind each door. Sits under the copy, dimmed hard so the
   text never has to fight it — a hero image that beats its own headline is the
   single most common way a landing page stops converting. */
.door__plate {
  position: absolute; inset: 0; z-index: -1;
  opacity: 0.20; transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}
.door:hover .door__plate { opacity: 0.32; transform: scale(1.04); }
.door__plate img, .door__plate video {
  width: 100%; height: 100%; object-fit: cover; display: block;
  /* The plate is a WIDE band and the avatar stills are PORTRAIT, so a centred
     cover-crop lands on the torso and loses the head entirely. The face is the
     entire reason this picture is here, so the focal point is pinned near the
     top of the frame. Landscape sources overflow vertically by very little, so
     the same value leaves those essentially unchanged. */
  object-position: 50% 14%;
}
.door__plate::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, var(--surface-0) 30%, rgb(13 13 15 / 0.78) 68%, rgb(13 13 15 / 0.35));
}

/* --------------------------------------------------------------------------
   Video embeds — YouTube and self-hosted reels
   -------------------------------------------------------------------------- */
.ytc { margin: 0; }
.ytc__frame {
  position: relative; aspect-ratio: 16 / 9;
  border-radius: var(--radius); overflow: hidden;
  background: var(--surface-2); border: 1px solid var(--surface-line);
}
.ytc--wide .ytc__frame { aspect-ratio: 16 / 9; }
.ytc__frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.ytc__play {
  position: absolute; inset: 0; width: 100%; height: 100%;
  padding: 0; border: 0; background: none; cursor: pointer; display: block;
}
.ytc__play img { width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform var(--dur-slow) var(--ease-out), filter var(--dur) var(--ease-out); }
.ytc__play:hover img { transform: scale(1.05); filter: brightness(1.08); }
.ytc__tri {
  position: absolute; top: 50%; left: 50%; translate: -50% -50%;
  width: 4rem; height: 4rem; border-radius: 50%;
  background: var(--y-red); box-shadow: var(--glow-red);
  transition: transform var(--dur) var(--ease-spring);
}
.ytc__tri::after {
  content: ""; position: absolute; top: 50%; left: 54%; translate: -50% -50%;
  border-style: solid; border-width: 0.7rem 0 0.7rem 1.15rem;
  border-color: transparent transparent transparent var(--y-white);
}
.ytc__play:hover .ytc__tri { transform: scale(1.12); }
.ytc__cap { display: flex; flex-direction: column; gap: var(--s-1); padding-top: var(--s-3); }
.ytc__title { font-weight: 500; color: var(--text-hi); }
.ytc__link { font-size: var(--fs-sm); color: var(--accent-ink); }

.reelc { margin: 0; display: flex; flex-direction: column; gap: var(--s-3); }
.reelc__frame {
  position: relative; aspect-ratio: 9 / 16;
  border-radius: var(--radius); overflow: hidden;
  background: var(--surface-2); border: 1px solid var(--surface-line);
}
.reelc__v { width: 100%; height: 100%; object-fit: cover; display: block; }
.reelc__sound {
  position: absolute; right: var(--s-3); bottom: var(--s-3);
  padding: var(--s-2) var(--s-4); border-radius: var(--radius-pill);
  background: rgb(13 13 15 / 0.76); color: var(--y-white);
  border: 1px solid var(--surface-line); cursor: pointer;
  font-size: var(--fs-xs); backdrop-filter: blur(10px);
}
.reelc__sound:hover { border-color: var(--y-yellow); }
.reelc__note { color: var(--text-mid); font-size: var(--fs-sm); margin: 0; }

/* --------------------------------------------------------------------------
   ECONOMICS — the figure grid and the before/after ledger.

   The figures are set at hero scale on purpose. This is the section that has
   to survive being skim-read: someone scrolling past at speed should come away
   with "8,760" and "marginal cost → 0" even if they read nothing else.
   -------------------------------------------------------------------------- */
.section--econ {
  background:
    radial-gradient(90% 60% at 50% 0%, rgb(240 198 74 / 0.07), transparent 70%),
    var(--surface-0);
}

.econ {
  display: grid;
  gap: var(--s-8) var(--s-6);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
  margin-bottom: clamp(var(--s-12), 7vw, var(--s-24));
}
.econ__item {
  padding-top: var(--s-5);
  border-top: 1px solid var(--surface-line);
  position: relative;
}
/* A short bright rule over a long dim one: the eye reads it as a measure
   filling up, which is the right metaphor for the section. */
.econ__item::before {
  content: ""; position: absolute; top: -1px; left: 0;
  width: 2.5rem; height: 2px; background: var(--y-yellow);
}
.econ__figure {
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 1.4rem + 3.4vw, 3.75rem);
  line-height: 0.95;
  letter-spacing: -0.035em;
  color: var(--text-hi);
  margin: 0;
  font-variant-numeric: tabular-nums;
}
.econ__unit {
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--accent-ink);
  margin: var(--s-2) 0 var(--s-5);
}
.econ__label {
  font-size: var(--fs-body-lg);
  line-height: var(--lh-snug);
  color: var(--text-hi);
  margin: 0 0 var(--s-2);
}
.econ__body {
  color: var(--text-mid);
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  margin: 0;
}

/* --- The ledger ------------------------------------------------------
   Two columns and an arrow. On narrow screens the arrow rotates to point
   down and the row stacks, so the "this becomes that" reading survives —
   a swap laid out side by side turns into two unrelated lists the moment
   it wraps. */
.ledger {
  border: 1px solid var(--surface-line);
  border-radius: var(--radius-lg);
  background: linear-gradient(160deg, var(--surface-1), var(--surface-0));
  overflow: hidden;
}
.ledger__head,
.ledger__row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: var(--s-4);
  align-items: center;
  padding: var(--s-5) clamp(var(--s-4), 3vw, var(--s-8));
}
.ledger__head {
  border-bottom: 1px solid var(--surface-line);
  background: rgb(255 255 255 / 0.02);
}
.ledger__col {
  margin: 0;
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
}
.ledger__col--out { color: var(--text-lo); grid-column: 1; }
.ledger__col--in  { color: var(--accent-ink); grid-column: 3; }

.ledger__row + .ledger__row { border-top: 1px solid var(--surface-line); }
.ledger__row { transition: background var(--dur) var(--ease-out); }
.ledger__row:hover { background: rgb(240 198 74 / 0.04); }

.ledger__before {
  margin: 0; color: var(--text-lo);
  /* Struck through, but faintly: the point is that it is being retired, not
     that it was a mistake. A hard line reads as a correction. */
  text-decoration: line-through;
  text-decoration-color: rgb(232 84 74 / 0.55);
  text-decoration-thickness: 1px;
}
.ledger__after { margin: 0; color: var(--text-hi); }
.ledger__arrow { color: var(--y-yellow); display: grid; place-items: center; }

@media (max-width: 46rem) {
  .ledger__head { display: none; }
  .ledger__head, .ledger__row { grid-template-columns: 1fr; gap: var(--s-2); text-align: left; }
  .ledger__arrow { justify-self: start; rotate: 90deg; }
}

/* --------------------------------------------------------------------------
   Reduced motion. The ambient field and the grid are pure decoration, so they
   stop entirely rather than merely shortening — a drifting blur at 0.01ms is
   worse than none.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .fx-field::before, .fx-field::after, .fx-grid, .deck__live::before { animation: none; }
  .deck__panel { transition: none; }
  .holo, .door { transform: none; }
}

/* --------------------------------------------------------------------------
   Page furniture used by the root and the AI pages.
   Kept here rather than in site.css because every rule below exists to serve
   the avatar story; site.css is the shared vocabulary and should not grow a
   dialect for one section of the site.
   -------------------------------------------------------------------------- */

/* The root hero is type-only — no media panel. The avatars appear a screen
   later, in the deck, where they can actually move. Leading with a still of a
   face and a headline over it is the exact template look we are avoiding. */
.hero--root {
  padding-block: clamp(var(--s-16), 12vh, var(--s-32)) var(--s-12);
  text-align: center;
}
.hero--root .hero__title {
  font-family: var(--font-display);
  font-size: var(--fs-hero);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-hero);
  margin: var(--s-4) auto var(--s-6);
  max-width: 18ch;
}
.hero--root .hero__sub {
  color: var(--text-mid);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
  max-width: 62ch;
  margin-inline: auto;
}

.deck__hint {
  text-align: center;
  max-width: none;
  color: var(--text-lo);
  font-size: var(--fs-sm);
  margin-top: var(--s-10);
}

.section__more  { margin-top: var(--s-10); text-align: center; max-width: none; }
.section__note  {
  margin-top: var(--s-8);
  color: var(--text-mid);
  max-width: var(--measure);
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
}
.section__note strong { color: var(--text-hi); font-weight: 500; }

/* Reels are 9:16. Two of them side by side at full container width would be
   absurdly tall, so the track is capped and centred rather than stretched.

   NOT CURRENTLY RENDERED: the only markup using this was the standalone reel
   section on the Sikha and Ge'sha case studies, removed from work_gallery()
   because those films now sit in the case grid. Kept because the sizing
   problem it solves will recur the moment a reel pair is laid out again; if it
   is still unused when this file is next touched, delete it. */
.grid--reels {
  margin-top: var(--s-12);
  max-width: 46rem;
  margin-inline: auto;
}

.cta-band {
  text-align: center;
  padding: clamp(var(--s-10), 6vw, var(--s-20)) var(--s-6);
  border-radius: var(--radius-lg);
  border: 1px solid var(--surface-line);
  background:
    radial-gradient(120% 140% at 50% 0%, rgb(240 198 74 / 0.10), transparent 62%),
    linear-gradient(160deg, var(--surface-1), var(--surface-0));
}
.cta-band__title {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-snug);
  letter-spacing: -0.02em;
  margin: 0 0 var(--s-4);
}
.cta-band__sub {
  color: var(--text-mid);
  max-width: 52ch;
  margin: 0 auto var(--s-8);
}
.cta-band__actions {
  display: flex; flex-wrap: wrap; gap: var(--s-4);
  justify-content: center; margin: 0;
  /* site.css caps every <p> at var(--measure). With margin:0 that cap makes a
     narrow, LEFT-ALIGNED box, and justify-content then centres the buttons
     inside it — so they land a third of the way across a wide band instead of
     in the middle. Every centred <p> on these pages needs the cap lifted. */
  max-width: none;
}

/* On the shared root neither door is "current", so the sliding thumb has
   nowhere to sit. Hiding it beats parking it under an arbitrary half. */
.page-root .door-switch__thumb { opacity: 0; }

/* Reveal wrappers.
   `data-reveal` and `data-tilt` must never sit on the same element: GSAP writes
   an inline `transform` when it reveals, which permanently outranks the
   `transform` the tilt builds from custom properties, so the card silently
   stops tilting the moment it animates in. The wrapper reveals; the card
   inside it tilts. They are separate elements precisely so they can each own
   `transform` outright. */
.door-slot, .holo-slot { display: flex; height: 100%; }
.door-slot > .door, .holo-slot > .holo { flex: 1; }

.films__more {
  margin: clamp(var(--s-12), 6vw, var(--s-20)) 0 var(--s-8);
  font-size: var(--fs-h3);
  color: var(--text-hi);
}

/* --------------------------------------------------------------------------
   ROSTER — alternating rows, one avatar each.

   Not a card grid. Five identical cards reads as a catalogue of stock photos;
   alternating full-width rows reads as five people who were each cast for
   something. The flip is what stops it settling into a rhythm the eye can
   stop paying attention to.
   -------------------------------------------------------------------------- */
.roster { display: grid; gap: clamp(var(--s-12), 8vw, var(--s-24)); }

.roster__row {
  display: grid;
  gap: clamp(var(--s-6), 4vw, var(--s-12));
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  align-items: center;
}
.roster__row--flip > .roster__media { order: 2; }

.roster__media {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--surface-line);
  background: var(--surface-1);
  box-shadow: var(--shadow-card);
}
.roster__media img,
.roster__media video { width: 100%; height: 100%; object-fit: cover; display: block; }

.roster__idx {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  color: var(--y-yellow);
  margin: 0 0 var(--s-4);
}
.roster__name {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-tight);
  letter-spacing: -0.02em;
  margin: 0 0 var(--s-2);
}
.roster__role {
  font-size: var(--fs-sm);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--text-lo);
  margin: 0 0 var(--s-5);
}
.roster__note {
  color: var(--text-mid);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
  max-width: 46ch;
  margin: 0;
}
.roster__tag {
  display: inline-block;
  margin: var(--s-5) 0 0;
  padding: var(--s-1) var(--s-4);
  border: 1px solid rgb(240 198 74 / 0.4);
  border-radius: var(--radius-pill);
  font-size: var(--fs-xs);
  color: var(--y-yellow);
}

/* Below ~54rem the two columns would each be too narrow to hold a portrait or
   a readable measure, so the row stacks and the flip is dropped — alternating
   a single column just produces an inconsistent reading order. */
@media (max-width: 54rem) {
  .roster__row { grid-template-columns: 1fr; }
  .roster__row--flip > .roster__media { order: 0; }
  .roster__media { aspect-ratio: 3 / 2; }
}

/* --------------------------------------------------------------------------
   WORKFORCE — the two speaking avatars on /ai/.
   Two cards, not five: this page's job is the argument, and the full roster
   lives one click away on /ai/avatar/. Showing everything here would make the
   deeper page pointless.
   -------------------------------------------------------------------------- */
.workforce {
  display: grid;
  gap: clamp(var(--s-4), 2vw, var(--s-6));
  /* One column per avatar, filling the container. The track was capped at
     64rem and set to five columns while only four cards exist, so the roster
     sat in the left two-thirds with a third of the section empty beside it. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(var(--s-4), 2.5vw, var(--s-8));
  max-width: none;
}
@media (max-width: 64rem) { .workforce { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 34rem; } }
@media (max-width: 34rem) { .workforce { grid-template-columns: 1fr; max-width: 16rem; } }
.workforce__card { margin: 0; }
.workforce__media {
  position: relative;
  /* 3:4 rather than the source 9:16. These are talking-head takes, so the
     bottom third of a 9:16 frame is just torso — cropping it costs nothing and
     halves the height. */
  aspect-ratio: 3 / 4;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--surface-line);
  background: var(--surface-1);
  box-shadow: var(--shadow-card);
  margin-bottom: var(--s-5);
}
.workforce__media video,
.workforce__media img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  object-position: 50% 18%;
}
/* The live badge is hidden on deck panels until they are frontmost; here every
   card is frontmost, so it is always on. */
.workforce__media .deck__live { opacity: 1; transform: scale(0.86); transform-origin: top left; }

.workforce__name { font-size: var(--fs-body); line-height: var(--lh-snug); margin: 0 0 var(--s-1); }
.workforce__role {
  font-size: var(--fs-xs); letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase; color: var(--accent-ink); margin: 0 0 var(--s-3);
}
.workforce__note { display: none; }   /* five cards; the note belongs on /ai/avatar/ */

/* ==========================================================================
   HOMEPAGE LAYOUT — the split first, then the avatars, then the work.
   ========================================================================== */

.preview-flag {
  /* site.css constrains stray <p> to a readable measure; this is a full-bleed
     band, so the width is reset explicitly rather than left to inheritance. */
  max-width: none; width: 100%; box-sizing: border-box;
  margin: 0; padding: var(--s-3) var(--gutter);
  background: rgb(240 198 74 / 0.10);
  border-bottom: 1px solid rgb(240 198 74 / 0.28);
  color: var(--text-mid); font-size: var(--fs-sm); text-align: center;
}
.preview-flag a { color: var(--accent-ink); }

/* --- 1 · Hero with the deck beside the type -------------------------------
   Type left, faces right. The deck is given the larger share because it is
   the thing doing the persuading; the copy only has to name what is on screen. */
.hero--deck { padding-block: clamp(var(--s-10), 7vh, var(--s-20)); }
.hero--deck__grid {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: clamp(var(--s-8), 5vw, var(--s-16));
  align-items: center;
}
.hero--deck__title {
  font-family: var(--font-display);
  font-size: var(--fs-hero);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-hero);
  margin: var(--s-4) 0 var(--s-6);
}
.hero--deck__sub {
  color: var(--text-mid);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
  max-width: 46ch;
  margin: 0 0 var(--s-8);
}
.hero--deck__cta { display: flex; flex-wrap: wrap; gap: var(--s-4); margin: 0; }

/* The hero deck is shorter than the standalone one: it has to share the fold
   with the headline, and a carousel that pushes the CTA below the fold is
   worse than a smaller carousel. */
.deck--hero { min-height: clamp(24rem, 46vw, 34rem); margin-block: 0; }
.deck--hero .deck__panel { width: clamp(12rem, 21vw, 17rem); }

@media (max-width: 60rem) {
  .hero--deck__grid { grid-template-columns: 1fr; }
  .deck--hero { min-height: clamp(26rem, 96vw, 34rem); }
}

/* --- 2 · The split -------------------------------------------------------
   Flat variant: no media plate, shorter cards. On this layout the avatars
   have already made the visual argument one screen above, so the doors only
   need to be legible and quick. */
.split {
  display: grid; gap: var(--s-6);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
}
.door--flat { min-height: 0; padding: clamp(var(--s-6), 3vw, var(--s-8)); }

/* --- 3 · The work, in one line -------------------------------------------
   A single track. It scrolls sideways rather than wrapping, because the whole
   point of the section is that it reads as ONE line — a strip that wraps to
   three rows on a phone is just a grid with extra steps.

   scroll-snap keeps it from stopping halfway between two films, and the
   scrollbar is left visible: a horizontal track with no visible affordance is
   a well-known way to hide content from people who never think to swipe. */
.workline {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(15rem, 1fr);
  gap: clamp(var(--s-4), 2vw, var(--s-6));
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--s-4);
  overscroll-behavior-x: contain;
}
.workline__item { scroll-snap-align: start; min-width: 0; }
.workline__why {
  margin: var(--s-3) 0 0;
  color: var(--text-mid);
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
}
/* The 16:9 films and the 9:16 reels sit in one track, so both are normalised
   to a common frame — otherwise the reels are three times the height of the
   vlogs and the "line" reads as a broken layout. */
.workline .ytc__frame,
.workline .reelc__frame { aspect-ratio: 4 / 5; }
.workline .reelc { gap: 0; }
.workline .reelc__note { display: none; }   /* the why line replaces it */

.workline::-webkit-scrollbar { height: 6px; }
.workline::-webkit-scrollbar-track { background: var(--surface-1); border-radius: var(--radius-pill); }
.workline::-webkit-scrollbar-thumb { background: var(--surface-line); border-radius: var(--radius-pill); }
.workline::-webkit-scrollbar-thumb:hover { background: var(--y-yellow); }

/* --------------------------------------------------------------------------
   Flat doors with a picture (homepage split).

   On the full-height doors the plate is a dim wash behind the copy, because
   there the headline is the thing doing the work. On the flat cards there is
   no headline large enough to carry a wash, and a 20%-opacity photo behind
   small type is neither legible as a picture nor invisible enough to ignore —
   it just looks like a smudge.

   So here the plate becomes a real, full-strength image band at the top of the
   card. That is the whole point of the picture: someone should know which card
   is which before reading a word.
   -------------------------------------------------------------------------- */
.door--flat {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  padding: 0;
  overflow: hidden;
}
.door--flat > .door__plate {
  position: relative;
  inset: auto;
  z-index: 0;
  opacity: 1;
  height: clamp(9rem, 18vw, 13rem);
  flex: none;
  border-bottom: 1px solid var(--surface-line);
}
.door--flat:hover > .door__plate { opacity: 1; transform: none; }
.door--flat > .door__plate img { transition: transform var(--dur-slow) var(--ease-out); }
.door--flat:hover > .door__plate img { transform: scale(1.05); }
/* A short scrim at the base of the band so the join to the card reads as one
   surface rather than a photo stuck on top of a box. */
.door--flat > .door__plate::after {
  background: linear-gradient(to top, rgb(13 13 15 / 0.85) 0%, rgb(13 13 15 / 0.12) 46%, transparent 72%);
}
/* Everything after the plate gets the card's padding back. */
.door--flat > :not(.door__plate) { margin-inline: clamp(var(--s-6), 3vw, var(--s-8)); }
.door--flat > .door__eyebrow { margin-top: clamp(var(--s-6), 3vw, var(--s-8)); }
.door--flat > .door__go { margin-bottom: clamp(var(--s-6), 3vw, var(--s-8)); margin-top: auto; }
/* The index sits ON the photograph now, and these photographs are bright — a
   white numeral with a text-shadow disappeared into the pale background of the
   headset shot. A small dark chip guarantees contrast against any image we
   ever put here, rather than against the two we happen to have today. */
.door--flat > .door__num {
  position: absolute; top: var(--s-3); right: var(--s-3);
  margin: 0; z-index: 2;
  padding: var(--s-1) var(--s-3);
  border-radius: var(--radius-pill);
  background: rgb(13 13 15 / 0.66);
  backdrop-filter: blur(8px);
  color: var(--y-white);
}

/* The light theme reads these as content images, so they keep full contrast. */
.theme-light .door--flat > .door__plate::after {
  background: linear-gradient(to top, rgb(255 253 247 / 0.9) 0%, transparent 62%);
}

/* ==========================================================================
   Homepage — work rows and the services menu
   ========================================================================== */

/* --- Films: three landscape 16:9 ---------------------------------------
   3 across → 2 → 1. Both step-downs are driven by the width the FRAME needs
   to stay watchable, not by device names: below about 19rem a 16:9 video is
   too small to read a face in, which is the whole content of these clips. */
.filmrow {
  display: grid;
  gap: clamp(var(--s-5), 2.4vw, var(--s-8));
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
@media (max-width: 64rem) { .filmrow { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 40rem) { .filmrow { grid-template-columns: 1fr; } }

.filmrow__item { margin: 0; min-width: 0; }
/* Native ratio restored. The shared track used to force 4/5 on these, which
   letterboxed every vlog into a tall black slab with a stripe of video in it. */
.filmrow .ytc__frame { aspect-ratio: 16 / 9; }
.filmrow__why {
  margin: var(--s-3) 0 0;
  color: var(--text-mid);
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
}

/* --- Reels: two portrait 9:16 ------------------------------------------
   Capped and centred. Two 9:16 films at full container width would be taller
   than the viewport, which turns a proof strip into a scroll obstacle. */
.reelrow {
  display: grid;
  gap: clamp(var(--s-5), 2.4vw, var(--s-8));
  grid-template-columns: repeat(2, minmax(0, 1fr));
  max-width: 44rem;
  margin: clamp(var(--s-10), 5vw, var(--s-16)) auto 0;
}
@media (max-width: 40rem) { .reelrow { grid-template-columns: 1fr; max-width: 22rem; } }
.reelrow__item { margin: 0; min-width: 0; }
.reelrow .reelc__frame { aspect-ratio: 9 / 16; }

/* --- Services menu -------------------------------------------------------
   Rows, not cards. Eight cards is a wall to be waded through; eight rows is a
   menu that can be scanned in a few seconds, and scanning is the only job a
   services list has.
   -------------------------------------------------------------------------- */
.section--services {
  background:
    radial-gradient(80% 50% at 12% 0%, rgb(240 198 74 / 0.06), transparent 68%),
    var(--surface-0);
}

.svcs {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: clamp(var(--s-3), 1.4vw, var(--s-5));
  /* Four across, then two, then one. Eight full-width rows made the reader
     scroll through a menu that should fit on one screen; small boxes let the
     whole offer be taken in at a glance, which is the only thing a services
     list has to be good at. */
  /* Three across suits six boxes; four left an awkward 4+2. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
@media (max-width: 68rem) { .svcs { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 34rem) { .svcs { grid-template-columns: 1fr; } }

.svc {
  position: relative;
  display: grid;
  grid-template-rows: auto auto 1fr;
  gap: var(--s-2);
  padding: clamp(var(--s-4), 1.6vw, var(--s-6));
  border: 1px solid var(--surface-line);
  border-radius: var(--radius);
  background: linear-gradient(160deg, var(--surface-1), var(--surface-0));
  transition: border-color var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
  min-width: 0;
}
.svc:hover { border-color: rgb(240 198 74 / 0.45); transform: translateY(-3px); }

/* The index sits in the corner rather than in its own column: at box width a
   dedicated column for two characters costs more than it explains. */
.svc__n {
  position: absolute; top: var(--s-3); right: var(--s-4);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.12em;
  color: var(--text-lo);
  transition: color var(--dur) var(--ease-out);
}
.svc:hover .svc__n { color: var(--y-yellow); }

.svc__body { display: contents; }

/* The paired services (inbound/outbound, ads/languages) list both lines inside
   the one box that owns them, so the category appears once in the kicker
   column instead of twice. */
.svc__items {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--s-2);
  align-self: start;
}
.svc__items li {
  position: relative;
  padding-left: var(--s-4);
  color: var(--text-mid);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}
.svc__items li::before {
  content: ""; position: absolute; left: 0; top: 0.55em;
  width: 6px; height: 1px; background: var(--y-yellow);
}

.svc__kicker {
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--accent-ink);
  padding-right: var(--s-8);   /* clear of the index */
}
.svc__title {
  font-family: var(--font-display);
  font-size: clamp(1.05rem, 0.95rem + 0.4vw, 1.3rem);
  line-height: var(--lh-snug);
  letter-spacing: -0.015em;
  color: var(--text-hi);
}
.svc__note {
  color: var(--text-mid);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  max-width: none;
}

/* The arrow has nowhere useful to sit in a box this size, and the whole box
   already lifts on hover — two affordances for one state is noise. */
.svc__arrow { display: none; }

@media (hover: none) { .svc:hover { transform: none; } }

/* --------------------------------------------------------------------------
   Door prompt — shared-homepage only.

   The switch has no `aria-current` on `/`, so nothing marks it as a live
   choice. The label restores the question, and the pills get a slow breathing
   outline so the eye finds them without a colour change that would compete
   with the red CTA on the other side of the bar.
   -------------------------------------------------------------------------- */
.door-prompt {
  margin: 0 var(--s-3) 0 var(--s-5);
  max-width: none;
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--text-lo);
  white-space: nowrap;
  align-self: center;
}

.door-switch--ask { position: relative; }
.door-switch--ask::after {
  content: "";
  position: absolute; inset: -2px;
  border-radius: inherit;
  border: 1px solid var(--y-yellow);
  opacity: 0;
  pointer-events: none;
  animation: door-breathe 3.6s var(--ease-inout) infinite;
}
@keyframes door-breathe {
  0%, 100% { opacity: 0; transform: scale(0.99); }
  50%      { opacity: 0.5; transform: scale(1); }
}
.door-switch--ask .door-switch__btn { color: var(--text-hi); }

/* Narrow bars have no room for the label; the pills still work without it. */
@media (max-width: 60rem) { .door-prompt { display: none; } }

@media (prefers-reduced-motion: reduce) {
  .door-switch--ask::after { animation: none; opacity: 0.35; }
}

/* ==========================================================================
   THE CHOOSER — expanding split-screen (homepage)

   Two halves that compete for space. Hovering one takes width from the other,
   returns its colour and unfolds its description; the other desaturates and
   folds away. The heading asks "which one are you?" and the interaction is
   the answer — a reader picks a side rather than comparing two feature lists.

   Every effect is transform/opacity/filter plus flex-grow. flex-grow is the
   one exception to the transform-only rule and it is deliberate: animating
   width would reflow the text inside, whereas flex-grow on two siblings that
   each contain an absolutely-positioned media layer only re-lays-out the two
   boxes, which is cheap and — unlike a transform: scaleX — does not distort
   the type.
   ========================================================================== */

.section--chooser { overflow: clip; }

.chooser {
  position: relative;
  display: flex;
  gap: 0;
  min-height: clamp(28rem, 62vh, 42rem);
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--surface-line);
  margin-top: clamp(var(--s-8), 4vw, var(--s-12));
  isolation: isolate;
}

.chooser__half {
  position: relative;
  flex: 1 1 0;
  display: flex;
  align-items: flex-end;
  padding: clamp(var(--s-6), 3vw, var(--s-12));
  text-decoration: none;
  color: inherit;
  overflow: hidden;
  min-width: 0;
  transition: flex-grow var(--dur-slow) var(--ease-out);
}

/* --- media -------------------------------------------------------------- */
.chooser__media { position: absolute; inset: 0; z-index: 0; }
.chooser__media img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  object-position: 50% 22%;
  /* Muted at rest so neither half shouts, and so the return of colour on
     hover is a real event rather than a slight brightening. */
  filter: grayscale(0.75) brightness(0.42) contrast(1.05);
  transform: scale(1.06);
  transition: filter var(--dur-slow) var(--ease-out),
              transform 1.1s var(--ease-out);
  will-change: transform, filter;
}
/* The scrim is a separate layer from the image so the image can brighten while
   the type keeps its guaranteed contrast. */
.chooser__half::before {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(to top, rgb(13 13 15 / 0.94) 8%, rgb(13 13 15 / 0.66) 44%, rgb(13 13 15 / 0.22) 100%);
  transition: opacity var(--dur-slow) var(--ease-out);
}
/* A wash in each half's own colour: yellow for the studio, cool for study. */
.chooser__half::after {
  content: ""; position: absolute; inset: 0; z-index: 2;
  opacity: 0; transition: opacity var(--dur-slow) var(--ease-out);
  pointer-events: none;
}
.chooser__half--ai::after    { background: radial-gradient(90% 70% at 30% 100%, rgb(240 198 74 / 0.24), transparent 68%); }
.chooser__half--study::after { background: radial-gradient(90% 70% at 70% 100%, rgb(90 170 255 / 0.18), transparent 68%); }

/* --- the ghost numeral -------------------------------------------------- */
.chooser__ghost {
  position: absolute; top: clamp(var(--s-4), 2vw, var(--s-8)); right: clamp(var(--s-4), 2vw, var(--s-8));
  z-index: 3;
  font-family: var(--font-display);
  font-size: clamp(3rem, 6vw, 5.5rem);
  line-height: 1;
  color: transparent;
  -webkit-text-stroke: 1px rgb(255 255 255 / 0.16);
  transition: -webkit-text-stroke-color var(--dur-slow) var(--ease-out),
              transform var(--dur-slow) var(--ease-out);
}

/* --- copy --------------------------------------------------------------- */
.chooser__body {
  position: relative; z-index: 3;
  display: grid; gap: var(--s-4);
  max-width: 30ch;
}
.chooser__kicker {
  font-size: var(--fs-xs); letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase; color: var(--y-yellow);
}
.chooser__half--study .chooser__kicker { color: #9ec9ff; }
.chooser__h {
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 1rem + 2.6vw, 3.25rem);
  line-height: 1.02; letter-spacing: -0.03em;
  color: var(--y-white);
}
/* Folded away at rest. grid-template-rows 0fr -> 1fr is the one way to
   animate to auto height without measuring anything in JavaScript. */
.chooser__more {
  display: grid; grid-template-rows: 0fr;
  opacity: 0;
  color: var(--text-mid);
  font-size: var(--fs-sm); line-height: var(--lh-body);
  transition: grid-template-rows var(--dur-slow) var(--ease-out),
              opacity var(--dur) var(--ease-out);
}
.chooser__more > * { min-height: 0; }
.chooser__go {
  display: inline-flex; align-items: center; gap: var(--s-2);
  color: var(--y-white); font-weight: 500;
  opacity: 0.7; transition: opacity var(--dur) var(--ease-out), gap var(--dur) var(--ease-out);
}
.chooser__go svg { transition: transform var(--dur) var(--ease-out); }

/* --- the seam ----------------------------------------------------------- */
.chooser__seam {
  position: absolute; top: -10%; bottom: -10%; left: 50%; z-index: 4;
  width: 1px; background: var(--surface-line);
  transform: translateX(-50%) rotate(4deg);
  transition: left var(--dur-slow) var(--ease-out), background var(--dur-slow) var(--ease-out);
  pointer-events: none;
}

/* --- the hover state ---------------------------------------------------- */
@media (hover: hover) {
  /* Both rules must be reachable, so the winning one has to be at least as
     specific as the shrink rule. `.chooser:hover .chooser__half` is three
     class-level selectors and `.chooser__half:hover` is two, so the shrink was
     silently applying to the hovered half as well and nothing resized — the
     colour and copy changed and the split stayed at 50/50. Qualifying the
     grow rule the same way settles it. */
  .chooser:hover .chooser__half       { flex-grow: 0.78; }
  .chooser:hover .chooser__half:hover { flex-grow: 1.55; }

  .chooser__half:hover .chooser__media img {
    filter: grayscale(0) brightness(0.78) contrast(1.02);
    transform: scale(1);
  }
  .chooser__half:hover::after { opacity: 1; }
  .chooser__half:hover .chooser__ghost {
    -webkit-text-stroke-color: rgb(240 198 74 / 0.55);
    transform: translateY(-4px);
  }
  .chooser__half:hover .chooser__more { grid-template-rows: 1fr; opacity: 1; }
  .chooser__half:hover .chooser__go   { opacity: 1; gap: var(--s-3); }
  .chooser__half:hover .chooser__go svg { transform: translateX(4px); }

  /* The unhovered half recedes rather than merely staying put — without this
     the effect reads as one panel growing instead of a choice being made. */
  .chooser:hover .chooser__half:not(:hover) .chooser__media img { filter: grayscale(1) brightness(0.3); }
  .chooser:hover .chooser__half:not(:hover) .chooser__body { opacity: 0.55; }
  .chooser__body { transition: opacity var(--dur-slow) var(--ease-out); }

  .chooser:has(.chooser__half--ai:hover)    .chooser__seam { left: 60%; background: rgb(240 198 74 / 0.5); }
  .chooser:has(.chooser__half--study:hover) .chooser__seam { left: 40%; background: rgb(158 201 255 / 0.5); }
}

/* Without a pointer there is no hover to reveal anything, so both halves ship
   open, in colour, at equal width. */
@media (hover: none) {
  .chooser { flex-direction: column; min-height: 0; }
  .chooser__seam { display: none; }
  .chooser__media img { filter: grayscale(0) brightness(0.5); transform: none; }
  .chooser__more { grid-template-rows: 1fr; opacity: 1; }
  .chooser__go { opacity: 1; }
  .chooser__half { min-height: 22rem; }
}

@media (prefers-reduced-motion: reduce) {
  .chooser__half, .chooser__media img, .chooser__more,
  .chooser__seam, .chooser__ghost, .chooser__body { transition: none; }
}

/* --------------------------------------------------------------------------
   CHOOSER HEADER — one line of heading, one line of explanation.

   The generic section head wrapped its subtitle onto a second row, which turns
   a crisp two-beat opener into a paragraph. Both lines are now sized to hold
   on one row at desktop and allowed to wrap only when the viewport genuinely
   cannot take them.
   -------------------------------------------------------------------------- */
.chooserhead { display: grid; gap: var(--s-4); }

.chooserhead__eyebrow {
  display: flex; align-items: center; gap: var(--s-3);
  margin: 0; max-width: none;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--y-yellow);
}
/* A rule that draws itself in, so the header has one moving part instead of
   a decorative flourish that just sits there. */
.chooserhead__eyebrow::after {
  content: ""; height: 1px; width: 0;
  background: linear-gradient(to right, var(--y-yellow), transparent);
  animation: head-rule 1.4s var(--ease-out) 0.2s forwards;
}
@keyframes head-rule { to { width: clamp(3rem, 12vw, 11rem); } }

.chooserhead__h {
  margin: 0;
  font-family: var(--font-display);
  /* The upper bound is set by what fits on ONE row in the wide container, not
     by taste — past this the line breaks and the whole point is lost. */
  font-size: clamp(2.25rem, 1rem + 5.4vw, 5rem);
  line-height: 1.02;
  letter-spacing: -0.035em;
  color: var(--text-hi);
  text-wrap: nowrap;
}

/* The accent phrase.
   Three layers, all cheap: a live gradient under the glyphs, a sheen that
   sweeps across it, and a soft bloom sitting behind. It is the only animated
   colour on the section at rest, so the eye lands on what the company does
   before it reads a word of the rest. */
.chooserhead__you {
  position: relative;
  display: inline-block;
  background: linear-gradient(100deg,
              var(--y-yellow)  0%,
              #FFE9A8         26%,
              #FFFFFF         42%,
              #FFE9A8         56%,
              var(--y-yellow) 78%,
              var(--y-yellow) 100%);
  background-size: 280% 100%;
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
  animation: you-sheen 7s var(--ease-inout) infinite;
  /* Paint hint: the sweep repaints a large text area every frame. */
  will-change: background-position;
}
@keyframes you-sheen {
  0%, 58%, 100% { background-position: 165% 0; }
  22%           { background-position: -55% 0; }
}

/* The bloom is a separate, blurred copy of the same text sitting behind the
   crisp one. Using the text itself rather than a rectangle means the glow
   follows the letterforms instead of boxing them. */
.chooserhead__you::before {
  content: attr(data-text);
  position: absolute; inset: 0;
  z-index: -1;
  color: var(--y-yellow);
  filter: blur(18px);
  opacity: 0.28;
  animation: you-bloom 7s var(--ease-inout) infinite;
  pointer-events: none;
}
@keyframes you-bloom {
  0%, 58%, 100% { opacity: 0.22; }
  22%           { opacity: 0.42; }
}

/* An underline that grows once on arrival and then stays — a rule that keeps
   re-drawing turns into a distraction on a page people are reading. */
.chooserhead__you::after {
  content: ""; position: absolute;
  left: 0; bottom: -0.08em; height: 2px; width: 0;
  background: linear-gradient(to right, var(--y-yellow), rgb(240 198 74 / 0));
  animation: you-rule 1.1s var(--ease-out) 0.55s forwards;
}
@keyframes you-rule { to { width: 100%; } }

/* LIGHT THEME.
   The dark-theme gradient runs through #FFE9A8 and #FFFFFF — on a white page
   that is invisible, and the headline lost its second half entirely. Light
   surfaces get a deep amber ramp instead: same gesture, actual contrast.
   The bloom goes too; a glow needs something dark to glow against. */
.theme-light .chooserhead__you {
  background: linear-gradient(100deg,
              #8A6100  0%,
              #B8860B 26%,
              #E0A62B 42%,
              #B8860B 58%,
              #8A6100 80%,
              #8A6100 100%);
  background-size: 280% 100%;
  -webkit-background-clip: text; background-clip: text;
}
.theme-light .chooserhead__you::before { display: none; }
.theme-light .chooserhead__you::after {
  background: linear-gradient(to right, #B8860B, rgb(184 134 11 / 0));
}

.chooserhead__sub {
  display: flex; align-items: center; gap: var(--s-4);
  margin: 0; max-width: none;
  color: var(--text-mid);
  font-size: var(--fs-body-lg);
  line-height: 1.4;
  text-wrap: nowrap;
}
.chooserhead__rule {
  flex: none; width: clamp(1.5rem, 4vw, 3.5rem); height: 1px;
  background: var(--surface-line);
}

/* Below ~46rem neither line can hold on one row at a readable size, so both
   are released. Forcing nowrap there would overflow the viewport, which is
   worse than the wrap it was avoiding. */
@media (max-width: 46rem) {
  .chooserhead__h,
  .chooserhead__sub { text-wrap: balance; }
  .chooserhead__sub { display: block; }
  .chooserhead__rule { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .chooserhead__eyebrow::after { animation: none; width: clamp(3rem, 12vw, 11rem); }
  .chooserhead__you,
  .chooserhead__you::before { animation: none; }
  .chooserhead__you::after  { animation: none; width: 100%; }
}

/* --------------------------------------------------------------------------
   Homepage seams.

   The chooser is the FIRST thing under the nav, and the avatar hero follows it
   directly. Two full sections meeting means the chooser's bottom padding
   (~150px) stacks on the hero's top padding (~55px) — over 200px of empty
   canvas between two blocks that belong together, which reads as a page that
   failed to load something rather than as breathing room.

   Both sides of that seam are pulled in. The generous default spacing is kept
   everywhere further down the page, where sections genuinely are separate
   arguments and need the pause.
   -------------------------------------------------------------------------- */
.page-root .section--chooser {
  padding-top: clamp(var(--s-8), 5vh, var(--s-12));
  padding-bottom: clamp(var(--s-6), 3vh, var(--s-10));
}
.page-root .hero--deck {
  /* The hero sets its own padding-block from a different scale, so the
     --section-y override below does not reach it. Both ends are pinned here
     or the deck sits in a pocket of its own. */
  padding-top: clamp(var(--s-4), 2vh, var(--s-8));
  padding-bottom: clamp(var(--s-6), 3vh, var(--s-10));
}

/* --------------------------------------------------------------------------
   Homepage rhythm.

   `--section-y` is 10rem at desktop, and it is applied to BOTH sides of every
   section — so two consecutive sections put ~300px of empty canvas between
   them, and once a section's own trailing margin is added the worst seam on
   this page measured 530px. That spacing is right for the long editorial
   pages, where each section is a separate argument that needs a pause. The
   homepage is a single continuous pitch: split, faces, work, services. It
   wants a beat between blocks, not a chapter break.

   The global --section-y was retuned in tokens.css for the same reason, so the
   homepage no longer needs its own value — only the two overrides below, which
   handle the specific seams this page has that others do not.
   -------------------------------------------------------------------------- */
/* The closing band carries its own generous inner padding on top of the
   section's, which is what made the last block look like it was floating in
   the middle of an empty screen. */
.page-root .cta-band { padding: clamp(var(--s-8), 4vw, var(--s-12)) var(--s-6); }

/* The benefit line. Divided off and tinted so it reads as a different KIND of
   statement from the description above it — what it is, then what it does for
   you. Without the rule the two sentences blur into one paragraph and the
   second one stops landing. */
.svc__helps {
  margin-top: auto;
  padding-top: var(--s-3);
  border-top: 1px solid var(--surface-line);
  color: var(--accent-ink);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}
/* The box is a grid of contents, so the benefit needs a row of its own and the
   items list must not stretch into the gap. */
.svc { grid-template-rows: auto auto auto 1fr auto; }
