@import '../tokens.css?v=a555c920';

/* ── The story — shared structure for the editorial long-reads ──────────
 * One page in place of a case study + showcase pair, built on the
 * homepage's work-index language (css/page/home-work-list.css) rather
 * than the case-study article: a cover; then spreads — a sticky,
 * viewport-tall spine on the left carrying the current chapter's title,
 * a chapter counter and the studio's discipline list (inked per
 * chapter), beside a flowing column of display-scale statements, short
 * copy, a spec list, three big numbers, viewport-tall plates and a few
 * borrowed case-study components; full-bleed pinned performances and
 * interludes between the spreads; a photographic close.
 *
 * This file is the structure every story page shares. Each story adds
 * its own extension (css/page/<slug>-story.css) for what is only its
 * own: the cover's treatment and its mark, the performances, and any
 * specimen of the product. Anything two stories both need lives here,
 * never in both extensions — the same rule as the showcase decks.
 *
 * Pages: ux-case-studies/rafa-nicky.html (+ rafa-nicky-story.css),
 *        ux-case-studies/artpilot.html (+ artpilot-story.css),
 *        ux-case-studies/no-road-block.html (+ no-road-block-story.css).
 * Driver: js/page/story.js (chapter lighting with the scramble, header
 * ink over [data-st-dark] planes, the numbers' count-up).
 *
 * Everything sits on the tokens and the ten type roles. White over
 * media is --color-text-on-image (theme-agnostic); the dark plane is
 * --color-plane-dark. Project colours never enter this file — an
 * extension sets the few custom-property hooks below (--st-plate-ground,
 * --st-ground, --st-ground-ink) from css/page/project-colours.css.
 * Components borrowed from case-study.css keep their own styles; the
 * extensions only place them. */

/* ── Chrome ─────────────────────────────────────────────────────────── */
/* Over the cover and every pinned dark plane ([data-st-dark]) the header
 * floats transparent (styles.css .is-over-hero, toggled by the driver)
 * and takes white ink through the same hook the HELLO hero uses. */
.story-page .header {
  --hero-active-fg: var(--color-text-on-image);
}

/* ── Cover ──────────────────────────────────────────────────────────── */
/* Full bleed, eating .main's top padding the way the peel stack does. */
.st-cover {
  position: relative;
  height: 100dvh;
  margin-inline: calc(-1 * var(--page-gutter));
  margin-top: calc(-1 * var(--space-32));
  overflow: hidden;
  isolation: isolate;
  background: var(--color-plane-dark);
  color: var(--color-text-on-image);
}

.st-cover-media {
  position: absolute;
  inset: 0;
}

.st-cover-media img,
.st-cover-media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--pos, 50% 15%);
  display: block;
  /* Scroll-scrubbed: a slow push-in across the first viewport — the cue
   * that the page scrolls. Off the duration scale (it is a scroll
   * timeline, not a timed transition). Engines without scroll-timeline
   * support land on the start frame. */
  animation: st-cover-zoom linear both;
  animation-timeline: scroll(root);
  animation-range: 0 100dvh;
}

@keyframes st-cover-zoom {
  to { scale: 1.08; }
}

.st-cover-inner {
  position: relative;
  z-index: 2;
  height: 100%;
  display: grid;
  grid-template-rows: 1fr auto;
  padding: var(--page-gutter);
}

/* The brand's one gesture is the h1; the extension sizes its mark. */
.st-cover-title {
  align-self: center;
  justify-self: center;
  margin: 0;
}

.st-cover-title svg {
  display: block;
  height: auto;
  fill: var(--color-text-on-image);
}

.st-cover-foot {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 5fr);
  column-gap: var(--space-10);
  align-items: end;
}

.st-cover-eyebrow,
.st-cover-client {
  margin: 0;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  font-weight: var(--font-weight-caption);
  color: var(--color-text-on-image);
}

.st-cover-client {
  opacity: 0.7;
  margin-top: var(--space-4);
}

.st-cover-line {
  margin: 0;
  max-width: 34ch;
  text-wrap: balance;
  font-size: var(--font-size-display-s);
  line-height: var(--line-height-display-s);
  letter-spacing: var(--letter-spacing-display-s);
  font-weight: var(--font-weight-display-s);
  color: var(--color-text-on-image);
}

/* ── Spread: sticky spine + flowing column ──────────────────────────── */
.st-spread {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 5fr);
  column-gap: var(--space-10);
  align-items: start;
}

.st-spine {
  position: sticky;
  top: 0;
  height: 100dvh;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: var(--space-10);
  padding-bottom: var(--page-gutter);
}

.st-spine-num {
  margin: 0;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  font-weight: var(--font-weight-caption);
  color: var(--color-text-tertiary);
  font-variant-numeric: tabular-nums;
}

/* Every title occupies the same grid cell, so the block is as tall as
 * the longest one and nothing reflows as they crossfade. */
.st-spine-titles {
  display: grid;
  margin: 0;
  margin-top: calc(-1 * var(--space-6));
}

.st-spine-title {
  grid-area: 1 / 1;
  align-self: end;
  margin: 0;
  font-size: var(--font-size-display-l);
  line-height: var(--line-height-display-l);
  letter-spacing: var(--letter-spacing-display-l);
  font-weight: var(--font-weight-display-l);
  color: var(--color-text-heading);
  opacity: 0;
  transition: opacity var(--duration-medium) var(--ease-smooth);
}

.st-spine-title.is-active {
  opacity: 1;
}

.st-spine-skills {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--space-8);
}

.st-spine-skills ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* Ghosted by opacity rather than a lighter token so the list reads the
 * same way on the dark surface — always the primary ink, mostly gone. */
.st-spine-skill {
  font-size: var(--font-size-body-large);
  line-height: var(--line-height-body-large);
  letter-spacing: var(--letter-spacing-body-large);
  font-weight: var(--font-weight-body-large);
  color: var(--color-text-primary);
  opacity: 0.14;
  white-space: nowrap;
  transition: opacity var(--duration-medium) var(--ease-smooth);
}

.st-spine-skill.is-active {
  opacity: 1;
}

.st-spine-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-8);
}

/* ── The flowing column ─────────────────────────────────────────────── */
.st-flow {
  min-width: 0;
}

.st-chapter {
  padding-top: var(--space-24);
  padding-bottom: var(--space-24);
}

.st-chapter + .st-chapter {
  padding-top: var(--space-40);
}

.st-chapter > * + * {
  margin-top: var(--space-16);
}

.st-statement + .st-copy,
.st-statement + .st-spec {
  margin-top: var(--space-8);
}

/* The hidden chapter heading takes no rhythm on the two-column layout. */
.st-chapter-title + * {
  margin-top: 0;
}

/* The chapter's own heading exists for the outline and for phones; on
 * the two-column layout the spine speaks for it. */
.st-chapter-title {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.st-statement {
  /* Block-start margin comes from the chapter rhythm above. */
  margin-inline: 0;
  margin-block-end: 0;
  max-width: 20ch;
  text-wrap: balance;
  font-size: var(--font-size-display-l);
  line-height: var(--line-height-display-l);
  letter-spacing: var(--letter-spacing-display-l);
  font-weight: var(--font-weight-display-l);
  color: var(--color-text-heading);
}

.st-statement--s {
  max-width: 30ch;
  font-size: var(--font-size-display-s);
  line-height: var(--line-height-display-s);
  letter-spacing: var(--letter-spacing-display-s);
  font-weight: var(--font-weight-display-s);
}

.st-copy {
  max-width: 58ch;
}

.st-copy p {
  margin: 0;
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  font-weight: var(--font-weight-body);
  color: var(--color-text-primary);
}

.st-copy p + p {
  margin-top: var(--space-5);
}

.st-copy a {
  color: var(--color-text-link);
}

/* ── Plates — viewport-tall media rows, the homepage's row geometry ─── */
.st-plate {
  --st-row-h: 100dvh;
  height: var(--st-row-h);
  box-sizing: border-box;
  padding-block: var(--page-gutter);
}

.st-plate-media {
  position: relative;
  height: 100%;
  overflow: hidden;
  border-radius: var(--radius-sharp);
  background: var(--color-surface-tertiary);
}

.st-plate-media img,
.st-plate-media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--pos, 50% 30%);
  display: block;
}

/* Screens are not viewport-tall: a recording keeps its own frame on
 * the product's ground (--st-plate-ground, set by the extension from the
 * project's palette); a render sits on the dark plane with breathing
 * room all round. Both contained, so the whole frame reads. */
.st-plate--paper,
.st-plate--dark {
  height: auto;
  padding-block: 0;
}

.st-plate--paper .st-plate-media {
  aspect-ratio: 16 / 10;
  height: auto;
  background: var(--st-plate-ground, var(--color-surface-secondary));
}

.st-plate--paper .st-plate-media img,
.st-plate--paper .st-plate-media video {
  object-fit: contain;
}

.st-plate--dark .st-plate-media {
  aspect-ratio: 16 / 10;
  height: auto;
  background: var(--color-plane-dark);
  padding: var(--space-12);
  box-sizing: border-box;
}

.st-plate--dark .st-plate-media img,
.st-plate--dark .st-plate-media video {
  object-fit: contain;
}

/* A spread of two portraits — not viewport-tall; each frame keeps a
 * 4:5 window so faces are never cropped to a sliver. */
.st-plate--pair {
  height: auto;
  padding-block: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-6);
}

.st-plate--pair .st-plate-media {
  aspect-ratio: 4 / 5;
  height: auto;
}

/* A plate that can be enlarged: the card-hover scale from styles.css,
 * on the plate's media, for a [data-lightbox] plate. */
.st-plate[data-lightbox] .st-plate-media img,
.st-plate[data-lightbox] .st-plate-media video {
  transition: transform var(--duration-luxury) var(--ease-smooth);
}

.st-plate[data-lightbox]:hover .st-plate-media img,
.st-plate[data-lightbox]:hover .st-plate-media video {
  transform: scale(1.02);
}

.st-plate-cap {
  margin: var(--space-3) 0 0;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  font-weight: var(--font-weight-caption);
  color: var(--color-text-tertiary);
}

/* Under the viewport-tall plate the caption pulls up into the plate's
 * own block padding; every modifier plate drops that padding, so its
 * caption keeps the plain gap. */
.st-plate + .st-plate-cap {
  margin-top: var(--space-3);
}

.st-plate:not([class*="st-plate--"]) + .st-plate-cap {
  margin-top: calc(var(--space-3) - var(--page-gutter));
}

/* ── Spec — the project's facts, a quiet two-column list ────────────── */
.st-spec {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  column-gap: var(--space-10);
  row-gap: var(--space-2);
  margin-inline: 0;
  margin-block-end: 0;
  max-width: 58ch;
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-border-default);
}

.st-spec dt {
  font-size: var(--font-size-caption);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-caption);
  font-weight: var(--font-weight-caption);
  color: var(--color-text-tertiary);
}

.st-spec dd {
  margin: 0;
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  font-weight: var(--font-weight-body);
  color: var(--color-text-primary);
}

/* ── Numbers — three magnitudes at display scale, no cards ──────────── */
.st-numbers {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: var(--space-8);
}

.st-number {
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border-default);
}

.st-number-value {
  display: block;
  font-size: var(--font-size-display-l);
  line-height: var(--line-height-display-l);
  letter-spacing: var(--letter-spacing-display-l);
  font-weight: var(--font-weight-display-l);
  color: var(--color-text-heading);
  font-variant-numeric: tabular-nums;
}

.st-number-label {
  display: block;
  margin-top: var(--space-2);
  max-width: 26ch;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  font-weight: var(--font-weight-caption);
  color: var(--color-text-tertiary);
}

/* ── Numbered list — decisions, reflections ─────────────────────────── */
.st-list {
  list-style: none;
  margin-inline: 0;
  margin-block-end: 0;
  padding: 0;
  max-width: 58ch;
  counter-reset: st-list;
}

.st-list li {
  counter-increment: st-list;
  display: grid;
  grid-template-columns: var(--space-10) minmax(0, 1fr);
  padding-block: var(--space-4);
  border-top: 1px solid var(--color-border-subtle);
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  font-weight: var(--font-weight-body);
  color: var(--color-text-primary);
}

.st-list li:last-child {
  border-bottom: 1px solid var(--color-border-subtle);
}

.st-list li::before {
  content: counter(st-list, decimal-leading-zero);
  font-size: var(--font-size-caption);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-caption);
  font-weight: var(--font-weight-caption);
  color: var(--color-text-tertiary);
  font-variant-numeric: tabular-nums;
}

/* ── Borrowed components — the caption under them, the CTA row ──────── */
.st-component-cap {
  margin: var(--space-3) 0 0;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  font-weight: var(--font-weight-caption);
  color: var(--color-text-tertiary);
}

.st-chapter > * + .st-component-cap {
  margin-top: var(--space-3);
}

.st-cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.st-credits {
  margin-inline: 0;
  margin-block-end: 0;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  font-weight: var(--font-weight-caption);
  color: var(--color-text-tertiary);
}

/* ── Cover entrances — the mark's letters and badges ────────────────────
 * From the decks: letters cascade in reading order out of a soft blur
 * (--i orders the wave), then a badge rubber-stamps in on its --bd delay
 * — both inline custom-property hooks on the glyphs. The extension sets
 * the delays and the stagger. The blur is a depth cue (an entrance), the
 * sanctioned exception. */
@keyframes st-letter-enter {
  from {
    opacity: 0;
    transform: translateY(14%) scale(0.92);
    filter: blur(8px);
  }
  60% {
    filter: blur(0);
  }
  to {
    opacity: 1;
    transform: none;
    filter: blur(0);
  }
}

@keyframes st-badge-stamp {
  from {
    opacity: 0;
    transform: scale(1.45) rotate(-8deg);
  }
  to {
    opacity: 1;
    transform: scale(1) rotate(0deg);
  }
}

/* ── Performances — full-bleed planes pinned on their own view timeline ─
 * A performance is a slot taller than the viewport holding a sticky,
 * viewport-tall plane. The slot publishes a named view timeline
 * (--st-perf, scoped to its subtree, so every performance on a page can
 * use the same name); `contain` = the slot fully contains the scrollport
 * = the pinned dwell (slot height minus 100dvh of scroll). The plane's
 * parts ride that timeline — either directly (the extension's own
 * keyframes on --st-perf) or as beats: any [data-beat] arrives over its
 * own window on the dwell, --a to --b (start and end, in % of the
 * dwell), two inline custom-property hooks the way the stat cards carry
 * --p. The extension may give a beat another animation-name.
 *
 * Base styles are the END state and keyframes start from the beginning,
 * so an engine without scroll timelines shows the finished composition.
 * Reduced motion unpins the plane and parks it on that end state; so
 * does an engine without scroll timelines (nothing to scrub — the pin
 * would only hold a still). */
.st-perf-slot {
  height: 250dvh;
  margin-inline: calc(-1 * var(--page-gutter));
  margin-block: var(--space-24);
  view-timeline: --st-perf block;
}

.st-perf {
  position: sticky;
  top: 0;
  height: 100dvh;
  overflow: hidden;
  background: var(--color-plane-dark);
  color: var(--color-text-on-image);
}

.st-perf [data-beat] {
  animation-name: st-arrive;
  animation-timing-function: linear;
  animation-fill-mode: both;
  animation-timeline: --st-perf;
  animation-range: contain calc(var(--a) * 1%) contain calc(var(--b) * 1%);
}

@keyframes st-arrive {
  from { opacity: 0; translate: 0 var(--space-4); }
  to { opacity: 1; translate: 0 0; }
}

/* A beat leaves before the next takes its place (phones, mostly). */
@keyframes st-leave {
  from { opacity: 1; translate: 0 0; }
  to { opacity: 0; translate: 0 calc(-1 * var(--space-4)); }
}

/* A window rises onto the plane — the product, shown on the dark stage. */
@keyframes st-rise {
  from { opacity: 0; translate: 0 40%; }
  to { opacity: 1; translate: 0 0; }
}

/* A tap: a fingertip's worth of shade on the thing pressed, pulsing once
 * in its window (the extension draws the fingertip). */
@keyframes st-tap {
  from { opacity: 0; scale: 1.5; }
  45% { opacity: 1; scale: 1; }
  to { opacity: 0; scale: 0.9; }
}

/* A button press: dips once in its window. */
@keyframes st-press {
  from { scale: 1; opacity: 1; }
  50% { scale: 0.97; opacity: 0.8; }
  to { scale: 1; opacity: 1; }
}

/* A line types itself: a left-to-right wipe. */
@keyframes st-type {
  from { clip-path: inset(0 100% 0 0); }
  to { clip-path: inset(0 0 0 0); }
}

/* The statement on a plane: white ink, bottom-left on the page's gutter. */
.st-perf-text {
  position: absolute;
  left: var(--page-gutter);
  right: var(--page-gutter);
  bottom: var(--page-gutter);
  z-index: 1;
  margin: 0;
  max-width: 22ch;
  font-size: var(--font-size-display-l);
  line-height: var(--line-height-display-l);
  letter-spacing: var(--letter-spacing-display-l);
  font-weight: var(--font-weight-display-l);
  color: var(--color-text-on-image);
}

.st-perf-text small {
  display: block;
  margin-top: var(--space-3);
  max-width: 44ch;
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  font-weight: var(--font-weight-body);
}

/* ── The stage — a performance's artwork, with headroom ───────────────
 * The deck's recipe: the header band above, a deeper band below so the
 * staged artwork clears the statement on the gutter. */
.st-perf-stage {
  position: absolute;
  inset: 0;
  padding: var(--space-16) var(--page-gutter) calc(var(--page-gutter) + var(--space-40));
}

/* ── The specimen phone — the product, rebuilt on the page's face ─────
 * A walkthrough performs the product itself, USED, so the app is a
 * specimen: its own inks and its own sizes, rebuilt in HTML. Every size
 * inside it is a multiple of --u, the specimen unit (one point of the
 * 390 × 844 frame, scaled to the stage's height with a container-query
 * unit), so the phone fits any plane and phones alike. The extension
 * sets the inks, the face and the screens; this is the frame. */
.st-phone-stage {
  container-type: size;
  display: grid;
  place-items: center;
}

.st-phone {
  /* The specimen unit: one point of the 390 × 844 frame. */
  --u: calc(100cqh / 844);
  position: relative;
  width: calc(390 * var(--u));
  height: 100cqh;
  overflow: hidden;
  border-radius: calc(8 * var(--u)); /* specimen: a screen depicted */
  line-height: 1.35;
  letter-spacing: 0;
  word-spacing: normal;
}

.st-phone p {
  margin: 0;
}

/* ── The numbers — each result takes the stage, then takes its place ──
 * Ported from the Artpilot deck, re-staged for the page. Each result
 * arrives alone at the centre of the plane at display-art scale — the
 * deck's solo figure — holds, and then PARKS into its own column as the
 * next one takes the stage. The hero result holds the centre column, so
 * it arrives last and only settles into scale. The statement lands last.
 * So the plane finishes composed with all three, which is also the frame
 * an engine without scroll timelines and reduced motion land on.
 *
 * Each .st-metric carries its windows as inline custom-property hooks
 * (the way the beats carry --a/--b): --a/--b the arrival, --c/--d the
 * park; .st-metric--left / --right say which column it parks into (the
 * unmodified metric is the centre); --st-to is the figure it counts to,
 * --st-suffix what follows it. The values themselves are never scrubbed:
 * the metric's scrubbed flag (--st-on, flipping in a hair-thin range as
 * it arrives) starts a TIME-based count (--duration-luxury) through a
 * style query, so the figure always completes to the true number even
 * if the reader stops scrolling mid-beat; a paused frame can never read
 * as a fake low score. The plane's ground is the extension's
 * (--st-nums-ground, from the project's palette); inks are the one
 * white, dimmed by mixing. On phones the three simply arrive, stacked. */
@property --st-num {
  syntax: '<integer>';
  initial-value: 0;
  inherits: false;
}

/* Inherits: the stage flag must reach a marker nested inside a scale
 * rule — style queries resolve against the nearest ancestor. */
@property --st-on {
  syntax: '<integer>';
  initial-value: 0;
  inherits: true;
}

.st-nums {
  background: var(--st-nums-ground, var(--color-plane-dark));
}

.st-nums-stage {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: var(--space-8);
  align-items: center;
}

/* Each metric in its own column; base: parked. --dx is the column's
 * offset from the plane's centre — the journey each result makes when
 * it parks (translate percentages are of the column's own width). */
.st-metric {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-4);
  --dx: 0px;
  animation: st-m-in linear both, st-park var(--ease-smooth) both, st-m-flag linear both;
  animation-timeline: --st-perf, --st-perf, --st-perf;
  animation-range:
    contain calc(var(--a) * 1%) contain calc(var(--b) * 1%),
    contain calc(var(--c) * 1%) contain calc(var(--d) * 1%),
    contain calc(var(--a) * 1%) contain calc((var(--a) + 0.5) * 1%);
}

.st-metric--left { --dx: calc(100% + var(--space-8)); }
.st-metric--right { --dx: calc(-100% - var(--space-8)); }

/* The result on stage sits at the plane's centre; parking is the
 * translate back to its column while the figure steps down to the
 * column's size (font-size, so the label follows it down). */
@keyframes st-park {
  from { translate: var(--dx) 0; }
  to { translate: 0 0; }
}

/* Display-art scale on stage (the deck's solo figure), a step under it
 * parked, so three fit across the plane. */
@keyframes st-value-park {
  from { font-size: clamp(88px, 13vw, 188px); }
  to { font-size: clamp(56px, 8vw, 120px); }
}

@keyframes st-m-in {
  from {
    opacity: 0;
    transform: translateY(0.45em);
    filter: blur(4px);
  }
  to {
    opacity: 1;
    transform: none;
    filter: blur(0);
  }
}

@keyframes st-m-flag {
  from { --st-on: 0; }
  to { --st-on: 1; }
}

/* The counts: TIME-based, started by the stage flag, always running to
 * completion regardless of scroll. Scrubbing back re-arms them. They
 * run on the figure's inner span (the value itself carries the park). */
@container style(--st-on: 1) {
  .st-metric-num {
    animation: st-count var(--duration-luxury) var(--ease-out-cubic) both;
  }
}

@keyframes st-count {
  to { --st-num: var(--st-to); }
}

.st-metric-key {
  font-size: var(--font-size-small);
  line-height: var(--line-height-small);
  letter-spacing: var(--letter-spacing-small);
  font-weight: var(--font-weight-small);
  color: color-mix(in srgb, var(--color-text-on-image) 70%, transparent);
}

/* Display-art scale, parked: a step under the deck's solo figure so
 * three fit across the plane with the stage figure clear of them. The
 * park (the step down) rides the metric's --c/--d window. */
.st-metric-value {
  font-size: clamp(56px, 8vw, 120px);
  line-height: 1;
  letter-spacing: -0.02em;
  font-weight: var(--font-book);
  color: var(--color-text-on-image);
  font-variant-numeric: tabular-nums;
  animation: st-value-park var(--ease-smooth) both;
  animation-timeline: --st-perf;
  animation-range: contain calc(var(--c) * 1%) contain calc(var(--d) * 1%);
}

.st-metric-num {
  counter-reset: stn var(--st-num);
}

.st-metric-num::after {
  content: counter(stn) var(--st-suffix, "");
}

/* A scale under a figure — the rule, its endpoint ticks, a reference
 * tick with its caption, and a travelling marker (the stat cards' 9px
 * ticks and 5px marker, the sanctioned optical pixels). The extension
 * places the tick and the marker's travel. The extra bottom margin keeps
 * the column label clear of the tick caption. */
.st-metric-scale {
  position: relative;
  width: min(100%, 460px);
  height: 0;
  border-top: 1px solid color-mix(in srgb, var(--color-text-on-image) 40%, transparent);
  margin-bottom: var(--space-8);
}

.st-metric-scale::before,
.st-metric-scale::after {
  content: "";
  position: absolute;
  top: -5px;
  width: 1px;
  height: 9px;
  background: color-mix(in srgb, var(--color-text-on-image) 40%, transparent);
}

.st-metric-scale::before { left: 0; }
.st-metric-scale::after { right: 0; }

.st-metric-ref {
  position: absolute;
  top: -5px;
  width: 1px;
  height: 9px;
  background: color-mix(in srgb, var(--color-text-on-image) 80%, transparent);
}

.st-metric-ref::before {
  content: attr(data-label);
  position: absolute;
  top: calc(100% + var(--space-1));
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  font-weight: var(--font-weight-caption);
  color: color-mix(in srgb, var(--color-text-on-image) 85%, transparent);
}

.st-metric-marker {
  position: absolute;
  top: -3px;
  left: 0;
  width: 5px;
  height: 5px;
  background: var(--color-text-on-image);
}

.st-metric-label {
  max-width: 22ch;
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  font-weight: var(--font-weight-body);
  color: color-mix(in srgb, var(--color-text-on-image) 80%, transparent);
  text-wrap: balance;
}

/* ── Interlude — a full-bleed, viewport-tall plate between spreads ──── */
/* Its ground and ink are the extension's (--st-ground / --st-ground-ink,
 * from the project's palette); the dark plane and white ink otherwise. */
.st-interlude {
  position: relative;
  height: 100dvh;
  margin-inline: calc(-1 * var(--page-gutter));
  margin-block: var(--space-24);
  overflow: hidden;
  background: var(--st-ground, var(--color-plane-dark));
}

.st-interlude video,
.st-interlude img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* The media runs under the statement, so a wash in the ground colour
 * rises from the bottom edge (a scrim over media, the sanctioned
 * gradient) and the statement sits on it in the ground's ink —
 * media-locked both, so the interlude reads the same in either theme. */
.st-interlude::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 45%;
  background: linear-gradient(to top, var(--st-ground, var(--color-plane-dark)) 30%, transparent);
  pointer-events: none;
}

.st-interlude-text {
  position: absolute;
  left: var(--page-gutter);
  right: var(--page-gutter);
  bottom: var(--page-gutter);
  z-index: 1;
  margin: 0;
  max-width: 22ch;
  font-size: var(--font-size-display-l);
  line-height: var(--line-height-display-l);
  letter-spacing: var(--letter-spacing-display-l);
  font-weight: var(--font-weight-display-l);
  color: var(--st-ground-ink, var(--color-text-on-image));
}

.st-interlude-text small {
  display: block;
  margin-top: var(--space-3);
  max-width: 44ch;
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  font-weight: var(--font-weight-body);
}

/* ── Close — a photograph, the ask over it ──────────────────────────── */
.st-close {
  position: relative;
  height: 100dvh;
  margin-inline: calc(-1 * var(--page-gutter));
  margin-top: var(--space-24);
  margin-bottom: calc(-1 * var(--space-20));
  overflow: hidden;
  background: var(--color-plane-dark);
  color: var(--color-text-on-image);
}

.st-close-media {
  position: absolute;
  inset: 0;
}

.st-close img,
.st-close video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--pos, 50% 40%);
  display: block;
}

/* Scrim so the ask reads on the brightest frames — a gradient over
 * media, the sanctioned kind. */
.st-close::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.55), transparent 55%);
  pointer-events: none;
}

.st-close-inner {
  position: absolute;
  left: var(--page-gutter);
  right: var(--page-gutter);
  bottom: var(--page-gutter);
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 5fr);
  column-gap: var(--space-10);
  align-items: end;
}

.st-close-title {
  margin: 0;
  max-width: 20ch;
  text-wrap: balance;
  font-size: var(--font-size-display-l);
  line-height: var(--line-height-display-l);
  letter-spacing: var(--letter-spacing-display-l);
  font-weight: var(--font-weight-display-l);
  color: var(--color-text-on-image);
}

.st-close .st-cta-row {
  justify-content: flex-start;
}

/* The ghost's theme ink and border vanish on the photograph — white
 * ink there, the way the deck hero does it. */
.st-close .btn-ghost {
  color: var(--color-text-on-image);
  border-color: var(--color-text-on-image);
}

/* ── Widths ─────────────────────────────────────────────────────────── */
@media (max-width: 1199px) {
  .st-spread,
  .st-cover-foot,
  .st-close-inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

@media (max-width: 1024px) {
  .st-numbers {
    column-gap: var(--space-6);
  }
}

/* ≤768: the spine goes; each chapter carries its own head; plates are
 * cards. Same collapse as the homepage index. */
@media (max-width: 768px) {
  .st-spread {
    display: block;
  }

  .st-spine {
    display: none;
  }

  .st-chapter {
    padding-top: var(--space-20);
    padding-bottom: var(--space-20);
  }

  .st-chapter + .st-chapter {
    padding-top: var(--space-24);
  }

  .st-chapter-title {
    position: static;
    width: auto; height: auto;
    margin: 0;
    overflow: visible;
    clip: auto;
    white-space: normal;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    font-size: var(--font-size-h1);
    line-height: var(--line-height-h1);
    letter-spacing: var(--letter-spacing-h1);
    font-weight: var(--font-weight-h1-emphasized);
    color: var(--color-text-heading);
  }

  .st-chapter-title + * {
    margin-top: var(--space-8);
  }

  .st-chapter-num {
    font-size: var(--font-size-caption);
    line-height: var(--line-height-caption);
    letter-spacing: var(--letter-spacing-caption);
    font-weight: var(--font-weight-caption);
    color: var(--color-text-tertiary);
    font-variant-numeric: tabular-nums;
  }

  .st-plate {
    --st-row-h: 80dvh;
    padding-block: 0;
  }

  .st-plate + .st-plate-cap {
    margin-top: var(--space-3);
  }

  .st-plate--dark .st-plate-media {
    padding: var(--space-5);
  }

  .st-cover-foot,
  .st-close-inner {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-6);
  }

  .st-cover-client {
    margin-top: var(--space-2);
  }

  .st-interlude,
  .st-perf-slot {
    margin-block: var(--space-16);
  }

  .st-close {
    margin-top: var(--space-16);
  }

  /* Phones: the plane pins and shows one beat at a time in the same
   * place — the deck's own phone treatment — with a deeper band below
   * for the statement's two lines and its small. */
  .st-perf-stage {
    padding: var(--space-16) var(--page-gutter) calc(var(--page-gutter) + var(--space-48));
  }

  /* The results stack and simply arrive; the type steps down so all
   * three clear the statement. */
  .st-nums-stage {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--space-6);
    align-content: center;
  }

  .st-metric {
    --dx: 0px;
    gap: var(--space-2);
    animation-name: st-m-in, none, st-m-flag;
  }

  .st-metric-value {
    animation: none;
  }

  .st-metric-scale {
    margin-bottom: var(--space-6);
  }
}

@media (max-width: 640px) {
  .st-statement,
  .st-perf-text,
  .st-interlude-text,
  .st-close-title {
    font-size: var(--font-size-display-m);
    line-height: var(--line-height-display-m);
    letter-spacing: var(--letter-spacing-display-m);
    font-weight: var(--font-weight-display-m);
  }

  .st-plate--pair {
    grid-template-columns: minmax(0, 1fr);
  }

  .st-numbers {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--space-8);
  }

  .st-number-value {
    font-size: var(--font-size-display-m);
    line-height: var(--line-height-display-m);
    letter-spacing: var(--letter-spacing-display-m);
    font-weight: var(--font-weight-display-m);
  }

  .st-spec {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 0;
  }

  .st-spec dt {
    margin-top: var(--space-3);
  }

  .st-spec dt:first-child {
    margin-top: 0;
  }

  .st-metric-value {
    font-size: clamp(44px, 12vw, 64px);
  }

  .st-metric-label {
    max-width: none;
    font-size: var(--font-size-small);
    line-height: var(--line-height-small);
    letter-spacing: var(--letter-spacing-small);
    font-weight: var(--font-weight-small);
  }
}

/* ── No scroll timelines — the performances unpin onto their end state.
 * Relative, not static: the plane stays its statement's containing
 * block. ──────────────────────────────────────────────────────────── */
@supports not (animation-timeline: view()) {
  .st-perf-slot {
    height: auto;
  }

  .st-perf {
    position: relative;
  }
}

/* ── Reduced motion — keyed off data-motion="reduce" on the root, which
 * the head script stamps from the OS preference or the visitor's switch. */
:root[data-motion="reduce"] .st-cover-media img,
:root[data-motion="reduce"] .st-cover-media video {
  animation: none;
}

:root[data-motion="reduce"] .st-spine-title,
:root[data-motion="reduce"] .st-spine-skill {
  transition: none;
}

:root[data-motion="reduce"] .st-plate[data-lightbox] .st-plate-media img,
:root[data-motion="reduce"] .st-plate[data-lightbox] .st-plate-media video {
  transition: none;
}

/* Every performance unpins and parks on its end state. */
:root[data-motion="reduce"] .st-perf-slot {
  height: auto;
}

:root[data-motion="reduce"] .st-perf {
  position: relative;
}

:root[data-motion="reduce"] .st-perf [data-beat] {
  animation: none;
}

/* The results, counted and parked. */
:root[data-motion="reduce"] .st-metric,
:root[data-motion="reduce"] .st-metric-value,
:root[data-motion="reduce"] .st-metric-num,
:root[data-motion="reduce"] .st-metric-marker {
  animation: none;
}

:root[data-motion="reduce"] .st-metric-num {
  --st-num: var(--st-to);
}
