/* ============================================================================
   EARLY MARKER — dense broadsheet layout.
   Composition, grid, scale, spacing, motion only. Palette + fonts come from
   design-tokens.css / fonts.css.
   ============================================================================ */

/* ---------- Layout constants ---------------------------------------------- */
:root {
  --edge:     clamp(1rem, 4vw, 3rem);        /* page margin */
  --gutter:   clamp(0.9rem, 1.8vw, 1.6rem);  /* 12-col gutter */
  --max:      84rem;

  /* ONE vertical scale, three steps, all keyed to viewport HEIGHT rather than
     width — the page composes screen by screen, so a short laptop has to give
     the same band the same proportions, just less of them. Bands used to mix a
     capped-viewport #biology against a width-derived --band-pad everywhere
     else and the rhythm read as accidental. Kept tight on purpose: the register
     is dense broadsheet, so margins are hairline-thin and the ART, never the
     background, is what takes up any spare height. */
  --band-pad: clamp(0.9rem, 2svh, 1.6rem);   /* band edge → its content */
  --head-gap: clamp(0.7rem, 1.7svh, 1.25rem);/* heading block → block below */
  --row-gap:  clamp(0.5rem, 1.2svh, 0.9rem); /* between rows inside a band */

  /* Measured at runtime by the inline script; the fallbacks are the desktop
     resting values, so the page composes correctly before the script runs. */
  --mast-h: 3.25rem;
  --foot-h: 3rem;
}

* { box-sizing: border-box; }

/* Anchors and snap points both land the target directly under the sticky
   masthead — measured, not guessed, so it stays right when the nav wraps. */
html { scroll-padding-top: var(--chrome); }
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p, dl, dd, ol, ul, blockquote, figure { margin: 0; padding: 0; }
ol, ul { list-style: none; }
a { color: inherit; }

.skip-link {
  position: absolute; left: -100vw; top: 0;
  background: var(--ink); color: var(--paper-light);
  font-family: var(--mono); font-size: 0.7rem; text-transform: uppercase;
  letter-spacing: 0.12em; padding: 0.5rem 1rem; z-index: 99;
}
.skip-link:focus-visible { left: 0; }

/* ---------- The 12-column band -------------------------------------------- */
.band {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: var(--gutter);
  row-gap: var(--row-gap);
  max-width: var(--max);
  margin-inline: auto;
  padding-inline: var(--edge);
  align-items: start;
}

.section {
  padding-block: var(--band-pad);
  position: relative;
}
/* Hairline rules do the separating — full content-width, never blank space. */
.section::before {
  content: "";
  grid-column: 1 / -1;
  grid-row: 1;
  border-top: 1px solid var(--rule);
  margin-bottom: calc(var(--row-gap) * -0.35);
}

/* ---------- SLIDES — REMOVED in this variant --------------------------------
   The dense-ink parent packed one band per screen and used mandatory
   scroll-snap to page between them. That model had a structural cost its own
   comments record: snap is only safe when EVERY slide fits, so a single band
   overflowing by a few pixels silently disabled the whole system. To stay
   inside that budget #biology had to hold a heading, a standfirst, six atlas
   cells, a pull quote and a closing paragraph on one screen at once.

   The instinct was right and the mechanism was fighting it. One screen should
   carry one unit of the argument; snap forced it to carry ten.

   This variant keeps the instinct and changes the mechanism: see narrative.css.
   A chapter is a tall scroll runway with a pinned stage inside it. The chapter
   can spend four screens of scroll and still only ever show one thing, and no
   measurement can fail, because nothing has to fit anything.

   The static closing bands (#programs, #company, #contact) keep normal document
   flow here and are deliberately NOT choreographed. */

/* ---------- Type roles (dense sizing) -------------------------------------- */
.folio, figcaption {
  font-family: var(--mono);
  font-size: 0.64rem;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--muted);
}
.folio { color: var(--ink); }

h1, h2, h3, .pull-quote, .principle-line {
  font-family: var(--display);
  font-weight: 400;
  color: var(--ink);
  letter-spacing: -0.035em;
  text-wrap: balance;
}
/* Display type carries emphasis by LINE BREAK alone. The italic cut was doing
   decorative work the grid already does, and it read as flourish rather than
   measurement — headings stay roman, one ink, one weight. */
h1 em, h2 em, h3 em,
.pull-quote em, .principle-line em,
.contact-h2 em, .page-h1 em { font-style: normal; }

.section-h2, .principle-line {
  font-size: clamp(1.9rem, 3.5vw, 3.1rem);
  line-height: 0.94;
}
/* Emphasis clause drops to its own line so every display heading reads as a
   clean two-line couplet (leading sentence / close). */
.section-h2 em,
.pull-quote em,
.principle-line em,
.contact-h2 em { display: block; }

/* One fixed right-hand spine. The standfirst of every band starts on column 9
   and runs to the margin, with a full empty column between it and the heading;
   the three bands used to open at columns 7, 9 and 8 and the page had no edge
   to read down. */
.section-h2, .principle-line { grid-column: 1 / 8; }
.section-intro, .principle-lead { grid-column: 9 / 13; }
.section-intro, .principle-lead {
  font-size: 0.95rem;
  line-height: 1.45;
  color: var(--body);
}

/* ---------- Specimen art — keyed to transparency, sits on the paper --------
   Backgrounds were alpha-keyed out, so each painting blends directly into the
   sheet with no box and no blend mode.

   .fill-fig is the shared mechanism behind the fill rule: the figure occupies
   the whole flexible grid row, and the painting is scaled to the largest size
   that fits that row, then set on its baseline. Whichever runs out first — the
   column's width or the row's height — is what limits the art, so the art is
   always as large as the composition physically allows.

   The picture is positioned OUT OF FLOW on purpose. A percentage height on an
   in-flow image falls back to the file's intrinsic pixel height whenever the
   row is being measured rather than resolved, which is how a 700px painting
   pushed the response atlas to nearly three screens. Out of flow, the figure
   contributes no height of its own, so the row is free to size itself from the
   band and the picture simply obeys it. */
.fill-fig {
  position: relative;
  min-height: 0;
  margin: 0 0 0.3rem;
}
.fill-fig img {
  position: absolute; inset: 0;
  display: block;
  width: 100%; height: 100%;
  object-fit: contain;
  object-position: left bottom;
}

.specimen { display: block; width: 100%; height: auto; }

/* ---------- Masthead -------------------------------------------------------- */
.masthead {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: baseline; gap: 1.5rem;
  max-width: var(--max); margin-inline: auto;
  padding: 0.7rem var(--edge) 0.62rem;
  background-color: var(--paper);
  border-top: 2px solid var(--ink);
  border-bottom: 1px solid var(--rule);
}
.wordmark {
  font-family: var(--display);
  font-size: clamp(1.4rem, 2.2vw, 1.6rem);
  line-height: 1;
  color: var(--ink);
  letter-spacing: -0.025em;
}
.wordmark a { text-decoration: none; }
.masthead nav {
  margin-left: auto;
  display: flex; gap: clamp(1rem, 2.2vw, 2rem);
  font-size: 0.95rem; font-weight: 520;
}
.masthead nav a { text-decoration: none; color: var(--ink); }
.masthead nav a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* ---------- HERO — headline + deck left, ripple painting right ------------- */
.hero {
  position: relative;
  min-height: var(--stage-h);
  padding-block: 0;
  overflow: hidden;
  isolation: isolate;
}
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    /* The painting is a full-bleed cover crop, so its bottom edge landed as a
       hard horizontal seam exactly on the next band's hairline. Dissolve the
       last strip into the paper — the rule below then reads as the rule, not
       as the edge of a photograph. */
    linear-gradient(
      to top,
      rgba(244, 243, 238, 1) 0%,
      rgba(244, 243, 238, 0) 11%
    ),
    linear-gradient(
      90deg,
      rgba(244, 243, 238, 0.98) 0%,
      rgba(244, 243, 238, 0.92) 30%,
      rgba(244, 243, 238, 0.62) 46%,
      rgba(244, 243, 238, 0.12) 62%,
      rgba(244, 243, 238, 0) 72%
    );
}

.hero-copy {
  /* Ten columns, not seven: the couplet's second line needs the room (see
     .hero-h1). The painting is a background layer, so widening the copy costs
     the composition nothing — the type still stops well short of the cluster. */
  grid-column: 1 / 11;
  position: relative;
  z-index: 2;
  align-self: stretch;
  display: flex;
  flex-direction: column;
  justify-content: space-between;  /* headline top, supporting text bottom */
  gap: clamp(1.2rem, 2.5vw, 2rem);
  padding-block: clamp(1.75rem, 5svh, 3.5rem) clamp(1.75rem, 4.5svh, 3rem);
  container-type: inline-size;     /* the headline is sized off THIS column */
}
.hero-h1 {
  /* The hero rests on a two-line couplet, and "Your blood holds the story."
     measures 11.0x its own font-size in Castoro. Sizing off the copy column
     (cqw) rather than the viewport is what guarantees the line holds: at any
     width the type can never grow past what the column can set on one line, so
     the third line cannot come back at some intermediate size. */
  font-size: clamp(2.4rem, min(5.6vw, 8.9cqw), 5.1rem);
  line-height: 0.92;
}
/* Padding + matching negative margins lift the overflow-clip boundary off BOTH
   the cap line and the baseline, so tight (0.92) leading no longer shaves the
   ascender tops or the descenders (e.g. the "y" in "story"). Layout position is
   unchanged; the reveal enters from far below, so the box still masks it. */
.hero-h1 .line {
  display: block; overflow: hidden;
  padding-top: 0.2em; margin-top: -0.2em;
  padding-bottom: 0.3em; margin-bottom: -0.3em;
}
.hero-h1 .line-in { display: inline-block; }

.hero-sub { max-width: 44ch; }
/* The deck is the hero's lead statement — one prominent, ink-weight column.
   40ch sets it in four lines under the wider headline; at 32ch it broke into
   six short ragged ones. */
.hero-deck { max-width: 40ch; }
.hero-lead {
  font-family: var(--sans);
  font-weight: 520;
  font-size: clamp(1.2rem, 2vw, 1.75rem);
  line-height: 1.28;
  letter-spacing: -0.005em;
  color: var(--ink);
  text-wrap: balance;
}
.hero-link { margin-top: 1rem; }
.hero-link a {
  font-family: var(--mono); font-size: 0.64rem; text-transform: uppercase;
  letter-spacing: 0.14em; color: var(--ink);
  text-decoration: underline; text-underline-offset: 4px;
}

.hero-figure { position: absolute; inset: 0; z-index: 0; margin: 0; }
.hero-ripple {
  display: block;
  width: 100%; height: 100%;
  max-width: none;
  object-fit: cover;
  object-position: center;
}

/* ---------- BIOLOGY — the response atlas: where the signal comes from ------- */
#biology { padding-block: var(--band-pad); }

/* Six host-response sources, read local → whole-body. Grouped by one top rule;
   no inter-cell lines (kept quiet per the ink-only, low-rule direction). */
.response-atlas {
  grid-column: 1 / 13;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  column-gap: clamp(1.25rem, 2.6vw, 2.5rem);
  row-gap: 0.6rem;
  padding-top: var(--head-gap);
}
.response-atlas li { display: grid; gap: 0.15rem; align-content: start; }
.atlas-head {
  font-family: var(--display); font-weight: 400;
  font-size: clamp(1.25rem, 1.5vw, 1.5rem); line-height: 1; color: var(--ink);
  letter-spacing: -0.02em;
}
.atlas-desc { font-size: 0.88rem; line-height: 1.36; color: var(--body); max-width: 38ch; }

.pull-quote {
  grid-column: 1 / 5;
  font-size: clamp(1.5rem, 2.8vw, 2.4rem);
  line-height: 1.05;
  align-self: center;
}
.measure-close { grid-column: 5 / 13; align-self: center; }
.measure-copy {
  font-size: 0.92rem; line-height: 1.42; color: var(--body);
  max-width: 68ch;
}

/* ---------- PLATFORM — the process, five stages on one row ------------------ */
#platform { padding-block: var(--band-pad); }
#platform .folio { grid-column: 1 / 13; }

/* One process, five stages, set on a five-track grid across the full band. The
   old centred flex wrap put three on the first row and two floating in the
   middle of the second, landing on no column of the 12-col grid at all — it
   read as a mistake rather than as a sequence. */
.method-steps {
  grid-column: 1 / 13;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  column-gap: clamp(0.9rem, 1.9vw, 1.7rem);
  row-gap: clamp(0.9rem, 2.2svh, 1.5rem);
  padding-top: var(--head-gap);
}
.step { display: grid; gap: 0.3rem; align-content: start; container-type: inline-size; }
.step h3 {
  font-family: var(--display); font-weight: 400;
  font-size: clamp(1.25rem, 1.55vw, 1.55rem); line-height: 1.04; color: var(--ink);
  letter-spacing: -0.02em;
}
.step > p:last-child { font-size: 0.88rem; line-height: 1.38; color: var(--body); }

/* ---------- STAKES — the second platform screen ---------------------------- */
#stakes { padding-block: var(--band-pad); }
#stakes .folio { grid-column: 1 / 13; }
.principle-lead { align-self: end; }

/* Clinical stakes triptych — kidney/tumor by stage, survival dropping 93.6 → 20.3 */
.stakes {
  grid-column: 1 / 13;
  display: grid; grid-template-columns: repeat(3, 1fr);
  column-gap: clamp(1.75rem, 3.5vw, 3rem);
  row-gap: 1.5rem;
  padding-top: var(--head-gap);
}
.stake { display: grid; gap: 0.3rem; align-content: start; }
.stake-stat {
  font-family: var(--display); font-weight: 400; color: var(--ink);
  font-size: clamp(2.2rem, 4vw, 3.4rem); line-height: 1; letter-spacing: -0.03em;
}
.stake-stat .pct { font-size: 0.5em; }
.stake-cap {
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--muted);
}
.stake-desc { font-size: 0.88rem; line-height: 1.42; color: var(--body); max-width: 36ch; margin-top: 0.25rem; }

/* Plate callout — hairline ring + leader drawn in the painting's own
   coordinates, mono label in page coordinates. The stroke is non-scaling, so
   the rule stays a true hairline however small the plate renders.

   The wrapper carries the plate's square ratio and the picture fills it
   exactly, so the overlaid SVG shares one coordinate space with the art and the
   ring stays on the nodule at every size. The square is driven by whichever is
   smaller — the figure's width or its 60svh stage cap — and centred inside the
   left side of the dial. */
.dial-fig .plate-mark {
  position: absolute; left: 50%; bottom: 0;
  display: block;
  width: min(100%, 60svh); height: auto;
  aspect-ratio: 1;
  transform: translateX(-50%);
}
.dial-fig .plate-mark img { width: 100%; height: 100%; max-width: none; }
.mark-leader {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  overflow: visible; pointer-events: none;
  stroke: var(--ink); stroke-width: 1;
}
.mark-label {
  position: absolute;
  left: calc(100% + 0.45rem);
  top: 5.8%;
  transform: translateY(-50%);
  /* absolutely positioned past the plate's right edge, so it has no available
     width to resolve against — size it from the text itself. */
  width: max-content; max-width: 10rem;
  font-family: var(--mono); font-size: 0.58rem; line-height: 1.3;
  text-transform: uppercase; letter-spacing: 0.11em; color: var(--ink);
}

/* ---------- The inverted band ----------------------------------------------- */
.night {
  background-color: var(--night);
  color: var(--night-paper);
  overflow: hidden;
}
.night :focus-visible { outline-color: var(--night-paper); }
.night ::selection { background: var(--night-paper); color: var(--night); }

/* ---------- PROGRAMS -------------------------------------------------------- */
#programs { padding-block: var(--band-pad); }
#programs .folio { grid-column: 1 / 13; }

/* This section has to make the expansion thesis visible, not merely name a
   list of programs. The plot is one continuous field: clinical difficulty on
   the vertical, cancer burden on the horizontal, program state in the marks. */
.cancer-landscape {
  grid-column: 1 / 13;
  min-width: 0;
  padding-top: var(--head-gap);
}
/* Thesis + key + sources, all UNDER the plot. Previously a display couplet and
   a three-row key stood between the heading and the figure, which pushed the
   only asset in the band off the first screen for two lines of text that say
   what the plot already shows. One row: statement left, key right. */
.landscape-foot {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: var(--gutter);
  row-gap: 0.45rem;
  align-items: baseline;
  padding-top: 0.7rem;
}
.landscape-thesis {
  grid-column: 1 / 7;
  font-family: var(--display);
  font-size: clamp(1rem, 1.35vw, 1.4rem);
  line-height: 1.12;
  letter-spacing: -0.022em;
  text-transform: none;
  color: var(--ink);
}
.landscape-key {
  grid-column: 7 / 13;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.3rem 1.5rem;
}
.landscape-key li {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--mono);
  font-size: 0.6rem;
  line-height: 1.35;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
}
.landscape-key strong { font-weight: 500; color: var(--ink); }
.landscape-source {
  grid-column: 1 / 13;
  font-size: 0.6rem;
  line-height: 1.45;
  letter-spacing: 0.05em;
  text-transform: none;
}
.landscape-source a { text-underline-offset: 3px; }
.key-mark {
  width: 0.7rem;
  aspect-ratio: 1;
  border: 1px solid var(--ink);
  border-radius: 50%;
  align-self: center;
}
.key-lead { background: var(--ink); box-shadow: 0 0 0 2px var(--paper), 0 0 0 3px var(--ink); }
.key-expansion {
  border-width: 2px;
  background: repeating-linear-gradient(45deg, transparent 0 2px, var(--rule) 2px 3px);
}
.key-roadmap { background: transparent; }
.landscape-mobile-note { display: none; }
/* The portrait chart is the phone's copy of the landscape and is off
   everywhere else; the swap happens in the 640px block at the end. */
.portrait-plot { display: none; }
.portrait-chart .status-expansion .cancer-bubble { fill: url(#expansion-hatch-p); }
.landscape-plot {
  min-width: 0;
  overflow: hidden;
  border-block: 1px solid var(--rule);
}
.landscape-chart {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}
.landscape-grid line {
  stroke: var(--rule-light);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.landscape-grid .axis { stroke: var(--rule); }
.landscape-grid .need-field { fill: var(--ink); opacity: 0.035; }
.landscape-axes text,
.platform-trajectory text,
.bubble-scale text {
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  fill: var(--muted);
}
.landscape-axes .axis-title { font-size: 13px; fill: var(--ink); }
.landscape-axes .need-label { font-size: 11px; letter-spacing: 0.1em; }
.platform-trajectory path {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1;
  stroke-dasharray: 5 7;
  opacity: 0.42;
  vector-effect: non-scaling-stroke;
}
.platform-trajectory text { font-size: 11px; fill: var(--muted); }
.cancer-mark text {
  font-family: var(--sans);
  font-size: 16px;
  font-weight: 520;
  letter-spacing: 0;
  fill: var(--ink);
}
.cancer-mark .mark-value {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  fill: var(--muted);
}
.cancer-bubble {
  fill: rgba(244, 243, 238, 0.82);
  stroke: var(--ink);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
/* The roadmap is context, not a claim: ghost outlines, quieter names, one type
   step down. It keeps the whole burden of cancer on the page without competing
   with the two programs that are actually running. */
.status-roadmap .cancer-bubble { opacity: 0.4; }
.status-roadmap text { font-size: 13px; font-weight: 450; fill: var(--muted); }
.status-expansion .cancer-bubble {
  fill: url(#expansion-hatch);
  stroke-width: 2.5;
}
.status-lead .cancer-bubble { fill: var(--ink); stroke-width: 0; }
.status-lead .lead-ring {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
}
.status-lead .inside-label,
.status-lead .inside-value { fill: var(--paper-light); }
.status-lead .inside-value {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.04em;
}
.status-lead .lead-note {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.bubble-scale circle {
  fill: none;
  stroke: var(--rule);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

/* The marks collect in once the static closing section reaches the reader. */
.motion .cancer-mark { opacity: 0; transition: opacity 0.45s var(--ease-out) calc(var(--i) * 38ms); }
.motion #programs.is-in .cancer-mark { opacity: 1; }

/* ---------- COMPANY — heading + two-column note ---------------------------- */
#company { padding-block: var(--band-pad); }
#company .folio { grid-column: 1 / 13; }
/* Statement left, note on the right-hand spine — the same two-part split the
   rest of the page uses, on the SAME columns. It was previously a private
   two-track grid whose note was then set in two more columns, so a single
   four-sentence paragraph broke into two narrow blocks reading across a gap,
   with the standing link stranded above the second one. One paragraph, one
   column, one measure. */
.company-body {
  grid-column: 1 / 13;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: var(--gutter);
  row-gap: 0.6rem;
  align-items: start;
  padding-top: var(--head-gap);
}
.studio-lead {
  grid-column: 1 / 6;
  font-weight: 520; font-size: clamp(1.05rem, 1.7vw, 1.6rem);
  line-height: 1.3; color: var(--ink);
}
.studio-copy {
  grid-column: 8 / 13;
  font-size: 0.95rem; line-height: 1.5;
  max-width: 58ch;
}
.company-more { grid-column: 1 / 6; }
.company-more a {
  font-family: var(--mono); font-size: 0.66rem; text-transform: uppercase;
  letter-spacing: 0.14em; color: var(--ink);
  text-decoration: underline; text-underline-offset: 4px;
}

/* ---------- CONTACT — reuse the night band ---------------------------------- */
.contact-inner { padding-block: clamp(1.4rem, 3.4svh, 2.5rem); position: relative; }
.contact .folio { grid-column: 1 / 13; color: var(--night-paper); }
.contact-h2 {
  grid-column: 1 / 11;
  font-size: clamp(2.4rem, 5.6vw, 4.75rem);
  line-height: 0.94;
  color: var(--night-paper);
}
.contact-link a {
  font-family: var(--mono); font-size: 0.72rem; text-transform: uppercase;
  letter-spacing: 0.15em; color: var(--night-paper);
  text-decoration: underline; text-underline-offset: 5px;
}

/* ---------- FOOTER ---------------------------------------------------------- */
/* Two items only, so it is set as a colophon: wordmark at the left margin,
   copyright at the right, at every width. Letting them sit next to each other
   on narrow screens read as a fragment of a wider bar. */
.footer {
  /* The SAME black as the contact strip above it, not --ink. Two near-blacks
     two values apart met at the fold and read as a seam across the closing
     mass; one value makes the strip and the colophon a single dark block. */
  background: var(--night);
  color: rgba(244, 243, 238, 0.72);
  display: flex; flex-wrap: wrap; gap: 0.4rem 2rem; align-items: baseline;
  padding: 0.75rem var(--edge);
  font-family: var(--mono); font-size: 0.6rem;
  text-transform: uppercase; letter-spacing: 0.13em;
}
.footer .wordmark { color: var(--paper-light); font-size: 1rem; text-transform: none; letter-spacing: -0.02em; }
.footer p:last-child { margin-left: auto; }

/* ============================================================================
   THE FILL RULE, band by band. Desktop only: below 981px the slide model is
   off and every band returns to natural document flow.
   Each band names its rows and gives exactly ONE of them 1fr; the figure inside
   that row is a .fill-fig, so the spare height goes into the painting.
   ============================================================================ */
@media (min-width: 981px) {
  /* On a screen wider than the measure the page was paying for its 84rem cap
     in background — three hundred pixels of empty margin down each side while
     the art inside was bound by its column width. Step the measure up once so
     the columns, and with them every painting, get bigger instead. */
  @media (min-width: 1500px) { :root { --max: 100rem; } }

  /* #biology, #platform and #stakes are choreographed chapters in this variant
     and compose themselves inside a pinned stage — their fill-rule rows lived
     here in the parent and now live in narrative.css. Only the two static
     closing bands still fill a band the old way. */

  /* rule / folio / heading / the cancer landscape.
     The band is held to one screen and the plot takes every pixel the heading
     does not, so the figure is as large as the composition allows and the
     reader never has to scroll to finish reading it. */
  #programs {
    /* A hard height, not a min: with min-height the 1fr row still sizes itself
       from the SVG's aspect ratio and the band overflows the screen. The band
       is given exactly one screen and the plot absorbs whatever is left. */
    height: calc(100svh - var(--mast-h));
    min-height: 34rem;
    grid-template-rows: 0 auto auto minmax(0, 1fr);
  }
  #programs::before { grid-row: 1; }
  #programs .folio { grid-row: 2; }
  /* One line, not the usual couplet: the second line cost 46px of chart for a
     break the sentence does not need. It reads as a masthead and the plot
     gets the height back. */
  #programs .section-h2 { grid-row: 3; grid-column: 1 / 11; }
  #programs .section-h2 em { display: inline; }
  .cancer-landscape {
    grid-row: 4;
    /* .band sets align-items:start, so without this the figure sizes itself
       from the SVG and ignores the row it was given. */
    align-self: stretch;
    display: flex;
    flex-direction: column;
    min-height: 0;
  }
  /* The plot takes whatever height the heading leaves and the SVG scales down
     into it — that is what guarantees the band never spills off the screen.
     The cap is the height the drawing would ask for at full band width: past
     that the plot would only be adding letterbox inside its own rules, and on
     a tall display the spare height should fall AFTER the key, not between the
     chart and the caption that belongs to it. */
  .landscape-plot {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    max-height: calc((min(100vw, var(--max)) - var(--edge) * 2) / 3.02);
  }
  .landscape-chart { height: 100%; }

  /* rule / folio / heading / body / the standing link */
  #company { grid-template-rows: 0 auto auto minmax(0, 1fr) auto; }
  #company::before { grid-row: 1; }
  #company .folio { grid-row: 2; }
  #company .section-h2 { grid-row: 3; grid-column: 1 / 7; }
  .company-body { grid-row: 4; align-self: start; }
  /* Under the statement it belongs to, not floating on the opposite spine. */
  #company .company-more { grid-row: 5; grid-column: 1 / 6; align-self: start; }
}

/* ---------- MOTION — press + collect, fully gated --------------------------- */
.motion .hero-h1 .line-in {
  transform: translateY(150%);
  transition: transform 0.85s var(--ease-out);
}
.motion .hero-h1 .line:nth-child(2) .line-in { transition-delay: 0.09s; }
.motion.pressed .hero-h1 .line-in { transform: none; }

.motion .hero-deck {
  opacity: 0; transform: translateY(10px);
  transition: opacity 0.6s var(--ease-out) 0.35s, transform 0.6s var(--ease-out) 0.35s;
}
.motion .hero-deck.is-in { opacity: 1; transform: none; }

.motion .collect {
  opacity: 0; transform: translateY(12px);
  transition:
    opacity 0.55s var(--ease-out) calc(var(--i, 0) * 90ms),
    transform 0.55s var(--ease-out) calc(var(--i, 0) * 90ms);
}
.motion .collect.is-in { opacity: 1; transform: none; }

/* ---------- RESPONSIVE ------------------------------------------------------
   Below 981px the slide model is off (see the script): bands return to normal
   document flow and the page scrolls continuously. Figures lose their flexible
   row, so they fall back to a height keyed to the column width. */
@media (max-width: 980px) {
  /* No flexible row to fill, so the pictures come back into flow and take a
     height keyed to the column width. */
  .fill-fig img { position: static; height: clamp(8rem, 22vw, 15rem); width: auto; max-width: 100%; }
  .dial-fig .plate-mark {
    position: relative; left: auto; bottom: auto;
    width: clamp(8rem, 22vw, 15rem); height: auto;
    transform: none;
  }

  /* Keep the painting as the hero BACKGROUND at narrow widths — it must stay
     behind the copy, never drop to a separate stacked block. The cluster is
     held to the right and the scrim is widened so the left-hand copy stays
     legible over it. (hero-figure/hero-ripple inherit the full-bleed base.) */
  .hero { min-height: clamp(34rem, 82svh, 48rem); }
  .hero-copy { grid-column: 1 / 13; }
  .hero-ripple { object-position: 74% center; }
  .hero::after {
    background:
      linear-gradient(to top, rgba(244, 243, 238, 1) 0%, rgba(244, 243, 238, 0) 10%),
      linear-gradient(
        90deg,
        rgba(244, 243, 238, 0.98) 0%,
        rgba(244, 243, 238, 0.95) 46%,
        rgba(244, 243, 238, 0.76) 64%,
        rgba(244, 243, 238, 0.30) 84%,
        rgba(244, 243, 238, 0) 100%
      );
  }

  .section-h2, .principle-line { grid-column: 1 / 13; }
  .section-intro, .principle-lead {
    grid-column: 1 / 13; max-width: 62ch; align-self: start;
  }
  .response-atlas { grid-template-columns: 1fr 1fr; }
  .pull-quote { grid-column: 1 / 5; align-self: center; }
  .measure-close { grid-column: 5 / 13; }

  /* 3 + 2, locked to the grid: step 4 starts on column 1, so the trailing pair
     still reads as part of the sequence rather than as loose items. */
  .method-steps { grid-template-columns: repeat(3, 1fr); }

  .landscape-thesis { grid-column: 1 / 8; }
  .landscape-key { grid-column: 8 / 13; }

  .company-body { grid-column: 1 / 13; grid-template-columns: repeat(12, 1fr); }
  .studio-lead { grid-column: 1 / 6; }
  .studio-copy { grid-column: 7 / 13; max-width: 62ch; }
  .company-more { grid-column: 1 / 13; margin-top: 0.35rem; }
  .contact-h2 { grid-column: 1 / 13; }
}

@media (max-width: 640px) {
  .band { grid-template-columns: 1fr; }
  .band > * { grid-column: auto !important; grid-row: auto !important; }
  .section::before { grid-column: auto; }

  /* The bar's block padding is set for BOTH the sheet and the narrative in
     narrative.css, which loads after this file and would overwrite anything
     stated here. One source of truth; look there. */
  .masthead { flex-wrap: wrap; row-gap: 0.45rem; }
  .masthead nav {
    margin-left: 0; flex-basis: 100%;
    flex-wrap: wrap; row-gap: 0.5rem;
  }

  .hero-sub { grid-template-columns: 1fr; }
  /* Mobile hero (redesigned in the clean room): on a portrait phone the
     illustration LEAVES the background and flows as a contained, full-width
     plate BELOW the copy — a deliberate figure, not a faded backdrop. Its ground
     is the same off-white as the page, so it needs no scrim and no frame; the
     copy never overlaps it, so nothing is washed out and the height stays honest
     (no forced viewport quota → graceful from 640px down to 360px). */
  .hero { min-height: 0; padding-bottom: clamp(1.5rem, 5vw, 2.25rem); }
  .hero::after { content: none; }
  .hero-copy {
    justify-content: flex-start;
    gap: clamp(1.15rem, 4.5vw, 1.75rem);
    padding-block: clamp(2rem, 7vw, 3rem) 0;
  }
  .hero-h1 { font-size: clamp(1.8rem, 7.6vw, 2.7rem); line-height: 0.95; }
  .hero-lead { font-size: clamp(1.02rem, 3.8vw, 1.28rem); line-height: 1.35; }
  .hero-link { margin-top: 0.9rem; }
  .hero-link a { font-size: 0.66rem; display: inline-block; padding-block: 0.35rem; }
  /* the figure already follows the copy in source order, so restacking is pure
     CSS. 4:3 plate, cover-cropped and positioned to keep the cluster focal. */
  .hero-figure {
    position: static; inset: auto;
    margin-inline: calc(var(--edge) * -1);
    margin-top: clamp(0.75rem, 3vw, 1.4rem);
  }
  .hero-ripple {
    height: auto; aspect-ratio: 4 / 3;
    object-fit: cover; object-position: 62% 48%;
  }

  /* Both of these used to floor at 9rem, which on a 390px phone left a 144px
     picture sitting in a 358px column with 60% of the row empty — and at that
     size the callout ring on the localized plate is smaller than the type. Let
     the column drive the width; 26rem keeps them from ballooning on a tablet. */
  .fill-fig img { height: auto; width: 100%; max-width: 26rem; }
  .dial-fig .plate-mark { width: min(100%, 26rem); }

  .response-atlas { grid-template-columns: 1fr; }
  .measure-copy { column-count: 1; }
  .method-steps { grid-template-columns: 1fr; }
  .stakes { grid-template-columns: 1fr; }

  .landscape-foot { grid-template-columns: 1fr; row-gap: 0.7rem; }
  .landscape-thesis, .landscape-key, .landscape-source { grid-column: 1 / -1; }
  .landscape-key { justify-content: flex-start; gap: 0.35rem 1.1rem; }
  /* THE SWAP. The wide chart and its "swipe across the landscape" note both
     go; the portrait chart replaces them and fits whole. Nothing here scrolls
     sideways any more, which is the entire point of having built it. */
  .landscape-mobile-note { display: none; }
  .landscape-plot { display: none; }
  .portrait-plot {
    display: block;
    min-width: 0;
    overflow: hidden;
    border-block: 1px solid var(--rule);
  }
  .portrait-chart { width: 100%; max-width: none; height: auto; }
  /* The wide chart's type sizes were set against a 1240-unit canvas. This one
     is 700 across, so the same font-size renders nearly twice as large — set
     against the canvas it actually has. */
  .portrait-chart .landscape-axes text { font-size: 15px; }
  .portrait-chart .landscape-axes .need-label,
  .portrait-chart .platform-trajectory text { font-size: 14px; }
  .portrait-chart .cancer-mark text { font-size: 16px; }
  .portrait-chart .bubble-scale text { font-size: 14px; }
  /* Phone: one stack, so every wide-layout span is released here as well. */
  .company-body { grid-template-columns: 1fr; }
  .studio-lead, .studio-copy { grid-column: 1 / -1; }
}

/* ============================================================================
   TEAM / ABOUT PAGE — same broadsheet DNA: folio + serif head, then a wall of
   grayscale portraits (executive row, advisory row). Monochrome by intent,
   so the color photos are pulled to grayscale to sit on the ink palette.
   ============================================================================ */
/* No folios on this page — every band leads with its headline. */
.page-head { padding-block: clamp(0.8rem, 2.3svh, 2rem) var(--band-pad); }
.page-h1 {
  grid-column: 1 / 9;
  font-family: var(--display); font-weight: 400; color: var(--ink);
  letter-spacing: -0.035em; text-wrap: balance;
  font-size: clamp(2rem, 4.4vw, 3.5rem); line-height: 0.96;
}
.page-intro {
  grid-column: 9 / 13; align-self: end;
  font-size: 0.95rem; line-height: 1.5; color: var(--body);
}

#executive, #advisory { padding-block: var(--band-pad); }

/* Both boards on ONE screen: the two rosters run side by side in a 4:3 split
   of the measure — four columns and three — which is what makes every column,
   and therefore every portrait, exactly the same width on both sides. Below
   981px the pair unwraps and the two sections stack in normal flow. */
/* The two boards STACK, each across the full measure. Side by side they shared
   seven tracks, so every portrait was a seventh of the sheet — about 149px —
   and the names broke over two and three lines beneath them. Fitting both
   boards on one screen and printing the faces at a decent size are in direct
   conflict on any laptop, and the faces win: this is the page where people look
   at people. The page scrolls instead, and the snap script turns itself off
   when it measures the overflow. */
.roster-pair { width: 100%; }
/* Roster heads run on ONE line — these are two- and four-word phrases, so the
   couplet break the section heads use elsewhere only fragmented them. */
#executive .section-h2, #advisory .section-h2 { grid-column: 1 / 13; }
#executive .section-h2 em, #advisory .section-h2 em { display: inline; }

/* NO RULES ON THIS PAGE. The band hairlines that separate sections elsewhere
   were fencing each board into a strip, and combined with a border around every
   portrait the roster read as a grid of boxes. The headings and the white space
   do the separating here; the faces carry the page. */
#executive.section::before, #advisory.section::before { content: none; }
/* With no rule between them, the gap IS the separation — the second board needs
   real air above its heading or it reads as a fifth column of the first. */
#advisory { padding-top: clamp(1.75rem, 4.5svh, 3.25rem); }

.roster {
  grid-column: 1 / 13;
  display: grid; grid-template-columns: repeat(4, 1fr);
  /* Tight. A wide gutter between portraits reads as the page falling apart —
     a roster wants to look like one group, not four separate plates. */
  column-gap: clamp(0.85rem, 1.5vw, 1.4rem);
  row-gap: 1.5rem;
  padding-top: var(--head-gap);
}
/* The advisory board keeps the executive row's FOUR tracks even though it
   currently runs three people: ONE portrait size down the whole page is the
   point. A three-track advisory row would have set the two bands at different
   scales, which reads as a redesign rather than as a run that ended. */
.member { display: grid; gap: 0.28rem; align-content: start; }
.member-fig { margin: 0 0 0.5rem; }
/* The portraits are the one piece of art on the site that is NOT stretched to
   fill a screen. They hold their own proportion — 1:1.15, sized by the column
   — so every face on the page is rendered at exactly the same size. */
.member-fig img {
  display: block; width: 100%; height: auto;
  aspect-ratio: 1 / 1.15;
  object-fit: cover; object-position: center 22%;
  filter: grayscale(1) contrast(1.03);
  /* No frame — the portraits sit on the paper like every other image on the
     site. The hairline around each one turned the roster into a row of boxes. */
  background: var(--paper-deep);
}
.member-name {
  font-family: var(--display); font-weight: 400;
  font-size: clamp(1.16rem, 1.4vw, 1.4rem); line-height: 1.08; color: var(--ink);
  letter-spacing: -0.02em;
}
.member-role {
  font-family: var(--mono); font-size: 0.62rem; text-transform: uppercase;
  letter-spacing: 0.12em; color: var(--muted); margin-top: 0.1rem;
}
.member-bio {
  font-size: 0.85rem; line-height: 1.42; color: var(--body); margin-top: 0.25rem;
}
/* advisory has no role line — match the name→bio leading of the exec row */
.roster.advisory .member-bio { margin-top: 0.45rem; }

/* No row-matching machinery is needed here: because every portrait is a fixed
   1:1.15 of an equal column, they are all exactly the same height, so the row
   shares one baseline by construction. Bios of different lengths simply run to
   different depths beneath it, which is how a roster reads on paper. */
/* The viewport-height cap that used to sit here is gone with the one-screen
   roster: the page scrolls now, so the portrait is sized by its column and by
   nothing else, and every face on the page renders at the same size. */

@media (max-width: 980px) {
  .page-h1   { grid-column: 1 / 13; }
  .page-intro{ grid-column: 1 / 13; align-self: start; max-width: 62ch; margin-top: 0.6rem; }
  .roster {
    grid-template-columns: 1fr 1fr;
    column-gap: clamp(1.25rem, 4vw, 2.5rem); row-gap: 1.9rem;
  }
}
@media (max-width: 640px) {
  .roster { grid-template-columns: 1fr; row-gap: 1.6rem; }
  /* THE FACES CARRY THIS PAGE, so give them the width. At 5.5rem the portrait
     was 88px against a name set at 18.6px and a bio at 13.6px: the type was
     shouting over a thumbnail, and the row read as a caption with a stamp
     beside it rather than as a person. Take the picture to 8rem and step the
     type down — the balance, not either value alone, is the point.

     The text column still holds ~30 characters a line at 8rem, which is short
     but is a bio of two sentences, not a paragraph to be read at length. */
  .member { grid-template-columns: 8rem 1fr; gap: 0 0.95rem; align-items: start; }
  .member-fig { grid-row: 1 / span 4; margin: 0; }
  .member-fig img { aspect-ratio: 1 / 1.2; }
  .member-name { font-size: 1.02rem; line-height: 1.12; }
  .member-role { font-size: 0.55rem; letter-spacing: 0.1em; }
  .member-bio  { font-size: 0.78rem; line-height: 1.4; margin-top: 0.3rem; }
}
