/* =========================================================================
   STAGE — the revamp layer. Every section below the hero now speaks the
   hero's and the pillars' language:

     · the sheet      near-black field, a vermilion light in one corner, the
                      plot grid and the tooth (the hero's own recipe, made
                      reusable as .field)
     · the lit card   border-subtle glass, a corner glow driven by --lit,
                      the first card lit until the pointer chooses another
     · the serif turn the second half of every headline in Instrument Serif
     · the proof line mono, uppercase, led by the circled arrow

   Motion here is all scroll-linked. scripts/scroll.js publishes --p (-1 →
   1 through the viewport), --v (0 → 1), --rail, --cover, --rx/--ry; this
   file is the only place they become movement. With reduced motion none of
   them is written, so every calc() below resolves to the resting state.

   Loads after path.css and before showcase.css.
   ========================================================================= */

/* ---- Shared: the circled arrow (pillars, board, rail) ------------------ */
.go {
  flex: none;
  width: 28px;
  height: 28px;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.go circle { stroke: var(--accent-line); }
.go path { stroke: var(--accent); }

/* ---- Shared: the serif turn in a section headline ----------------------
   The pillars set it: the second half of the title in the italic serif, at
   1.1em so its x-height meets the sans beside it. */
.sec-head__title em {
  display: block;
  font-weight: 400;
  letter-spacing: -0.01em;
  font-size: 1.1em;
  line-height: 1;
}
.sec-head__title .dot { color: var(--accent-ink); font-family: var(--font-display); font-style: normal; }

/* ---- Shared: the pen mark ----------------------------------------------
   One drawn underline under the few words a section turns on. It is used
   four times on the page, spaced apart (the sum, the loop, "and", the ask),
   plus once in each role's opening beat on the path (path.css), and not
   under every serif turn: a mark under everything marks nothing.
   Not on the hero, where it was tried and came off (see data.js).

   .pen carries data-reveal only as its trigger; it never fades itself. */
.pen {
  position: relative;
  display: inline-block;
  white-space: nowrap;
}
.pen::after {
  content: "";
  position: absolute;
  inset: auto -4% -0.14em -2%;
  height: 0.2em;
  background: var(--accent);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 14' preserveAspectRatio='none'%3E%3Cpath d='M3 10C42 4.5 92 3 132 5.5c24 1.5 44 1 65-2.5' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round'/%3E%3C/svg%3E") center / 100% 100% no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 14' preserveAspectRatio='none'%3E%3Cpath d='M3 10C42 4.5 92 3 132 5.5c24 1.5 44 1 65-2.5' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round'/%3E%3C/svg%3E") center / 100% 100% no-repeat;
}
.js .pen[data-reveal] { opacity: 1; transform: none; filter: none; transition: none; }
.js .pen[data-reveal]::after {
  clip-path: inset(0 100% 0 0);
  transition: clip-path 700ms var(--e-expo) 420ms;
}
.js .pen[data-reveal].is-in::after { clip-path: inset(0 0 0 0); }
@media (prefers-reduced-motion: reduce) {
  .js .pen[data-reveal]::after { clip-path: none; transition: none; }
}
/* In running text the mark sits closer and a little heavier. */
.sec-head__deck .pen { color: var(--ink); font-style: italic; }
.sec-head__deck .pen::after { inset: auto -8% -0.16em -8%; height: 0.26em; }

/* ---- Shared: parallax ---------------------------------------------------
   --depth is in vh at the edge of the viewport. Negative lags the page
   (background), positive leads it (foreground). The hero's collage keeps
   its pointer drift on `translate`; scroll drift is on `transform`, so the
   two compose instead of fighting. */
.hero__main,
.hero__art,
.field__bloom,
.value__sky,
.about__float,
.pillar__art {
  transform: translate3d(0, calc(var(--p, 0) * var(--depth, 0) * 1vh), 0);
}
/* The hero lifts off as you leave it: the copy lags and thins, so the
   board below arrives over it rather than after it. */
.hero__main { opacity: calc(1 - max(0, var(--p, 0) * -1) * 0.9); }

/* =========================================================================
   THE FIELD — the hero's sheet, reusable. Same layers, same alphas, same
   contrast reasoning as hero.css (read that before changing a value); only
   the corner the light comes from changes per section.
   ========================================================================= */
/* No z-index on the field, for the reason hero.css gives at length: a
   stacking context of its own leaves the tooth's `overlay` blending with
   nothing, which paints a grey slab. It stays in paint order under the
   content, and the section keeps `isolation`. */
.field {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: clip;
  --plot-minor: 24px;
  --plot-major: 96px;
  --plot-ink: color-mix(in srgb, var(--ink-meta) 26%, transparent);
  --plot-ink-soft: color-mix(in srgb, var(--ink-meta) 9%, transparent);
  --bloom-a: color-mix(in srgb, var(--accent) 30%, transparent);
  --bloom-b: color-mix(in srgb, var(--accent) 8%, transparent);
  --bloom-at: 99% 4%;
}
[data-theme="light"] .field {
  --plot-ink: color-mix(in srgb, var(--ink-meta) 20%, transparent);
  --plot-ink-soft: color-mix(in srgb, var(--ink-meta) 8%, transparent);
  --bloom-a: color-mix(in srgb, var(--accent) 11%, transparent);
  --bloom-b: color-mix(in srgb, var(--accent) 3%, transparent);
}
.field--end { --bloom-at: 99% 98%; }

.field__bloom {
  position: absolute;
  inset: -12% 0;
  background-image:
    radial-gradient(56% 62% at var(--bloom-at), var(--bloom-a) 0%, transparent 72%),
    radial-gradient(30% 36% at 18% 70%, var(--bloom-b) 0%, transparent 74%);
}
.field__plot {
  position: absolute;
  inset: 0;
  opacity: 0.4;
  background-image:
    linear-gradient(to right,  var(--plot-ink)      1px, transparent 1px),
    linear-gradient(to bottom, var(--plot-ink)      1px, transparent 1px),
    linear-gradient(to right,  var(--plot-ink-soft) 1px, transparent 1px),
    linear-gradient(to bottom, var(--plot-ink-soft) 1px, transparent 1px);
  background-size:
    var(--plot-major) var(--plot-major), var(--plot-major) var(--plot-major),
    var(--plot-minor) var(--plot-minor), var(--plot-minor) var(--plot-minor);
  -webkit-mask-image: radial-gradient(70% 70% at 70% 30%, #000 0%, transparent 72%);
          mask-image: radial-gradient(70% 70% at 70% 30%, #000 0%, transparent 72%);
}
.field--end .field__plot {
  -webkit-mask-image: radial-gradient(70% 80% at 60% 80%, #000 0%, transparent 72%);
          mask-image: radial-gradient(70% 80% at 60% 80%, #000 0%, transparent 72%);
}
.field__tooth {
  position: absolute;
  inset: 0;
  opacity: 0.5;
  mix-blend-mode: overlay;
  background-image: url("../assets/tooth.webp");
  background-size: 168px 168px;
}
[data-theme="light"] .field__tooth { mix-blend-mode: soft-light; opacity: 0.85; }
@media (max-width: 819px) { .field__tooth { opacity: 0.32; } }

/* =========================================================================
   02 · THE NUMBERS — the board.
   No boxes: the figures are the composition. A hairline over each, the
   label under it, the source on the proof line. The lead figure is the
   largest type on the page by design: it is the one thing a reader who
   gives the site ten seconds should leave with.
   ========================================================================= */
.numbers {
  isolation: isolate;
  overflow: clip;
  border-top: 0;
  padding-block: clamp(var(--s-8), 6vw, var(--s-10)) var(--section-y);
  background: var(--bg);
}
.numbers__inner { position: relative; z-index: 1; }
.numbers__title {
  max-width: none;
  margin-bottom: clamp(var(--s-7), 6vw, var(--s-9));
}
.numbers__title em { display: block; }

.board { display: grid; gap: clamp(var(--s-7), 5vw, var(--s-9)); }
.board__top,
.board__grid { display: grid; gap: clamp(var(--s-6), 4vw, var(--s-8)) clamp(var(--s-5), 3vw, var(--s-7)); }
@media (min-width: 1000px) {
  .board__top { grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); align-items: end; }
}
@media (min-width: 640px)  { .board__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1000px) { .board__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.tally {
  --lit: 0;
  position: relative;
  container-type: inline-size;
  display: grid;
  align-content: start;
  gap: var(--s-3);
  padding-top: var(--s-5);
  border-top: 1px solid var(--border-subtle);
  /* Each row drifts a few vw against the next as it crosses the screen,
     on `translate` so the reveal keeps `transform` to itself. */
  translate: calc(var(--p, 0) * var(--drift, 0) * 1vw) 0;
}
/* The light under a figure: the pillars' corner glow, as a line. */
.tally::before {
  content: "";
  position: absolute;
  top: -1px; left: 0;
  width: 100%;
  height: 1px;
  background: linear-gradient(to right, var(--accent), transparent 70%);
  transform-origin: left;
  transform: scaleX(var(--lit));
  transition: transform var(--d-enter) var(--e-expo);
}
.board:not(:has(.tally:hover)) .tally--lead,
.tally:hover { --lit: 1; }

.tally__fig {
  font-family: var(--font-display);
  /* Sized by its own column as well as the viewport, so the widest figure
     (12→16%, about 5.4em) can never run into the next one. */
  font-size: min(clamp(3.25rem, 1.6rem + 5.4vw, 7rem), 17cqi);
  font-weight: 600;
  line-height: 0.9;
  letter-spacing: -0.055em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--ink);
  /* Descenders of the serif arrow and the rupee sign need the room. */
  padding-bottom: 0.04em;
}
.tally--lead .tally__fig { font-size: min(clamp(5.5rem, 2rem + 15vw, 15rem), 44cqi); letter-spacing: -0.065em; }
.tally--side .tally__fig { font-size: min(clamp(3.75rem, 1.6rem + 6.4vw, 8.5rem), 21cqi); }

/* Everything that is not a digit steps back a shade; the digits are what
   count up, and what the eye should land on. */
.tally__fig .unit { display: none; }
.tally__fig .fig__to {
  font-family: var(--font-edit);
  font-weight: 400;
  font-style: italic;
  margin-inline: 0.04em;
  color: var(--accent-ink);
}
.tally__label {
  max-width: 34ch;
  color: var(--ink-secondary);
  font-size: var(--t-body);
  line-height: 1.45;
  text-wrap: pretty;
}
.tally--lead .tally__label { font-size: var(--t-lead); max-width: 30ch; }
.tally__source {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin-top: var(--s-2);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-meta);
}
.tally__source .go { width: 24px; height: 24px; }
.numbers__note {
  margin-top: clamp(var(--s-7), 5vw, var(--s-8));
  color: var(--ink-meta);
  font-size: var(--t-small);
  max-width: 60ch;
}

/* The lead figure is set by its own column on a phone. */
@media (max-width: 639px) {
  .tally--lead .tally__fig { font-size: min(clamp(5rem, 1rem + 30vw, 9rem), 44cqi); }
}

/* =========================================================================
   02b · THE LOOP — the pillars' night, carried on.
   One person, five stages, and the line that comes back. A night plate of
   its own, directly under the sky, so the two read as one dark band and
   the glass tiles always sit on the dark they were rendered for.

   Motion, all of it in service of "it comes back":
     · the stages arrive in order and each arrow draws to the next;
     · the return line then draws from Grow back to Ideate;
     · on scroll (--p on .loop__flow) the tiles settle into one line as the
       row reaches the middle of the screen, and a light travels the return
       line from right to left.
   ========================================================================= */
.loop {
  position: relative;
  isolation: isolate;
  overflow: clip;
  padding-block: clamp(var(--s-6), 5vw, var(--s-9)) calc(var(--section-y) * 0.75);
  background: var(--bg);
  color: var(--ink);
}
/* The plate restates the tokens, but `[data-theme="light"] .field` still
   matches inside it; these are the dark values again. The reference comp had
   blooms on every edge. Two is enough: any more and the tiles' own glow has
   nothing to stand out from. */
.loop.plate-night .field {
  --plot-ink: color-mix(in srgb, var(--ink-meta) 26%, transparent);
  --plot-ink-soft: color-mix(in srgb, var(--ink-meta) 9%, transparent);
}
.field--loop .field__bloom {
  background-image:
    radial-gradient(34% 54% at 0% 62%, color-mix(in srgb, var(--accent) 24%, transparent) 0%, transparent 72%),
    radial-gradient(30% 50% at 100% 44%, color-mix(in srgb, var(--accent) 17%, transparent) 0%, transparent 72%);
}
/* The plate ends on paper in the light theme and on the same night in the
   dark one; either way the light must be gone before the edge. */
.field--loop .field__bloom {
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 22%, #000 70%, transparent 96%);
          mask-image: linear-gradient(to bottom, transparent, #000 22%, #000 70%, transparent 96%);
}
.field--loop .field__plot {
  -webkit-mask-image: radial-gradient(60% 70% at 50% 55%, #000 0%, transparent 76%);
          mask-image: radial-gradient(60% 70% at 50% 55%, #000 0%, transparent 76%);
}
[data-theme="light"] .loop .field__tooth { mix-blend-mode: overlay; opacity: 0.5; }
@media (max-width: 819px) { [data-theme="light"] .loop .field__tooth { opacity: 0.32; } }

.loop__inner {
  position: relative;
  z-index: 1;
  display: grid;
  gap: clamp(var(--s-7), 5vw, var(--s-9));
}

/* ---- Head -------------------------------------------------------------- */
.loop__head { display: grid; gap: var(--s-5); }
.loop__title {
  font-family: var(--font-display);
  font-size: clamp(1.9rem, 1.2rem + 2.6vw, 3.5rem);
  font-weight: 500;
  line-height: 1.04;
  letter-spacing: var(--tr-tight);
  text-wrap: balance;
}
.loop__title em {
  font-family: var(--font-edit);
  font-style: italic;
  font-weight: 400;
  font-size: 1.12em;
  letter-spacing: -0.01em;
  color: var(--accent-ink);
}

.loop__body {
  max-width: 62ch;
  color: var(--ink-secondary);
  font-size: var(--t-lead);
  line-height: 1.4;
  text-wrap: pretty;
}
.loop__body b { color: var(--ink); font-weight: 500; }

/* ---- The five stages --------------------------------------------------- */
.loop__flow {
  --tile: clamp(3.5rem, 2rem + 7vw, 7.5rem);
  --arrow-gap: clamp(var(--s-2), 1.4vw, var(--s-5));
  display: grid;
  gap: var(--s-5);
  max-width: 1120px;
  width: 100%;
  margin-inline: auto;
}
.loop__steps {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  margin: 0; padding: 0;
  list-style: none;
}
.loop__step {
  position: relative;
  display: grid;
  justify-items: center;
  gap: var(--s-2);
  text-align: center;
}
.loop__tile {
  position: relative;
  width: var(--tile);
  /* Off-centre the row is a slope; at the centre of the screen it is a
     line. --i is 0…4, so the middle tile never moves. */
  translate: 0 calc(var(--p, 0) * (var(--i) - 2) * 9px);
  transition: scale var(--d-state) var(--e-out), filter var(--d-state) var(--e-soft);
}
.loop__tile::before {
  content: "";
  position: absolute;
  inset: 12% 0 -8%;
  z-index: -1;
  border-radius: 50%;
  background: radial-gradient(closest-side, color-mix(in srgb, var(--accent) 34%, transparent), transparent);
  filter: blur(14px);
  opacity: 0.55;
  transition: opacity var(--d-state) var(--e-soft);
}
.loop__tile img { display: block; width: 100%; height: auto; }
@media (hover: hover) {
  .loop__step:hover .loop__tile { scale: 1.07; filter: brightness(1.15); }
  .loop__step:hover .loop__tile::before { opacity: 1; }
}
.loop__name { color: var(--ink); font-size: 0.8125rem; letter-spacing: 0.18em; }

/* The arrow runs from this tile's edge to the next one's. */
.loop__arrow {
  position: absolute;
  top: calc(var(--tile) * 0.5);
  left: calc(50% + var(--tile) * 0.5 + var(--arrow-gap));
  width: calc(100% - var(--tile) - var(--arrow-gap) * 2);
  height: 1px;
  background: linear-gradient(to right, color-mix(in srgb, var(--accent) 35%, transparent), var(--accent));
  transform-origin: 0 50%;
}
.loop__arrow::after {
  content: "";
  position: absolute;
  right: 0; top: 50%;
  width: 7px; height: 7px;
  border-top: 1px solid var(--accent);
  border-right: 1px solid var(--accent);
  translate: 0 -50%;
  rotate: 45deg;
}
.js .loop__step .loop__arrow {
  scale: 0 1;
  opacity: 0;
  transition: scale 520ms var(--e-expo), opacity 200ms linear;
  transition-delay: calc(var(--reveal-delay, 0ms) + 260ms);
}
.js .loop__step.is-in .loop__arrow { scale: 1 1; opacity: 1; }
@media (max-width: 639px) {
  .loop__arrow { display: none; }
  .loop__name { font-size: var(--t-mono); letter-spacing: 0.1em; }
}

/* ---- The line that comes back ----------------------------------------- */
.loop__back {
  display: grid;
  justify-items: center;
  gap: var(--s-4);
  text-align: center;
}
.loop__return {
  position: relative;
  /* From the centre of the last column to the centre of the first. */
  width: 80%;
  height: 26px;
  border: 1px solid color-mix(in srgb, var(--accent) 80%, transparent);
  border-top: 0;
  border-radius: 0 0 var(--r-3) var(--r-3);
}
.loop__return::before {
  content: "";
  position: absolute;
  left: -4.5px; top: -1px;
  width: 8px; height: 8px;
  border-top: 1px solid var(--accent);
  border-left: 1px solid var(--accent);
  rotate: 45deg;
}
/* The light on the line. It is at Grow when the row enters and at Ideate
   when it leaves; with motion reduced --p is 0 and it rests in the middle. */
.loop__return::after {
  content: "";
  position: absolute;
  bottom: -5px;
  left: clamp(14%, calc((0.5 + var(--p, 0) * 1.3) * 100%), 86%);
  width: clamp(5rem, 14vw, 11rem);
  height: 9px;
  translate: -50% 0;
  background:
    radial-gradient(50% 1.5px at 50% 50%, #ffd9cc, transparent),
    radial-gradient(closest-side, color-mix(in srgb, var(--accent) 55%, transparent), transparent);
}
.js .loop__back .loop__return {
  clip-path: inset(-14px 0 -14px 100%);
  transition: clip-path 900ms var(--e-expo);
  transition-delay: calc(var(--reveal-delay, 0ms) + 120ms);
}
.js .loop__back.is-in .loop__return { clip-path: inset(-14px -14px -14px -14px); }

/* ---- What it rests on -------------------------------------------------- */
.loop__proofs {
  display: grid;
  gap: var(--s-3);
  margin: 0; padding: 0;
  list-style: none;
}
@media (min-width: 640px)  { .loop__proofs { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1200px) { .loop__proofs { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.loop__proof {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-4) var(--s-5) var(--s-4) var(--s-4);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-3);
  background: color-mix(in srgb, var(--bg-raised) 72%, transparent);
  color: var(--ink-secondary);
  font-size: var(--t-small);
  line-height: 1.35;
}
.loop__proof b {
  display: block;
  color: var(--ink);
  font-size: var(--t-body);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.loop__proof-icon {
  flex: none;
  width: 40px; height: 40px;
  stroke: var(--accent);
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.loop__proof-icon circle { stroke: var(--accent-line); stroke-width: 1; }

@media (prefers-reduced-motion: reduce) {
  .loop__tile { translate: none; transition: none; }
  .js .loop__back .loop__return { clip-path: none; transition: none; }
  .js .loop__step .loop__arrow { scale: 1 1; opacity: 1; transition: none; }
}

/* =========================================================================
   03 · THE PATH — the year behind the pinned stage.
   An outline numeral, enormous and quiet, drifting up as the role plays
   out. It is the only outlined type on the page besides the kinetic band.
   ========================================================================= */
.role__stage { position: sticky; isolation: isolate; }
.role__year {
  position: absolute;
  right: -0.04em;
  bottom: 4vh;
  z-index: -1;
  font-family: var(--font-display);
  font-size: clamp(8rem, 4rem + 18vw, 22rem);
  font-weight: 600;
  line-height: 0.8;
  letter-spacing: -0.06em;
  color: transparent;
  -webkit-text-stroke: 1px color-mix(in srgb, var(--ink-meta) 30%, transparent);
  pointer-events: none;
  user-select: none;
  transform: translate3d(0, calc((0.5 - var(--rp, 0.5)) * 18vh), 0);
}
.path-section .sec-head__title em { color: var(--ink); }
@media (max-width: 899px), (prefers-reduced-motion: reduce) {
  .role__stage { position: relative; }
  .role__year { bottom: auto; top: var(--s-6); font-size: clamp(5rem, 2rem + 16vw, 9rem); transform: none; }
}

/* =========================================================================
   04 · WORK — the rail.
   ========================================================================= */
.work { padding-inline: 0; }
.rail { position: relative; }
.rail__pin {
  display: grid;
  /* minmax(0, 1fr), not auto: an auto track grows to the flex row's full
     max-content width, the rail never overflows, and there is nothing to
     pan. */
  grid-template-columns: minmax(0, 1fr);
  align-content: center;
  gap: clamp(var(--s-5), 3vh, var(--s-7));
}
.rail__head {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: var(--s-5);
}
.rail__head .sec-head__title { max-width: 16ch; }
.rail__head .sec-head__title em { display: block; }
.rail__count { white-space: nowrap; padding-bottom: 0.6em; }
.rail__count span:first-child { color: var(--accent-ink); }

.rail__track {
  /* The first card lines up with the headline above it at every width. */
  --edge: max(var(--gutter), calc((100vw - var(--maxw)) / 2 + var(--gutter)));
  display: flex;
  gap: clamp(var(--s-4), 1.6vw, var(--s-5));
  padding-inline: var(--edge);
  padding-block: var(--s-3);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--edge);
  scrollbar-width: none;
}
.rail__track::-webkit-scrollbar { display: none; }
.rail__track:focus-visible { outline-offset: -4px; }

.rail__foot { display: flex; }
.rail__bar {
  position: relative;
  flex: 1;
  max-width: 22rem;
  height: 2px;
  border-radius: 2px;
  overflow: hidden;
  background: var(--border-subtle);
}
.rail__bar i {
  position: absolute;
  inset: 0;
  background: var(--accent);
  transform-origin: left;
  transform: scaleX(calc(0.08 + var(--rail, 0) * 0.92));
}

/* Pinned: the stage sticks and the track is moved by scroll.js. */
.rail.is-pinned .rail__pin {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  padding-top: 60px;           /* clears the fixed nav */
  /* clip, not hidden: a hidden box is still a scroll container, and
     tabbing to an off-stage card would scroll it sideways under the pan. */
  overflow: clip;
}
.rail.is-pinned .rail__track {
  overflow: visible;
  scroll-snap-type: none;
  will-change: transform;
}

/* ---- The case card ---------------------------------------------------- */
.case {
  --lit: 0;
  position: relative;
  flex: none;
  display: grid;
  grid-template-rows: auto 1fr;
  width: min(30rem, 82vw);
  scroll-snap-align: start;
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-3);
  overflow: hidden;
  color: inherit;
  text-decoration: none;
  background:
    radial-gradient(90% 60% at 0% 100%, color-mix(in srgb, var(--accent) calc(var(--lit) * 12%), transparent), transparent 70%),
    linear-gradient(to bottom, color-mix(in srgb, var(--ink) 3%, transparent), transparent 40%),
    color-mix(in srgb, var(--bg-raised) 82%, transparent);
  transition: border-color var(--d-state) var(--e-soft), box-shadow var(--d-state) var(--e-soft),
              translate var(--d-state) var(--e-out);
}
@media (min-width: 900px) and (min-height: 760px) { .case { width: min(34rem, 40vw); } }
.rail__track:not(:has(.case:hover)) .case.is-current,
a.case:hover,
a.case:focus-visible {
  --lit: 1;
  border-color: color-mix(in srgb, var(--accent) 70%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 18%, transparent),
              0 24px 60px -30px color-mix(in srgb, var(--accent) 45%, transparent);
}
a.case:hover { translate: 0 -4px; }

.case__shot {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--bg-card);
  border-bottom: 1px solid var(--border-subtle);
}
@media (min-width: 900px) and (max-height: 820px) { .case__shot { aspect-ratio: 16 / 8; } }
.case__shot img {
  position: absolute;
  inset: 0 -8%;
  width: 116%;
  max-width: none;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  transform: translate3d(calc(var(--sx, 0) * -7%), 0, 0) scale(1.001);
  transition: scale var(--d-enter) var(--e-expo);
}
a.case:hover .case__shot img { scale: 1.035; }
.case__pending {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background:
    linear-gradient(to right, color-mix(in srgb, var(--ink-meta) 10%, transparent) 1px, transparent 1px) 0 0 / 24px 24px,
    linear-gradient(to bottom, color-mix(in srgb, var(--ink-meta) 10%, transparent) 1px, transparent 1px) 0 0 / 24px 24px;
}
.case--planned { border-style: dashed; border-color: var(--border); }

.case__body {
  display: grid;
  align-content: start;
  gap: var(--s-3);
  padding: clamp(var(--s-4), 1.6vw, var(--s-5)) clamp(var(--s-4), 1.8vw, var(--s-6)) clamp(var(--s-4), 1.6vw, var(--s-5));
}
.case__idx { color: var(--ink-meta); transition: color var(--d-state) var(--e-soft); }
.rail__track:not(:has(.case:hover)) .case.is-current .case__idx,
a.case:hover .case__idx { color: var(--accent-ink); }
.case__title {
  font-family: var(--font-display);
  font-size: clamp(1.2rem, 1rem + 0.8vw, 1.6rem);
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: var(--tr-tight);
  text-wrap: balance;
}
.case__teaser {
  color: var(--ink-secondary);
  font-size: var(--t-small);
  line-height: 1.55;
  text-wrap: pretty;
}
.case__proof {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin-top: var(--s-2);
  padding-top: var(--s-4);
  border-top: 1px solid var(--border-subtle);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-secondary);
}
.case__proof .go { width: 24px; height: 24px; transition: translate var(--d-state) var(--e-out); }
a.case:hover .case__proof .go { translate: 4px 0; }
@media (min-width: 900px) and (max-height: 760px) { .case__teaser { display: none; } }

/* =========================================================================
   05 · PRODUCTS — a bento of lit cards that tilt toward the pointer.
   ========================================================================= */
.prods {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  perspective: 1200px;
}
@media (min-width: 1000px) { .prods { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.prod {
  --lit: 0;
  --gx: 30%;
  --gy: 100%;
  position: relative;
  display: grid;
  grid-template-rows: auto 1fr;
  min-width: 0;
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-3);
  overflow: hidden;
  background:
    radial-gradient(60% 60% at var(--gx) var(--gy), color-mix(in srgb, var(--accent) calc(var(--lit) * 14%), transparent), transparent 70%),
    linear-gradient(to bottom, color-mix(in srgb, var(--ink) 3%, transparent), transparent 40%),
    color-mix(in srgb, var(--bg-raised) 82%, transparent);
  transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transform-style: preserve-3d;
  transition: border-color var(--d-state) var(--e-soft), box-shadow var(--d-state) var(--e-soft),
              transform var(--d-enter) var(--e-expo);
}
.prod.is-tilting { transition: border-color var(--d-state) var(--e-soft), box-shadow var(--d-state) var(--e-soft), transform 120ms linear; }
.prods:not(:has(.prod:hover)) .prod:first-child,
.prod:hover {
  --lit: 1;
  border-color: color-mix(in srgb, var(--accent) 70%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 18%, transparent),
              0 24px 60px -30px color-mix(in srgb, var(--accent) 45%, transparent);
}
.prod--big { grid-column: span 2; }
@media (max-width: 559px) { .prods { grid-template-columns: minmax(0, 1fr); } .prod--big { grid-column: auto; } }

.prod__media {
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 9;
  padding: var(--s-4);
  border-bottom: 1px solid var(--border-subtle);
  background:
    radial-gradient(120% 90% at 20% 0%, color-mix(in srgb, var(--accent) 7%, transparent), transparent 60%),
    color-mix(in srgb, var(--bg) 55%, transparent);
}
.prod--big .prod__media { aspect-ratio: 16 / 8; }
.prod__media .gfx {
  width: auto;
  height: 100%;
  max-height: 9rem;
  color: var(--ink-secondary);
  transform: translateZ(30px);
}
.prod--big .prod__media .gfx { max-height: 13rem; }
.prod__copy {
  display: grid;
  align-content: start;
  gap: var(--s-3);
  padding: clamp(var(--s-4), 1.8vw, var(--s-6));
}
.prod__fig {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-4);
  font-family: var(--font-display);
  font-size: clamp(3rem, 1.4rem + 4vw, 5.5rem);
  font-weight: 600;
  line-height: 0.9;
  letter-spacing: -0.055em;
  margin-bottom: var(--s-2);
}
.prod__fig span {
  font-family: var(--font-edit);
  font-style: italic;
  font-weight: 400;
  font-size: var(--t-lead);
  letter-spacing: 0;
  color: var(--ink-secondary);
}
.prod__name {
  font-family: var(--font-display);
  font-size: var(--t-h3);
  font-weight: 500;
  line-height: var(--lh-snug);
  letter-spacing: var(--tr-tight);
}
.prod__body { color: var(--ink-secondary); font-size: var(--t-small); line-height: 1.55; text-wrap: pretty; }
.prod__meta { margin-top: var(--s-2); color: var(--ink-secondary); }

/* =========================================================================
   06 · PRACTICE — the sticky stack.
   ========================================================================= */
.stack { display: grid; gap: var(--s-5); }
.stack__card {
  --cover: 0;
  position: relative;
  display: grid;
  gap: clamp(var(--s-5), 3vw, var(--s-7));
  padding: clamp(var(--s-5), 3vw, var(--s-7));
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-3);
  background:
    radial-gradient(70% 90% at 0% 100%, color-mix(in srgb, var(--accent) 10%, transparent), transparent 70%),
    linear-gradient(to bottom, color-mix(in srgb, var(--ink) 3%, transparent), transparent 40%),
    var(--stack-surface, var(--bg-raised));
  box-shadow: 0 -24px 60px -40px rgba(0, 0, 0, 0.6);
  transform-origin: 50% 0;
}
/* On paper the raised tone is darker than the page, which took the accent
   number to 4.45:1. The card goes to the white card tone instead (5.6:1). */
[data-theme="light"] .stack__card {
  --stack-surface: var(--bg-card);
  box-shadow: 0 -18px 50px -38px rgba(13, 13, 12, 0.35);
}
/* The veil that settles a covered card back into the stack. */
.stack__card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--bg);
  opacity: calc(var(--cover) * 0.55);
  pointer-events: none;
}
@media (min-width: 900px) {
  .stack__card {
    grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
    align-items: center;
    min-height: min(34rem, calc(100svh - 9rem));
  }
}
@media (min-width: 900px) and (prefers-reduced-motion: no-preference) {
  .stack { gap: 12vh; padding-bottom: 6vh; }
  .stack__card {
    position: sticky;
    top: calc(84px + var(--i, 0) * 18px);
    transform: scale(calc(1 - var(--cover) * 0.05));
  }
}
.stack__copy { display: grid; align-content: start; gap: var(--s-4); }
.stack__num { color: var(--accent-ink); }
.stack__title {
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 1.1rem + 1.8vw, 2.75rem);
  font-weight: 500;
  line-height: 1.08;
  letter-spacing: -0.03em;
  max-width: 16ch;
  text-wrap: balance;
}
.stack__body {
  color: var(--ink-secondary);
  font-size: var(--t-body);
  line-height: 1.6;
  max-width: 40ch;
  text-wrap: pretty;
}
.stack__body b { color: var(--ink); font-weight: 500; }
.stack__media { min-width: 0; }
.stack__media .demo { margin: 0; }

.stack__shot { display: grid; gap: var(--s-3); color: inherit; text-decoration: none; }
.stack__frame {
  display: block;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-2);
  background: var(--bg-card);
}
.stack__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top left;
  transition: scale var(--d-enter) var(--e-expo);
}
.stack__shot:hover .stack__frame img { scale: 1.03; }
.stack__link { color: var(--ink-secondary); transition: color var(--d-micro) var(--e-soft); }
.stack__link span { color: var(--accent-ink); }
.stack__shot:hover .stack__link { color: var(--accent-ink); }

.stack__figs { display: grid; gap: var(--s-4); }
@media (min-width: 640px) { .stack__figs { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.stack__figs .craft-fig { background: color-mix(in srgb, var(--bg) 55%, transparent); }

.stack__lead { display: grid; gap: var(--s-6); }
.stack__lead .gfx { color: var(--ink); max-width: 26rem; }
.stack__stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-5); }
.stack__stats .metric__value {
  font-size: clamp(3rem, 1.4rem + 4vw, 5.5rem);
  font-weight: 600;
  letter-spacing: -0.055em;
}

/* =========================================================================
   KINETIC TYPE — two rows that slide against each other with the scroll.
   ========================================================================= */
.kinetic {
  display: grid;
  gap: var(--s-2);
  padding-block: clamp(var(--s-7), 6vw, var(--s-9));
  overflow: clip;
  border-top: 1px solid var(--border-subtle);
  border-bottom: 1px solid var(--border-subtle);
  user-select: none;
}
.kinetic__row {
  display: flex;
  width: max-content;
  /* Rows start offset so both directions have room to travel. */
  transform: translate3d(calc(-25% + var(--p, 0) * var(--dir, 1) * 22vw), 0, 0);
}
.kinetic__set { display: flex; }
.kinetic__w {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 1rem + 6vw, 7rem);
  font-weight: 600;
  line-height: 1.02;
  letter-spacing: -0.045em;
  white-space: nowrap;
  color: var(--ink);
}
.kinetic__w::after {
  content: "";
  width: 0.14em;
  height: 0.14em;
  margin-inline: 0.36em;
  border-radius: 50%;
  background: var(--accent);
}
.kinetic__w.is-outline {
  color: transparent;
  -webkit-text-stroke: 1.5px var(--ink-meta);
  font-family: var(--font-edit);
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.01em;
  -webkit-text-stroke-width: 1px;
}
@media (prefers-reduced-motion: reduce) {
  .kinetic__row { transform: none; width: auto; flex-wrap: wrap; justify-content: center; }
  .kinetic__set[aria-hidden] { display: none; }
  .kinetic__set { flex-wrap: wrap; justify-content: center; }
  .kinetic__w { font-size: clamp(1.75rem, 1rem + 3vw, 3.5rem); }
}

/* =========================================================================
   09 · ABOUT — the motto lights word by word as it crosses the screen.
   --v runs 0 → 1 over the whole crossing; the words fill between 0.25 and
   0.6 of it, so the line is complete while it is still near the middle.
   ========================================================================= */
.about__float { display: block; }
.motto { border-top: 1px solid var(--border-subtle); }
.motto__line { font-size: clamp(2.25rem, 1rem + 5.4vw, 6rem); max-width: 18ch; text-wrap: balance; }
.motto__line .w {
  opacity: clamp(0.2, calc((var(--v, 1) - 0.25) / 0.35 * var(--n, 6) - var(--i, 0) + 1), 1);
  transition: opacity 120ms linear;
}
@media (prefers-reduced-motion: reduce) { .motto__line .w { opacity: 1; } }

/* =========================================================================
   10 · CONTACT — the bookend sheet.
   ========================================================================= */
.contact {
  isolation: isolate;
  overflow: clip;
  padding-block: var(--section-y);
  background: var(--bg);
}
.contact__inner { position: relative; z-index: 1; }
.contact .sec-head__title { max-width: 14ch; }
.contact .sec-head__title em { display: block; }
.contact__big {
  /* Sized so the whole address holds one line on a 375px phone (it is about
     10.6em wide at this weight); breaking an email address mid-word reads
     as a typo. */
  font-size: clamp(1.5rem, 8.2vw, 6rem);
  font-weight: 600;
  letter-spacing: -0.05em;
  word-break: normal;
  white-space: nowrap;
}

/* ---- Reduced motion: every scroll-linked transform at rest ------------- */
@media (prefers-reduced-motion: reduce) {
  .hero__main, .hero__art, .field__bloom, .value__sky, .about__float, .pillar__art,
  .role__year, .case__shot img, .prod { transform: none; }
  .hero__main { opacity: 1; }
  .tally { translate: none; }
}

/* ---- Path: the jump to the rail ---------------------------------------- */
.beat__jump {
  justify-self: start;
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  padding-bottom: 2px;
  color: var(--ink-secondary);
  text-decoration: none;
  border-bottom: 1px solid var(--border);
  transition: color var(--d-micro) var(--e-soft), border-color var(--d-micro) var(--e-soft);
}
.beat__jump .mono { color: inherit; }
.beat__jump:hover,
.beat__jump:focus-visible { color: var(--accent-ink); border-bottom-color: var(--accent); }

/* ---- Short laptops: a pinned card must fit under its sticky top -------- */
@media (min-width: 900px) and (max-height: 820px) {
  .stack__card { padding: var(--s-5) var(--s-6); }
  .stack__frame { aspect-ratio: 16 / 8; }
  .stack__figs .craft-fig { padding: var(--s-4); }
}
