/* ============================================================
   EBAMed, one stylesheet for the whole site.

   BRAND PALETTE (the only decorative colours on this site):
     BLACK   #000000   console ground, headings on paper
     GREY 2  #504f4f   body copy on paper
     BLUE    #4871b6   accent: links, buttons, targets, keylines
   Paper white and the grey ramp are neutral tints of the above.

   Design: a clinical instrument readout. Black console sections
   alternate with paper-white data sheets. Three further colours
   are FUNCTIONAL and RESERVED for diagram data only. coral = the
   ECG trace, cyan = the cardio-respiratory target motion, amber =
   the beam-on gate window and the motion margin it removes. They
   are legend keys, not decoration. Never style chrome with them.

   Layout: every section is a two-column grid, a narrow left
   rail (index label + title) and a wide right column (content).

   Edit tokens in @layer tokens below; nothing else needs touching.
   ============================================================ */

@layer tokens, base, layout, components, motion;

/* cross-document view transitions between pages */
@view-transition { navigation: auto; }

/* ---------------------------------------------------------- */
@layer tokens {
  :root {
    /* ground + ink, neutral ramp anchored on BLACK #000000 -- */
    --ground:     oklch(0 0 0);            /* #000000  BLACK console */
    --ground-lift:oklch(0.220 0.013 260);  /* #171b21  raised panel  */
    --paper:      oklch(1 0 0);            /* #ffffff  sheet         */
    --paper-lift: oklch(0.976 0.002 260);  /* #f6f7f8  keeps brand blue at 4.5:1 */
    --ink:        oklch(0 0 0);            /* #000000  BLACK  heads  */
    --ink-soft:   oklch(0.429 0.001 17);   /* #504f4f  GREY 2 body   */
    --mist:       oklch(0.795 0.005 260);  /* #babcbf  body on black */

    /* brand accent + functional signals ---------------------- */
    --blue:       oklch(0.551 0.118 260.3);/* #4871b6  BLUE accent, links   */
    --blue-lift:  oklch(0.700 0.120 260);  /* #729fe9  BLUE on black, 7.8:1 */
    --pulse:      oklch(0.660 0.175 30);   /* ECG TRACE ONLY   */
    --breath:     oklch(0.800 0.115 203);  /* RESPIRATION ONLY */
    --beam:       oklch(0.901 0.181 97.8); /* GATE / MARGIN, the product's own gate yellow (Controller Colors.BEAM #FFDE21) */

    /* type ------------------------------------------------- */
    --display: "Bricolage Grotesque", "Helvetica Neue", Arial, sans-serif;
    --body:    "Instrument Sans", "Segoe UI", system-ui, sans-serif;
    --mono:    "Martian Mono", ui-monospace, Consolas, monospace;

    --step--1: clamp(0.78rem, 0.76rem + 0.12vw, 0.86rem);
    --step-box: clamp(0.92rem, 0.89rem + 0.16vw, 1.01rem); /* body copy inside cards/tiles */
    --step-0:  clamp(1rem, 0.97rem + 0.16vw, 1.09rem);
    --step-1:  clamp(1.18rem, 1.1rem + 0.4vw, 1.4rem);
    --step-2:  clamp(1.5rem, 1.3rem + 1vw, 2.1rem);
    --step-3:  clamp(2rem, 1.6rem + 2vw, 3rem);
    --step-4:  clamp(3.15rem, 2.3rem + 4.6vw, 5.6rem);

    /* space ------------------------------------------------ */
    --gutter: clamp(1.25rem, 4vw, 3rem);
    --bay:    clamp(3.5rem, 8vw, 7.5rem);   /* section padding */
    --shelf:  clamp(1.75rem, 4vw, 3.25rem); /* grid gap        */
    --measure: 1240px;
  }
}

/* ---------------------------------------------------------- */
@layer base {
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

  html {
    scroll-behavior: smooth;
    -webkit-text-size-adjust: 100%;
  }

  body {
    font-family: var(--body);
    font-size: var(--step-0);
    line-height: 1.65;
    background: var(--ground);
    color: var(--ink);
    font-synthesis: none;
  }

  img { max-width: 100%; display: block; }

  h1, h2, h3 {
    font-family: var(--display);
    font-weight: 620;
    line-height: 1.02;
    letter-spacing: -0.025em;
    text-wrap: balance;
    color: var(--hd, var(--ink));
  }
  h1 { font-size: var(--step-4); }
  h2 { font-size: var(--step-3); }
  h3 { font-size: var(--step-1); letter-spacing: -0.015em; line-height: 1.2; }

  p { text-wrap: pretty; max-width: 62ch; }
  p + p { margin-top: 0.85em; }

  a { color: currentColor; text-decoration-thickness: 1px; text-underline-offset: 3px; }
  a:hover { text-decoration-color: var(--accent, var(--blue)); }

  :focus-visible { outline: 2px solid var(--accent, var(--blue-lift)); outline-offset: 3px; }

  ::selection { background: var(--blue); color: var(--paper); }
}

/* ---------------------------------------------------------- */
@layer layout {
  /* two grounds. Components read --hd / --fg / --rule, so every
     component works on both without theme-specific overrides. */
  .console {
    --hd: var(--paper);
    --fg: var(--mist);
    --rule: color-mix(in oklab, var(--mist) 22%, transparent);
    --accent: var(--blue-lift);
    background: var(--ground);
    color: var(--fg);
  }
  .sheet {
    --hd: var(--ink);
    --fg: var(--ink-soft);
    --rule: color-mix(in oklab, var(--ink) 14%, transparent);
    --accent: var(--blue);
    background: var(--paper);
    color: var(--fg);
  }
  .sheet.lift { background: var(--paper-lift); }
  .console.lift { background: var(--ground-lift); }

  .bay { padding: var(--bay) 0; }
  /* for bands that carry a single row of marks, not an argument */
  .bay.thin { padding-block: clamp(2.5rem, 5vw, 4rem); }

  .wrap {
    width: min(100% - (var(--gutter) * 2), var(--measure));
    margin-inline: auto;
  }

  /* rail + body: the site's structural signature.
     The shared top rule is the alignment datum, rail and content
     both start on it, so every section lines up down the page. */
  .split {
    display: grid;
    grid-template-columns: minmax(0, 19rem) minmax(0, 1fr);
    gap: var(--shelf);
    align-items: start;
    border-top: 1px solid var(--rule);
    padding-top: clamp(1.4rem, 3vw, 2.4rem);
  }
  .rail { position: sticky; top: 6rem; display: grid; gap: 0.9rem; }
  .rail h2 { font-size: var(--step-2); }
  /* the section rule already carries the eyebrow, no second hairline */
  .rail .idx::after { display: none; }

  .flow { display: grid; gap: clamp(1.5rem, 3vw, 2.5rem); }

  @media (width < 60rem) {
    .split { grid-template-columns: 1fr; }
    .rail { position: static; }
  }
}

/* ---------------------------------------------------------- */
@layer components {

  /* ---- index label (mono eyebrow) ---- */
  .idx {
    font-family: var(--mono);
    font-size: 0.74rem;
    font-weight: 400;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--accent);
    display: flex;
    align-items: center;
    gap: 0.7rem;
  }
  .idx::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--rule);
  }

  /* a hero eyebrow names the page, not a section within it: a little more
     size than a section index, and room of its own before the h1, which
     otherwise sits hard against it on a zero top margin */
  .hero .idx {
    font-size: 0.86rem;
    margin-bottom: 1rem;
  }

  .lede {
    font-size: var(--step-1);
    line-height: 1.45;
    color: var(--hd);
    max-width: 46ch;
    letter-spacing: -0.01em;
  }

  /* ---- header ---- */
  .topbar {
    position: sticky;
    top: 0;
    z-index: 20;
    background: color-mix(in oklab, var(--ground) 82%, transparent);
    backdrop-filter: blur(14px) saturate(1.2);
    border-bottom: 1px solid color-mix(in oklab, var(--mist) 16%, transparent);
  }
  .topbar nav {
    width: min(100% - (var(--gutter) * 2), var(--measure));
    margin-inline: auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    padding-block: 0.9rem;
    flex-wrap: wrap;
  }
  .mark img {
    height: clamp(2.3rem, 1.9rem + 1vw, 2.7rem);
    width: auto;
    filter: brightness(0) invert(1);
    opacity: 0.94;
  }
  .topbar ul { list-style: none; display: flex; flex-wrap: wrap; gap: clamp(0.9rem, 2.2vw, 1.9rem); }
  .topbar a {
    font-family: var(--mono);
    font-size: 0.68rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--mist);
    text-decoration: none;
    padding-block: 0.35rem;
    border-bottom: 1px solid transparent;
  }
  .topbar a:hover { color: var(--paper); }
  .topbar a[aria-current="page"] { color: var(--paper); border-bottom-color: var(--blue-lift); }
  /* the LinkedIn mark is one more nav link, not a badge stuck on the end:
     it takes the row's colour and hover so the line still reads as one
     object. currentColor is what carries that, so the glyph is filled,
     not stroked like a .picto. */
  .topbar .social { display: inline-flex; align-items: center; }
  .topbar .social svg {
    display: block;
    width: 1.15rem;
    height: 1.15rem;
    fill: currentColor;
  }

  /* scroll progress ---- */
  .progress {
    position: fixed;
    inset: 0 0 auto 0;
    height: 2px;
    z-index: 30;
    background: var(--blue-lift);
    transform-origin: 0 50%;
    scale: 0 1;
  }

  /* ---- hero ---- */
  .hero {
    position: relative;
    overflow: clip;
    padding-block: clamp(4rem, 12vh, 9rem) 0;
    background:
      radial-gradient(120% 90% at 15% -10%, color-mix(in oklab, var(--blue) 22%, transparent), transparent 60%),
      var(--ground);
  }
  /* instrument screen ruling */
  .hero::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image:
      repeating-linear-gradient(to right, color-mix(in oklab, var(--mist) 8%, transparent) 0 1px, transparent 1px 84px),
      repeating-linear-gradient(to bottom, color-mix(in oklab, var(--mist) 6%, transparent) 0 1px, transparent 1px 84px);
    mask-image: linear-gradient(to bottom, black, transparent 78%);
    pointer-events: none;
  }
  .hero > * { position: relative; }
  .hero h1 { max-width: 15ch; margin-block: 0 1.6rem; }
  .hero .lede { max-width: 44ch; color: var(--mist); }
  .actions { display: flex; flex-wrap: wrap; gap: 0.8rem; }
  .hero .actions { margin-top: 2.2rem; }
  .hero.deck { padding-bottom: clamp(3rem, 7vw, 5.5rem); }
  .hero.deck h1 { font-size: var(--step-3); max-width: 22ch; }

  /* ---- instrument: ultrasound panel + ECG ---- */
  .instrument {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 33rem);
    gap: var(--shelf) clamp(2rem, 5vw, 4rem);
    align-items: center;
  }
  @media (width < 62rem) {
    .instrument { grid-template-columns: 1fr; align-items: start; }
  }

  .panel {
    border: 1px solid var(--rule);
    background: color-mix(in oklab, var(--ground) 88%, black);
    padding: 0.7rem;
    display: grid;
    gap: 0.6rem;
  }
  .chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.4rem 0.8rem;
    font-family: var(--mono);
    font-size: 0.62rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--fg);
  }
  .swap { display: grid; }         /* two chips stacked, one visible at a time */
  .swap > * { grid-area: 1 / 1; }
  .chip {
    border: 1px solid var(--rule);
    border-radius: 999px;
    padding: 0.2rem 0.6rem;
    white-space: nowrap;
  }
  .chip.on {
    color: var(--beam);
    border-color: var(--beam);
    background: color-mix(in oklab, var(--beam) 16%, transparent);
    opacity: 0;
  }

  /* ---- scope: ultrasound with the plan drawn over it ---- */
  .scope { position: relative; display: grid; isolation: isolate; }
  .scope img { grid-area: 1 / 1; width: 100%; height: auto; }
  .scope .held { display: none; }
  .scope .plan {
    grid-area: 1 / 1;
    width: 100%;
    height: 100%;
    z-index: 1;
  }
  .plan .win { fill: none; stroke: var(--beam); stroke-width: 2; stroke-dasharray: 7 6; opacity: 0.55; }
  .plan .flood { fill: color-mix(in oklab, var(--beam) 30%, transparent); opacity: 0; }
  .plan .ptv { fill: none; stroke: var(--blue-lift); stroke-width: 2; }
  .plan .cross { stroke: var(--blue-lift); stroke-width: 1.4; }
  .plan .track { filter: drop-shadow(0 0 6px color-mix(in oklab, var(--blue-lift) 70%, transparent)); }

  /* ---- signals: two lanes, one 6 s loop ---- */
  .signal { display: block; width: 100%; height: clamp(160px, 23vh, 215px); margin-top: clamp(2rem, 5vw, 3.5rem); }
  .signal .gmin { stroke: color-mix(in oklab, var(--mist) 13%, transparent); stroke-width: 1; }
  .signal .gmaj { stroke: color-mix(in oklab, var(--mist) 24%, transparent); stroke-width: 1; }
  .signal .base { stroke: color-mix(in oklab, var(--mist) 22%, transparent); stroke-width: 1; stroke-dasharray: 2 6; }
  .signal .trace {
    fill: none;
    stroke: var(--pulse);
    stroke-width: 2.2;
    stroke-linecap: round;
    stroke-linejoin: round;
    filter: drop-shadow(0 0 7px color-mix(in oklab, var(--pulse) 50%, transparent));
  }
  .signal .motion {
    fill: none;
    stroke: var(--breath);
    stroke-width: 2.2;
    stroke-linecap: round;
    stroke-linejoin: round;
    filter: drop-shadow(0 0 7px color-mix(in oklab, var(--breath) 45%, transparent));
  }
  .signal .exhale { fill: color-mix(in oklab, var(--breath) 12%, transparent); }
  .signal .gate { fill: color-mix(in oklab, var(--beam) 13%, transparent); }
  .signal .edge { stroke: var(--beam); stroke-width: 1; stroke-dasharray: 3 6; opacity: 0.8; }
  .signal .rider { fill: var(--pulse); opacity: 0; }
  .signal .rider-m { fill: var(--breath); opacity: 0; }
  /* the instant under decision: one "live" line across both lanes */
  .signal .sweep {
    stroke: color-mix(in oklab, var(--paper) 85%, transparent);
    stroke-width: 1.6;
    filter: drop-shadow(0 0 6px color-mix(in oklab, var(--paper) 60%, transparent));
  }

  /* Labels live in HTML, not in the SVG: the chart is stretched to the page
     width (preserveAspectRatio="none"), which would squash any text inside it. */
  .signals { position: relative; margin-top: clamp(2rem, 5vw, 3.5rem); }
  .signals .signal { margin-top: 0; }
  .signals .key {
    position: absolute;
    font-family: var(--mono);
    font-size: 0.62rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    white-space: nowrap;
    pointer-events: none;
  }
  /* each key is mixed with the ground's own body color, so the same markup
     reads on the console and on the sheet */
  .signals .key.ecg  { top: 0; left: 0; color: color-mix(in oklab, var(--pulse) 80%, var(--fg)); }
  .signals .key.motion { top: 55%; left: 0; color: color-mix(in oklab, var(--breath) 74%, var(--fg)); }
  .signals .key.gate { top: 0; color: color-mix(in oklab, var(--beam) 74%, var(--fg)); }
  /* the acquisition badge: a blinking record dot, top right of the strip */
  .signals .key.live {
    top: 0;
    right: 0;
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    color: color-mix(in oklab, var(--paper) 85%, transparent);
  }
  .signals .key.live::before {
    content: "";
    width: 0.45rem;
    height: 0.45rem;
    border-radius: 50%;
    background: var(--pulse);
    box-shadow: 0 0 6px var(--pulse);
  }
  .signals figcaption { margin-top: 0.9rem; max-width: 74ch; }
  .hero .signals { padding-bottom: clamp(2.5rem, 5vw, 4rem); }

  /* ---- buttons ---- */
  .btn {
    --btn-bg: var(--blue);
    font-family: var(--mono);
    font-size: 0.72rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.95rem 1.5rem;
    border: 1px solid var(--btn-bg);
    border-radius: 2px;
    background: var(--btn-bg);
    color: var(--paper);
    transition: background 0.18s ease, color 0.18s ease, translate 0.18s ease;
  }
  .btn:hover {
    translate: 0 -2px;
    background: color-mix(in oklab, var(--blue) 72%, black);
    border-color: color-mix(in oklab, var(--blue) 72%, black);
    color: var(--paper);
  }
  .btn.line { background: transparent; color: var(--hd); border-color: var(--rule); }
  .btn.line:hover { border-color: var(--hd); background: color-mix(in oklab, var(--hd) 10%, transparent); color: var(--hd); }
  /* ---- readout: big numbers ---- */
  .readout {
    display: grid;
    gap: var(--shelf);
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  }
  /* four figures go 2 x 2 rather than 3 + 1: a lone cell on a second row reads
     as a mistake, the same way a wrapped card grid does */
  @media (width < 78rem) {
    .readout:has(> :nth-child(4)) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }
  @media (width < 30rem) {
    .readout { grid-template-columns: 1fr; }
  }
  .cell { border-top: 1px solid var(--rule); padding-top: 0.9rem; }
  .cell b {
    display: block;
    font-family: var(--display);
    font-weight: 640;
    font-size: var(--step-3);
    line-height: 1;
    letter-spacing: -0.04em;
    font-variant-numeric: tabular-nums;
    color: var(--hd);
  }
  .cell span {
    display: block;
    margin-top: 0.6rem;
    font-family: var(--mono);
    font-size: 0.70rem;
    line-height: 1.7;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--fg);
  }
  /* .bare: a readout with nothing above it to be divided from. The keyline
     is a separator, and drawn against open space it reads as a stray rule. */
  .readout.bare .cell { border-top: none; padding-top: 0; }
  .readout.beamed .cell { border-top-color: var(--beam); }
  /* funding figures are long strings, hold them on one line */
  .readout.beamed .cell b { font-size: var(--step-2); white-space: nowrap; }

  /* ---- versus: the therapy today, against what CardioKit aims for ----
     A panel, not a continuation of the readout above it: the figures in it
     are a claim about two treatments, not more epidemiology. Inside, the
     same readout cells in two columns, paired row by row, so the eye
     compares across rather than down. Each heading sits directly on the
     keyline that opens its column, and that keyline is drawn at double
     weight, so the heading reads as governing everything beneath it. The
     aim column is keyed to the accent throughout, so a target is never
     mistaken for a published result. Stays two-up at every width: a
     stacked comparison is no longer a comparison. */
  .versus {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--shelf);
    row-gap: 0;   /* rows set their own spacing below, they do not all want the same */
    padding: clamp(1.3rem, 3vw, 2rem) clamp(1.2rem, 3vw, 2rem) clamp(1.6rem, 3.4vw, 2.4rem);
    background: color-mix(in oklab, var(--hd) 4%, transparent);
  }
  @media (width < 46rem) {
    .versus { column-gap: clamp(1rem, 4vw, 1.6rem); }
    .versus .cell b { font-size: var(--step-2); }
  }
  /* the column heading: what the therapy is, then which side of the
     argument it stands on, the label closest to the numbers it labels */
  .versus .head {
    display: grid;
    gap: 0.3rem;
    align-content: end;
    padding-bottom: 0.85rem;
  }
  .versus .head b {
    font-family: var(--display);
    font-weight: 640;
    font-size: var(--step-1);
    line-height: 1.15;
    letter-spacing: -0.02em;
    color: var(--hd);
  }
  .versus .head .tag {
    font-family: var(--mono);
    font-size: 0.72rem;
    line-height: 1.5;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--fg);
  }
  .versus .head.aim b, .versus .head.aim .tag { color: var(--accent); }
  /* one keyline weight through the whole panel: the rule the heading stands
     on and the rule between figure rows are the same line doing the same job */
  .versus .cell { border-top-width: 2px; border-top-color: var(--hd); }
  /* the aim column reads as one run: accent keyline top to bottom */
  .versus .cell.aim { border-top-color: var(--accent); }
  /* every figure row after the first stands off from the one above it */
  .versus .cell:not(.lead) { margin-top: clamp(1.4rem, 3vw, 2.1rem); }

  /* ---- cards ---- */
  /* Column count follows the tile COUNT, not the available width: an
     auto-fit deck that wraps its last tile leaves a visible dead cell.
     :has() lets the grid read how many children it has. */
  .deck-grid {
    display: grid;
    gap: 1px;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: 1fr;          /* tiles share a height, so text tops line up */
    background: var(--rule);
    border: 1px solid var(--rule);
  }
  /* the same logic at the other end: one tile on its own is a block in its
     own right, not half a deck with a dead cell beside it */
  .deck-grid:has(> :first-child:last-child) { grid-template-columns: 1fr; }
  /* four across only when the content column is genuinely wide enough;
     below that a 2 × 2 deck reads better than four cramped columns */
  @media (width >= 82rem) {
    .deck-grid:has(> :nth-child(3):last-child) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .deck-grid:has(> :nth-child(4))            { grid-template-columns: repeat(4, minmax(0, 1fr)); }
    .deck-grid:has(> :nth-child(6):last-child) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  }
  @media (width < 34rem) {
    .deck-grid { grid-template-columns: 1fr; }
  }
  .tile {
    container-type: inline-size;
    background: var(--bg-tile, transparent);
    padding: clamp(1.4rem, 2.6vw, 2.1rem);
    display: grid;
    gap: 0.6rem;
    align-content: start;
    transition: background 0.2s ease;
  }
  .console .tile { --bg-tile: var(--ground); }
  .console.lift .tile { --bg-tile: var(--ground-lift); }
  .sheet .tile { --bg-tile: var(--paper); }
  .sheet.lift .tile { --bg-tile: var(--paper-lift); }
  /* Only a tile that goes somewhere lights up on hover. A plain tile that
     shaded on hover read as a link and sent visitors clicking on nothing. */
  a.tile:hover { background: color-mix(in oklab, var(--accent) 12%, var(--bg-tile)); }
  a.tile {
    color: inherit;
    text-decoration: none;
    display: flex;
    flex-direction: column;
  }
  a.tile h3 { color: var(--hd); transition: color 0.2s ease; }
  a.tile:hover h3 { color: var(--accent); }
  .tile .go {
    margin-top: auto;
    padding-top: 0.6rem;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-family: var(--mono);
    font-size: 0.70rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--accent);
  }
  .tile .go::after { content: "↓"; transition: translate 0.18s ease; }
  a.tile:hover .go::after { translate: 0 3px; }
  .hero .tracks { margin-top: 2.6rem; }
  .tile p { font-size: var(--step-box); max-width: 38ch; color: var(--fg); }
  .tile .step {
    font-family: var(--mono);
    font-size: 0.70rem;
    letter-spacing: 0.2em;
    color: var(--accent);
    max-width: none;   /* a spaced mono eyebrow outruns 34ch long before it is long */
  }
  @container (width < 14rem) {
    .tile { padding: 1.2rem; }
  }

  /* ---- lane: which body of evidence a section belongs to ----
     Target is a filled pill, Protect an outlined one. Same brand blue,
     different weight, so the two tracks tell apart without a new colour. */
  .lane {
    display: inline-block;
    padding: 0.14rem 0.55rem 0.1rem;
    border-radius: 999px;
    line-height: 1.5;
    background: var(--accent);
    color: var(--paper);
  }
  .console .lane { color: var(--ground); }
  .lane.protect {
    background: transparent;
    color: var(--accent);
    box-shadow: inset 0 0 0 1px var(--accent);
  }
  /* in-page anchors land below the sticky top bar */
  [id] { scroll-margin-top: 5.5rem; }

  /* ---- collaborator logo wall ----
     Real marks, held to one optical height and desaturated so the wall
     reads as one object; color returns on hover. Light grounds only. */
  /* One row that wraps, no cells, so an odd count never leaves a hole.
     Every mark is optically centerd on the same baseline. */
  .logos {
    list-style: none;
    display: grid;
    grid-auto-flow: column;
    align-items: center;
    justify-content: space-between;
    gap: clamp(1.25rem, 2.4vw, 2.25rem);
  }
  .logos li { display: grid; place-items: center; }
  /* too narrow for one row: fall to tidy columns rather than an orphan */
  @media (width < 82rem) {
    .logos { grid-auto-flow: row; grid-template-columns: repeat(4, 1fr); row-gap: 2rem; }
  }
  @media (width < 40rem) {
    .logos { grid-template-columns: repeat(2, 1fr); }
  }
  /* optical sizing: a crest needs more height than a wordmark to read
     at the same weight, so height is set per mark type, not per pixel */
  .logos img.crest    { height: clamp(2.2rem, 3.4vw, 2.9rem); }
  .logos img.lockup   { height: clamp(1.9rem, 2.9vw, 2.45rem); }
  .logos img.wordmark { height: clamp(1.5rem, 2.3vw, 1.95rem); }
  .logos img {
    height: clamp(1.7rem, 2.7vw, 2.2rem);
    width: auto;
    max-width: 100%;
    object-fit: contain;
    filter: grayscale(1) contrast(1.1) brightness(0.88);
    opacity: 0.74;
    transition: filter 0.3s ease, opacity 0.3s ease;
  }
  /* marks drawn in pale blues and greys need pushing further down the scale,
     or they read as missing next to a dark crest */
  .logos img.pale { filter: grayscale(1) contrast(1.25) brightness(0.42); opacity: 0.82; }
  .logos li:hover img { filter: none; opacity: 1; }  /* flags and marks that carry meaning in their color keep it */
  .logos img.keep { filter: none; opacity: 0.92; }
  .logos img.flag { height: clamp(1.3rem, 2vw, 1.65rem); box-shadow: 0 0 0 1px var(--rule); }
  /* a support row is a caption, not a claim: smaller and quieter, and it holds
     one row of six until the viewport genuinely cannot take it */
  .logos.support { grid-auto-flow: row; grid-template-columns: repeat(6, minmax(0, 1fr)); row-gap: 1.7rem; }
  .logos.support img { height: clamp(1.2rem, 1.9vw, 1.6rem); }
  .logos.support img.crest { height: clamp(1.35rem, 2.1vw, 1.75rem); }
  .logos.support img.flag { height: clamp(0.95rem, 1.5vw, 1.2rem); }  /* a heavy serif wordmark reads larger than its height, hold it back */
  .logos.support img.tight { height: clamp(1rem, 1.6vw, 1.3rem); }
  /* a two-line lockup spends half its height on the second line, give it more */
  .logos.support img.stack { height: clamp(1.6rem, 2.5vw, 2.1rem); }
  @media (width < 82rem) {
    .logos.support { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  }
  @media (width < 34rem) {
    .logos.support { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }
  .band { display: grid; gap: clamp(1.4rem, 3vw, 2.2rem); }
  /* a band that holds a link list sits tighter, the rule under each link
     already separates the rows, so the heading should not float away */
  .band:has(> .links) { gap: 0.9rem; }
  /* a band that heads a deck sits tighter too: the deck's own border is
     the heading's rule, and it should not float away from it */
  .band:has(> .deck-grid) { gap: 0.9rem; }
  /* .now / .aim: which side of the roadmap a group of indications is on.
     Same convention as the .versus panel, and the keyline that opens the
     deck carries it at the same double weight: plain for what is being
     built and trialled now, accent for what CardioKit is aimed at next. */
  .band.now h4 { color: var(--hd); }
  .band.now > .deck-grid { border-top: 2px solid var(--hd); }
  .band.aim > .deck-grid { border-top: 2px solid var(--accent); }
  /* .mine: a band of EBAMed’s own work, marked the same way as an .ours row */
  .band.mine {
    background: color-mix(in oklab, var(--accent) 9%, transparent);
    box-shadow: inset 0.15rem 0 0 var(--accent);
    border-radius: 0 3px 3px 0;
    padding: 1rem 1.15rem 0.6rem;
  }
  .band h4 {
    font-family: var(--mono);
    font-weight: 400;
    font-size: 0.68rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--accent);
  }

  /* ---- pictograms: one line-drawn glyph per workflow step ---- */
  .picto {
    width: clamp(1.9rem, 3vw, 2.3rem);
    height: auto;
    fill: none;
    stroke: var(--accent);
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
    margin-bottom: 0.2rem;
  }
  .picto .solid { fill: var(--accent); stroke: none; }
  /* the same glyph, used once more as the anchor of a section rail */
  .rail .picto {
    width: clamp(2.6rem, 4.6vw, 3.4rem);
    stroke-width: 1.35;
    margin-top: 0.3rem;
  }
  /* a beam drawn past its stopping point: it deposits nothing there */
  .picto .spent { opacity: 0.42; }
  /* a glyph heading a number cell: what the figure counts */
  .cell .picto { margin-bottom: 0.55rem; }
  /* a figure inside a number cell: the funder's own mark, held small */
  .cell .mark { height: 1.5rem; width: auto; margin-top: 0.9rem; opacity: 0.75; }
  .console .cell .mark { filter: brightness(0) invert(1); }
  /* a flag means nothing in silhouette, keep its colors, add an edge */
  .console .cell .mark.keep { filter: none; opacity: 0.95; box-shadow: 0 0 0 1px var(--rule); }
  .cell .mark.flag { height: 1.15rem; }
  /* a square seal carries its lettering inside the mark: give it height, and no
     frame, the octagon is its own edge */
  .cell .mark.seal { height: 2.6rem; margin-top: 0.55rem; }
  .console .cell .mark.seal { box-shadow: none; opacity: 1; }

  /* ---- slab: image with a plan drawn over it ---- */
  .slab { position: relative; display: grid; isolation: isolate; background: var(--ground); }
  .slab img { grid-area: 1 / 1; width: 100%; height: auto; }
  .slab svg { grid-area: 1 / 1; width: 100%; height: 100%; z-index: 1; }
  .pair { display: grid; gap: var(--shelf); grid-template-columns: repeat(2, minmax(0, 1fr)); }
  @media (width < 52rem) {
    .pair { grid-template-columns: 1fr; }
  }
  /* ---- shot: a photograph that scales with its column ----
     the global img rule sets max-width but not height, so an img carrying
     width/height attributes holds its attribute height while the width
     shrinks to fit, and the picture stretches. height: auto hands the
     ratio back to the file. */
  .shot img { width: 100%; height: auto; }

  figure figcaption {
    font-family: var(--mono);
    font-size: 0.70rem;
    line-height: 1.7;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--fg);
    margin-top: 0.7rem;
  }
  figcaption b { color: var(--hd); font-weight: 400; }

  /* the plan itself: target blue, everything motion forces on it amber */
  .rt .gtv { fill: color-mix(in oklab, var(--blue-lift) 26%, transparent); stroke: var(--blue-lift); stroke-width: 2.6; }
  .rt .ptv { fill: none; stroke: var(--blue-lift); stroke-width: 2.2; stroke-dasharray: 6 5; }
  .rt .itv { fill: color-mix(in oklab, var(--beam) 16%, transparent); stroke: var(--beam); stroke-width: 2.8; stroke-dasharray: 9 7; }
  .rt .ghost { fill: none; stroke: color-mix(in oklab, var(--paper) 60%, transparent); stroke-width: 1.8; stroke-dasharray: 4 5; }
  .rt .beam { fill: color-mix(in oklab, var(--beam) 15%, transparent); }
  .rt .beam-edge { fill: none; stroke: color-mix(in oklab, var(--beam) 60%, transparent); stroke-width: 1.6; }
  /* the big labels sit against the ventricular wall they are talking about; the small
     ones carry what the margin costs, down in the quiet corner of the slice */
  .rt text { font-family: var(--mono); font-size: 23px; letter-spacing: 0.1em; fill: var(--paper); paint-order: stroke; stroke: color-mix(in oklab, black 55%, transparent); stroke-width: 4; }
  .rt text.small { font-size: 19px; letter-spacing: 0.08em; }
  .rt text.blue { fill: var(--blue-lift); }
  .rt text.amber { fill: var(--beam); }

  /* ---- biplane ---- */
  .biplane { display: grid; gap: 1rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .biplane figure { display: grid; gap: 0.5rem; }
  .biplane .scope { border: 1px solid var(--rule); }
  @media (width < 34rem) {
    .biplane { grid-template-columns: 1fr; }
  }

  .probe-fig { margin-top: 0.6rem; }
  .probe-fig img { width: min(13rem, 70%); height: auto; display: block; }
  @media (width < 60rem) {
    .probe-fig img { width: min(11rem, 55%); }
  }

  /* ---- rhythm strip ---- */
  .strip { display: block; width: 100%; height: clamp(120px, 18vh, 170px); }
  .strip .gmin { stroke: color-mix(in oklab, var(--ink) 7%, transparent); stroke-width: 1; }
  .strip .gmaj { stroke: color-mix(in oklab, var(--ink) 13%, transparent); stroke-width: 1; }
  .strip .trace {
    fill: none;
    stroke: var(--pulse);
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
  }
  .strip .onset { stroke: var(--ink-soft); stroke-width: 1; stroke-dasharray: 4 4; }
  .strip text {
    font-family: var(--mono);
    font-size: 12px;
    letter-spacing: 0.16em;
    fill: var(--fg);
  }
  .strip text.hot { fill: var(--pulse); }

  /* ---- circuit ---- */
  .circuit { width: 100%; height: auto; max-width: 26rem; }
  .circuit .wall { fill: none; stroke: color-mix(in oklab, var(--hd) 30%, transparent); stroke-width: 26; }
  .circuit .cavity { fill: color-mix(in oklab, var(--hd) 5%, transparent); }
  .circuit .scar { fill: none; stroke: color-mix(in oklab, var(--pulse) 62%, transparent); stroke-width: 26; }
  .circuit .loop { fill: none; stroke: var(--pulse); stroke-width: 2.6; stroke-linecap: round; }
  .circuit .dose { fill: none; stroke: var(--blue); stroke-width: 2; stroke-dasharray: 7 5; }
  .circuit .lead { stroke: var(--rule); stroke-width: 1; }
  .circuit text { font-family: var(--mono); font-size: 12px; letter-spacing: 0.1em; fill: var(--fg); }
  .circuit text.blue { fill: var(--blue); }
  .circuit text.hot { fill: var(--pulse); }
  .circuit .mass { fill: color-mix(in oklab, var(--pulse) 32%, transparent); stroke: var(--pulse); stroke-width: 2; }
  .circuit .outflow { fill: none; stroke: color-mix(in oklab, var(--hd) 30%, transparent); stroke-width: 26; stroke-linecap: butt; }
  .circuit .margin { fill: none; stroke: var(--beam); stroke-width: 2.4; stroke-dasharray: 8 6; opacity: 0.85; }
  .circuit .beam { fill: color-mix(in oklab, var(--beam) 18%, transparent); stroke: color-mix(in oklab, var(--beam) 60%, transparent); stroke-width: 1.4; }
  .circuit text.amber { fill: var(--beam); }
  .console .circuit .wall, .console .circuit .outflow { stroke: color-mix(in oklab, var(--mist) 32%, transparent); }
  .console .circuit .cavity { fill: color-mix(in oklab, var(--mist) 8%, transparent); }
  .console .circuit text.blue { fill: var(--blue-lift); }
  .circuit.oar { max-width: 30rem; }

  /* ---- room ---- */
  .room { display: block; width: 100%; height: auto; max-width: 52rem; margin-inline: auto; }
  .room .frame { stroke: var(--rule); stroke-width: 1.5; }
  .room .solid { fill: color-mix(in oklab, var(--mist) 16%, transparent); stroke: color-mix(in oklab, var(--mist) 55%, transparent); stroke-width: 1.5; }
  .room .body { fill: color-mix(in oklab, var(--mist) 9%, transparent); stroke: color-mix(in oklab, var(--mist) 45%, transparent); stroke-width: 1.5; }
  .room .organ { fill: color-mix(in oklab, var(--mist) 14%, transparent); stroke: color-mix(in oklab, var(--mist) 50%, transparent); stroke-width: 1.5; }
  .room .marker { fill: color-mix(in oklab, var(--mist) 75%, var(--ground-lift)); stroke: color-mix(in oklab, var(--mist) 55%, transparent); stroke-width: 1; }
  .room .sight { fill: none; stroke: color-mix(in oklab, var(--mist) 45%, transparent); stroke-width: 1.3; stroke-dasharray: 2 6; stroke-linecap: round; }
  .room .sector { fill: color-mix(in oklab, var(--mist) 20%, transparent); stroke: color-mix(in oklab, var(--mist) 55%, transparent); stroke-width: 1.2; stroke-dasharray: 4 4; }
  .room .beam-fill { fill: color-mix(in oklab, var(--beam) 22%, transparent); }
  .room .beam-edge { fill: none; stroke: var(--beam); stroke-width: 1.5; stroke-dasharray: 6 5; opacity: 0.8; }
  .room .tgt { fill: var(--blue-lift); }
  .room .tgt-ring { fill: none; stroke: var(--blue-lift); stroke-width: 2; stroke-dasharray: 5 4; }
  .room .iso { fill: none; stroke: var(--blue-lift); stroke-width: 1.5; stroke-dasharray: 3 3; opacity: 0.7; }
  .room .axis { fill: none; stroke: var(--breath); stroke-width: 2; stroke-linecap: round; }
  .room .head { fill: var(--breath); stroke: none; }
  .room text { font-family: var(--mono); font-size: 14px; letter-spacing: 0.1em; fill: var(--fg); }
  .room text.blue { fill: var(--blue-lift); }
  .room .lead { stroke: var(--rule); stroke-width: 1; }
  .room .electrode { fill: color-mix(in oklab, var(--pulse) 35%, var(--ground-lift)); stroke: color-mix(in oklab, var(--pulse) 70%, transparent); stroke-width: 1; }
  .room .wire { fill: none; stroke: color-mix(in oklab, var(--pulse) 50%, transparent); stroke-width: 1.3; stroke-linecap: round; }
  .room .ecg { fill: none; stroke: var(--pulse); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }
  .room text.pulse { fill: color-mix(in oklab, var(--pulse) 80%, var(--fg)); }
  /* the one output of the system: an amber gate line from the probe to the machine */
  .room .gate { fill: none; stroke: var(--beam); stroke-width: 1.6; stroke-linejoin: round; opacity: 0.9; }
  .room .gate-head { fill: var(--beam); stroke: none; }
  .room text.amber { fill: var(--beam); }

  /* ---- seq: the session as one figure, four stages then the gate ----
     The stage row is chrome (rule + heading ink). Only the decision strip
     carries the functional signals: coral = ECG, cyan = respiration,
     amber = the window where the beam is released. */
  .seq { display: block; width: 100%; height: auto; max-width: 60rem; margin-inline: auto; }
  .seq .card { fill: none; stroke: var(--rule); stroke-width: 1.5; }
  /* the last stage is the one the strip below explains */
  .seq .card.live { stroke: color-mix(in oklab, var(--beam) 45%, var(--rule)); }
  .seq .step { fill: none; stroke: color-mix(in oklab, var(--fg) 55%, transparent); stroke-width: 1.4; }
  .seq .step-head { fill: color-mix(in oklab, var(--fg) 55%, transparent); stroke: none; }
  .seq text { font-family: var(--mono); font-size: 13px; letter-spacing: 0.1em; fill: var(--hd); }
  .seq text.num { font-size: 10px; letter-spacing: 0.2em; fill: var(--accent); }
  .seq text.sub { font-size: 10px; letter-spacing: 0.06em; fill: var(--fg); }
  .seq text.lane { font-size: 10px; letter-spacing: 0.16em; fill: var(--fg); }
  .seq text.note { font-size: 10px; letter-spacing: 0.08em; fill: var(--fg); }
  .seq text.amber { fill: var(--beam); }
  .seq text.blue { fill: var(--accent); }
  .seq text.pulse { fill: color-mix(in oklab, var(--pulse) 80%, var(--fg)); }
  .seq text.breath { fill: color-mix(in oklab, var(--breath) 74%, var(--fg)); }
  /* the beam-on label sits on the amber bar itself, so it takes the ground */
  .seq text.onlab { font-size: 10px; letter-spacing: 0.14em; fill: var(--ground); }

  .seq .gmin { stroke: color-mix(in oklab, var(--fg) 13%, transparent); stroke-width: 1; }
  .seq .gmaj { stroke: color-mix(in oklab, var(--fg) 24%, transparent); stroke-width: 1; }
  .seq .base { stroke: color-mix(in oklab, var(--fg) 22%, transparent); stroke-width: 1; stroke-dasharray: 2 6; }
  .seq .trace { fill: none; stroke: var(--pulse); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
  .seq .motion { fill: none; stroke: var(--breath); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
  .seq .exhale { fill: color-mix(in oklab, var(--breath) 12%, transparent); }
  .seq .gate { fill: color-mix(in oklab, var(--beam) 13%, transparent); }
  .seq .edge { stroke: var(--beam); stroke-width: 1; stroke-dasharray: 3 6; opacity: 0.8; }
  .seq .hold { fill: color-mix(in oklab, var(--fg) 10%, transparent); stroke: var(--rule); stroke-width: 1; }
  .seq .on { fill: var(--beam); stroke: none; }
  .seq .lead { stroke: var(--rule); stroke-width: 1; }

  /* A wide figure keeps its own scale rather than shrinking its labels out of
     legibility: below the fold width the reader pans it instead. */
  /* min-width: 0 keeps the wide child inside the grid track instead of
     stretching the whole page sideways */
  .pan { min-width: 0; overflow-x: auto; overscroll-behavior-x: contain; }
  .pan > .seq { min-width: 44rem; }

  /* ---- people ---- */
  .roster {
    list-style: none;
    display: grid;
    gap: clamp(1.5rem, 3vw, 2.5rem);
    /* auto-FILL, not auto-fit: a section with only two people keeps the
       same column width as a section with six, instead of collapsing the
       empty tracks and blowing the two portraits up to half the page. */
    grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
  }
  /* a card whose bio runs short still gets the full row height from its
     tallest neighbour. Without this the leftover is shared out between the
     auto rows, and the portrait, name and role of a one-line entry drift
     down out of line with a two-line one. Same fix as .tile. */
  .who { display: grid; gap: 0.45rem; align-content: start; }
  .who img {
    width: 100%;
    aspect-ratio: 4 / 5;
    object-fit: cover;
    object-position: center 18%;
    background: var(--paper-lift);
    filter: grayscale(1) contrast(1.06);
    transition: filter 0.35s ease, scale 0.35s ease;
    margin-bottom: 0.7rem;
  }
  .who:hover img { filter: grayscale(0) contrast(1); scale: 1.012; }
  .who h3 { font-size: var(--step-0); }
  .who .role {
    font-family: var(--mono);
    font-size: 0.68rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--accent);
    min-height: 2lh;              /* a two-line role can't shove the bio down */
  }
  .who p { font-size: var(--step-box); max-width: 36ch; }

  /* ---- trail (dated history) ----
     Two-column rhythm, entries hang off one vertical spine, so a long run
     of dates reads as a single continuous line rather than as a stack of
     cards. No color coding, the eras are separated by the sections they
     sit in, which keeps the three reserved colors free.
     .ours marks an entry EBAMed took part in: filled node, accent date,
     a tinted row with an accent edge, and an EBAMed badge over the title. */
  .trail {
    --spine-gap: clamp(1.25rem, 3vw, 2.25rem);
    --spine: calc(6.5rem + var(--spine-gap) / 2);
    /* how far the .ours accent edge hangs left of the row. It shifts that
       row's padding box, which is what the spine and node are positioned
       against, so it has to be added back here or the axis breaks. */
    --hang: 0rem;
    display: grid;
    list-style: none;
  }
  .trail > li {
    position: relative;
    display: grid;
    grid-template-columns: 6.5rem minmax(0, 1fr);
    gap: 0 var(--spine-gap);
    padding-block: 1.15rem;
  }
  /* the spine is drawn per entry, so it stops at the first and last node */
  .trail > li::before {
    content: "";
    position: absolute;
    left: calc(var(--spine) + var(--hang));
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--rule);
  }
  .trail > li:first-child::before { top: 1.55rem; }
  .trail > li:last-child::before { bottom: auto; height: 1.55rem; }
  .trail > li::after {
    content: "";
    position: absolute;
    left: calc(var(--spine) + var(--hang) - 0.2rem);
    top: 1.36rem;
    width: 0.4rem;
    height: 0.4rem;
    border-radius: 50%;
    background: color-mix(in oklab, var(--fg) 70%, transparent);
  }
  .trail > li.ours::after {
    left: calc(var(--spine) + var(--hang) - 0.34rem);
    top: 1.22rem;
    width: 0.68rem;
    height: 0.68rem;
    background: var(--accent);
    box-shadow: 0 0 0 0.28rem color-mix(in oklab, var(--accent) 22%, transparent);
  }
  /* the row itself is tinted and edged. The edge hangs a little outside the
     row and the padding puts the content back, so the spine, the node and
     every date stay on the same axis as the rest of the trail while the
     date keeps clear air between it and the accent edge. */
  .trail > li.ours {
    --hang: 0.75rem;
    background: color-mix(in oklab, var(--accent) 10%, transparent);
    border-left: 2px solid var(--accent);
    margin-left: calc(-2px - var(--hang));
    padding-left: var(--hang);
    border-radius: 0 3px 3px 0;
  }
  .trail > li.ours::before { background: color-mix(in oklab, var(--accent) 45%, transparent); }
  /* badge: says whose step this is without spending a title on it */
  .trail .mine {
    font-family: var(--mono);
    font-size: 0.62rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--accent);
    border: 1px solid color-mix(in oklab, var(--accent) 55%, transparent);
    border-radius: 999px;
    padding: 0.1rem 0.45rem;
    margin: 0 0 0.35rem;
    width: max-content;
    max-width: none;
  }
  .trail time {
    font-family: var(--mono);
    font-size: 0.72rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--fg);
    padding-top: 0.35rem;
  }
  .trail > li.ours time { color: var(--accent); }
  .trail h3 { font-size: var(--step-0); }
  .trail p { font-size: var(--step-box); margin-top: 0.35rem; max-width: 68ch; }
  .trail .src {
    font-family: var(--mono);
    font-size: 0.66rem;
    letter-spacing: 0.08em;
    margin-top: 0.5rem;
  }
  .trail .src a { color: var(--accent); text-decoration: none; }
  .trail .src a:hover { text-decoration: underline; }

  /* ---- pages: the first page of a paper, hung beside its trail entry ----
     A .paper entry gains a third column between the date and the text, and
     each .page in it is a link wrapping the rendered first page of the
     publication, like a reprint left on the bench. Two pages in one entry
     stack, the second peeking out from behind the first. Hover or focus
     brings a page forward and lifts it. The spine is still positioned
     against the 6.5rem date column, so nothing here moves the axis. */
  .trail { --page-w: clamp(6rem, 11vw, 9.75rem); --page-peek: 0.6rem; }
  .trail > li.paper {
    grid-template-columns: 6.5rem var(--page-w) minmax(0, 1fr);
    grid-template-areas: "date pages body";
  }
  .trail > li.paper > time  { grid-area: date; }
  .trail > li.paper > .pages { grid-area: pages; }
  .trail > li.paper > div:not(.pages) { grid-area: body; }
  .pages {
    display: grid;
    align-content: start;
    margin-top: 0.3rem;
  }
  .pages > .page { grid-area: 1 / 1; }
  /* a pair needs room below and to the right for the copy behind */
  .pages:has(> .page:nth-child(2)) { margin-bottom: var(--page-peek); }
  .page {
    display: block;
    position: relative;
    z-index: 1;
    aspect-ratio: 3 / 4;
    overflow: hidden;
    background: #fff;
    border: 1px solid var(--rule);
    border-radius: 2px;
    box-shadow: 0 1px 2px rgb(0 0 0 / 0.08), 0 10px 24px -14px rgb(0 0 0 / 0.35);
    transition: translate 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
  }
  .page img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: top;
  }
  .page:hover, .page:focus-visible {
    z-index: 3;
    translate: 0 -3px;
    border-color: var(--accent);
    box-shadow: 0 2px 4px rgb(0 0 0 / 0.10), 0 18px 32px -16px rgb(0 0 0 / 0.45);
  }
  .pages > .page:nth-child(2) { z-index: 0; translate: var(--page-peek) var(--page-peek); }
  .pages > .page:nth-child(2):hover,
  .pages > .page:nth-child(2):focus-visible { translate: var(--page-peek) calc(var(--page-peek) - 3px); }
  @media (width < 40rem) {
    .trail { --page-w: 5.25rem; }
    /* one column below, the date goes on top and the page sits beside the text */
    .trail > li.paper {
      grid-template-columns: var(--page-w) minmax(0, 1fr);
      grid-template-areas: "date date" "pages body";
      column-gap: 1rem;
    }
    .pages { margin-top: 0.2rem; }
  }
  @media (prefers-reduced-motion: reduce) {
    .page { transition: none; }
  }
  @media (width < 40rem) {
    .trail { --spine: 0.2rem; }
    .trail > li { grid-template-columns: 1fr; gap: 0.3rem; padding-left: 1.5rem; }
    /* stacked to one column, the date already sits clear of the edge, so the
       row keeps the shared padding and the edge sits back on its boundary */
    .trail > li.ours { --hang: 0rem; margin-left: -2px; padding-left: 1.5rem; }
  }

  /* ---- map (contact) ---- */
  .map iframe {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    border: 1px solid var(--rule);
    background: var(--paper-lift);
    filter: grayscale(1) contrast(1.04);
    transition: filter 0.35s ease;
  }
  .map:hover iframe { filter: none; }
  .map figcaption {
    margin-top: 0.7rem;
    font-size: var(--step-box);
    color: var(--fg);
    max-width: 68ch;
  }
  .map figcaption a { color: var(--accent); text-decoration: none; }
  .map figcaption a:hover { text-decoration: underline; }
  @media (width < 40rem) { .map iframe { aspect-ratio: 4 / 3; } }

  /* ---- notes / findings ---- */
  .notes { display: grid; gap: 0.9rem; }
  .notes li { list-style: none; padding-left: 1.6rem; position: relative; font-size: var(--step-box); }
  .notes li::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0.62em;
    width: 0.7rem;
    height: 1px;
    background: var(--accent);
  }
  .notes b { color: var(--hd); font-weight: 600; }

  /* the rule under a section spans the whole content column, not the text measure */
  .fine { font-size: var(--step-box); color: var(--fg); border-top: 1px solid var(--rule); padding-top: 1rem; max-width: none; }

  /* footnote refs: held below body copy, the asterisk carries the pointer, the
     line itself is there to be checkable, not to be read in flow */
  .refs {
    font-size: 0.70rem;
    line-height: 1.8;
    color: color-mix(in oklab, var(--fg) 62%, transparent);
    border-top: 1px solid var(--rule);
    padding-top: 0.9rem;
    max-width: none;
  }
  .refs a { color: inherit; text-decoration: underline; text-underline-offset: 0.18em; }
  .refs a:hover { color: var(--accent); }
  /* the marker tying a figure to .refs, raised, never resized by the parent */
  .cell sup, .notes sup, .versus .head sup { font-size: 0.7em; line-height: 0; color: var(--accent); }

  .links { display: grid; gap: 0.85rem; }
  .links li { list-style: none; font-size: var(--step-box); border-bottom: 1px solid var(--rule); padding-bottom: 0.85rem; }
  .links a { color: var(--hd); text-decoration: none; }
  .links a:hover { color: var(--accent); text-decoration: underline; }

  /* ---- form ---- */
  form { display: grid; gap: 1rem; max-width: 30rem; }
  form label {
    font-family: var(--mono);
    font-size: 0.62rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--fg);
    display: grid;
    gap: 0.4rem;
  }
  form input, form textarea {
    font: inherit;
    font-size: var(--step--1);
    color: var(--hd);
    background: color-mix(in oklab, var(--hd) 6%, transparent);
    border: 1px solid var(--rule);
    border-radius: 2px;
    padding: 0.75rem 0.85rem;
    letter-spacing: normal;
    text-transform: none;
  }
  form textarea { field-sizing: content; min-height: 6lh; resize: vertical; }
  form input:focus, form textarea:focus { border-color: var(--accent); outline: none; }
  form input:not(:placeholder-shown):invalid { border-color: var(--pulse); }
  /* dim the send button while the form can't be sent */
  form:has(input:invalid, textarea:invalid) .btn { opacity: 0.5; }
  form .btn { border: 1px solid var(--blue); cursor: pointer; justify-self: start; }

  /* ---- footer ---- */
  .footer { padding: clamp(3rem, 6vw, 4.5rem) 0 2rem; }
  .footer .cols {
    display: grid;
    gap: var(--shelf);
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  }
  .footer h4 {
    font-family: var(--mono);
    font-weight: 400;
    font-size: 0.68rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--hd);
    margin-bottom: 1rem;
  }
  .footer ul { list-style: none; display: grid; gap: 0.5rem; }
  .footer, .footer p, .footer li { font-size: var(--step--1); }
  .footer a { text-decoration: none; }
  .footer a:hover { color: var(--hd); text-decoration: underline; }
  .footer .base {
    margin-top: clamp(2.5rem, 5vw, 3.5rem);
    padding-top: 1.4rem;
    border-top: 1px solid var(--rule);
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem 2rem;
    justify-content: space-between;
    font-family: var(--mono);
    font-size: 0.66rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: color-mix(in oklab, var(--fg) 75%, transparent);
  }}

/* ---------------------------------------------------------- */
@layer motion {
  /* One 6 s loop drives everything: six beats at 60 bpm, two breaths at 20 / min. */

  /* two dots ride the traces, one 50 ms step per keyframe, in step with the sweep line */
  @keyframes ride-ecg {
    0.00%{transform:translate(0px,110.0px)} 0.83%{transform:translate(10px,109.8px)} 1.67%{transform:translate(20px,101.1px)} 2.50%{transform:translate(30px,109.0px)} 3.33%{transform:translate(40px,109.9px)}
    4.17%{transform:translate(50px,38.7px)} 5.00%{transform:translate(60px,110.0px)} 5.83%{transform:translate(70px,109.7px)} 6.67%{transform:translate(80px,105.7px)} 7.50%{transform:translate(90px,97.9px)}
    8.33%{transform:translate(100px,91.9px)} 9.17%{transform:translate(110px,99.3px)} 10.00%{transform:translate(120px,109.5px)} 10.83%{transform:translate(130px,109.5px)} 11.67%{transform:translate(140px,109.6px)}
    12.50%{transform:translate(150px,109.6px)} 13.33%{transform:translate(160px,109.6px)} 14.17%{transform:translate(170px,109.7px)} 15.00%{transform:translate(180px,109.7px)} 15.83%{transform:translate(190px,109.7px)}
    16.67%{transform:translate(200px,109.8px)} 17.50%{transform:translate(210px,109.8px)} 18.33%{transform:translate(220px,101.1px)} 19.17%{transform:translate(230px,109.0px)} 20.00%{transform:translate(240px,109.9px)}
    20.83%{transform:translate(250px,38.7px)} 21.67%{transform:translate(260px,110.0px)} 22.50%{transform:translate(270px,109.7px)} 23.33%{transform:translate(280px,105.7px)} 24.17%{transform:translate(290px,97.9px)}
    25.00%{transform:translate(300px,91.9px)} 25.83%{transform:translate(310px,99.3px)} 26.67%{transform:translate(320px,109.5px)} 27.50%{transform:translate(330px,109.5px)} 28.33%{transform:translate(340px,109.6px)}
    29.17%{transform:translate(350px,109.6px)} 30.00%{transform:translate(360px,109.6px)} 30.83%{transform:translate(370px,109.7px)} 31.67%{transform:translate(380px,109.7px)} 32.50%{transform:translate(390px,109.7px)}
    33.33%{transform:translate(400px,109.8px)} 34.17%{transform:translate(410px,109.8px)} 35.00%{transform:translate(420px,101.1px)} 35.83%{transform:translate(430px,109.0px)} 36.67%{transform:translate(440px,109.9px)}
    37.50%{transform:translate(450px,38.7px)} 38.33%{transform:translate(460px,110.0px)} 39.17%{transform:translate(470px,109.7px)} 40.00%{transform:translate(480px,105.7px)} 40.83%{transform:translate(490px,97.9px)}
    41.67%{transform:translate(500px,91.9px)} 42.50%{transform:translate(510px,99.3px)} 43.33%{transform:translate(520px,109.5px)} 44.17%{transform:translate(530px,109.5px)} 45.00%{transform:translate(540px,109.6px)}
    45.83%{transform:translate(550px,109.6px)} 46.67%{transform:translate(560px,109.6px)} 47.50%{transform:translate(570px,109.7px)} 48.33%{transform:translate(580px,109.7px)} 49.17%{transform:translate(590px,109.7px)}
    50.00%{transform:translate(600px,109.8px)} 50.83%{transform:translate(610px,109.8px)} 51.67%{transform:translate(620px,101.1px)} 52.50%{transform:translate(630px,109.0px)} 53.33%{transform:translate(640px,109.9px)}
    54.17%{transform:translate(650px,38.7px)} 55.00%{transform:translate(660px,110.0px)} 55.83%{transform:translate(670px,109.7px)} 56.67%{transform:translate(680px,105.7px)} 57.50%{transform:translate(690px,97.9px)}
    58.33%{transform:translate(700px,91.9px)} 59.17%{transform:translate(710px,99.3px)} 60.00%{transform:translate(720px,109.5px)} 60.83%{transform:translate(730px,109.5px)} 61.67%{transform:translate(740px,109.6px)}
    62.50%{transform:translate(750px,109.6px)} 63.33%{transform:translate(760px,109.6px)} 64.17%{transform:translate(770px,109.7px)} 65.00%{transform:translate(780px,109.7px)} 65.83%{transform:translate(790px,109.7px)}
    66.67%{transform:translate(800px,109.8px)} 67.50%{transform:translate(810px,109.8px)} 68.33%{transform:translate(820px,101.1px)} 69.17%{transform:translate(830px,109.0px)} 70.00%{transform:translate(840px,109.9px)}
    70.83%{transform:translate(850px,38.7px)} 71.67%{transform:translate(860px,110.0px)} 72.50%{transform:translate(870px,109.7px)} 73.33%{transform:translate(880px,105.7px)} 74.17%{transform:translate(890px,97.9px)}
    75.00%{transform:translate(900px,91.9px)} 75.83%{transform:translate(910px,99.3px)} 76.67%{transform:translate(920px,109.5px)} 77.50%{transform:translate(930px,109.5px)} 78.33%{transform:translate(940px,109.6px)}
    79.17%{transform:translate(950px,109.6px)} 80.00%{transform:translate(960px,109.6px)} 80.83%{transform:translate(970px,109.7px)} 81.67%{transform:translate(980px,109.7px)} 82.50%{transform:translate(990px,109.7px)}
    83.33%{transform:translate(1000px,109.8px)} 84.17%{transform:translate(1010px,109.8px)} 85.00%{transform:translate(1020px,101.1px)} 85.83%{transform:translate(1030px,109.0px)} 86.67%{transform:translate(1040px,109.9px)}
    87.50%{transform:translate(1050px,38.7px)} 88.33%{transform:translate(1060px,110.0px)} 89.17%{transform:translate(1070px,109.7px)} 90.00%{transform:translate(1080px,105.7px)} 90.83%{transform:translate(1090px,97.9px)}
    91.67%{transform:translate(1100px,91.9px)} 92.50%{transform:translate(1110px,99.3px)} 93.33%{transform:translate(1120px,109.5px)} 94.17%{transform:translate(1130px,109.6px)} 95.00%{transform:translate(1140px,109.6px)}
    95.83%{transform:translate(1150px,109.7px)} 96.67%{transform:translate(1160px,109.8px)} 97.50%{transform:translate(1170px,109.8px)} 98.33%{transform:translate(1180px,109.9px)} 99.17%{transform:translate(1190px,109.9px)}
    100.00%{transform:translate(1200px,110.0px)}
  }
  @keyframes ride-motion {
    0.00%{transform:translate(0px,219.1px)} 0.83%{transform:translate(10px,217.4px)} 1.67%{transform:translate(20px,217.1px)} 2.50%{transform:translate(30px,217.1px)} 3.33%{transform:translate(40px,217.2px)}
    4.17%{transform:translate(50px,217.2px)} 5.00%{transform:translate(60px,215.6px)} 5.83%{transform:translate(70px,212.5px)} 6.67%{transform:translate(80px,210.6px)} 7.50%{transform:translate(90px,211.8px)}
    8.33%{transform:translate(100px,215.5px)} 9.17%{transform:translate(110px,219.4px)} 10.00%{transform:translate(120px,222.3px)} 10.83%{transform:translate(130px,223.8px)} 11.67%{transform:translate(140px,225.4px)}
    12.50%{transform:translate(150px,227.0px)} 13.33%{transform:translate(160px,228.5px)} 14.17%{transform:translate(170px,230.1px)} 15.00%{transform:translate(180px,232.4px)} 15.83%{transform:translate(190px,234.9px)}
    16.67%{transform:translate(200px,236.3px)} 17.50%{transform:translate(210px,237.4px)} 18.33%{transform:translate(220px,239.6px)} 19.17%{transform:translate(230px,242.3px)} 20.00%{transform:translate(240px,245.0px)}
    20.83%{transform:translate(250px,247.1px)} 21.67%{transform:translate(260px,247.2px)} 22.50%{transform:translate(270px,245.9px)} 23.33%{transform:translate(280px,245.5px)} 24.17%{transform:translate(290px,246.7px)}
    25.00%{transform:translate(300px,249.8px)} 25.83%{transform:translate(310px,254.1px)} 26.67%{transform:translate(320px,258.1px)} 27.50%{transform:translate(330px,259.8px)} 28.33%{transform:translate(340px,260.5px)}
    29.17%{transform:translate(350px,261.2px)} 30.00%{transform:translate(360px,261.9px)} 30.83%{transform:translate(370px,262.5px)} 31.67%{transform:translate(380px,263.2px)} 32.50%{transform:translate(390px,264.2px)}
    33.33%{transform:translate(400px,265.0px)} 34.17%{transform:translate(410px,264.1px)} 35.00%{transform:translate(420px,262.6px)} 35.83%{transform:translate(430px,261.5px)} 36.67%{transform:translate(440px,260.9px)}
    37.50%{transform:translate(450px,259.6px)} 38.33%{transform:translate(460px,255.6px)} 39.17%{transform:translate(470px,250.1px)} 40.00%{transform:translate(480px,246.1px)} 40.83%{transform:translate(490px,243.2px)}
    41.67%{transform:translate(500px,240.5px)} 42.50%{transform:translate(510px,239.5px)} 43.33%{transform:translate(520px,239.0px)} 44.17%{transform:translate(530px,237.6px)} 45.00%{transform:translate(540px,234.4px)}
    45.83%{transform:translate(550px,231.3px)} 46.67%{transform:translate(560px,228.5px)} 47.50%{transform:translate(570px,225.9px)} 48.33%{transform:translate(580px,223.4px)} 49.17%{transform:translate(590px,221.8px)}
    50.00%{transform:translate(600px,221.0px)} 50.83%{transform:translate(610px,220.1px)} 51.67%{transform:translate(620px,218.3px)} 52.50%{transform:translate(630px,216.8px)} 53.33%{transform:translate(640px,216.5px)}
    54.17%{transform:translate(650px,216.9px)} 55.00%{transform:translate(660px,215.1px)} 55.83%{transform:translate(670px,212.0px)} 56.67%{transform:translate(680px,211.9px)} 57.50%{transform:translate(690px,213.5px)}
    58.33%{transform:translate(700px,215.1px)} 59.17%{transform:translate(710px,217.2px)} 60.00%{transform:translate(720px,220.3px)} 60.83%{transform:translate(730px,223.7px)} 61.67%{transform:translate(740px,225.5px)}
    62.50%{transform:translate(750px,227.2px)} 63.33%{transform:translate(760px,229.0px)} 64.17%{transform:translate(770px,230.8px)} 65.00%{transform:translate(780px,232.5px)} 65.83%{transform:translate(790px,233.9px)}
    66.67%{transform:translate(800px,236.6px)} 67.50%{transform:translate(810px,239.5px)} 68.33%{transform:translate(820px,241.4px)} 69.17%{transform:translate(830px,242.8px)} 70.00%{transform:translate(840px,244.1px)}
    70.83%{transform:translate(850px,245.8px)} 71.67%{transform:translate(860px,245.9px)} 72.50%{transform:translate(870px,243.8px)} 73.33%{transform:translate(880px,244.6px)} 74.17%{transform:translate(890px,247.9px)}
    75.00%{transform:translate(900px,251.4px)} 75.83%{transform:translate(910px,254.1px)} 76.67%{transform:translate(920px,256.7px)} 77.50%{transform:translate(930px,259.3px)} 78.33%{transform:translate(940px,260.9px)}
    79.17%{transform:translate(950px,261.8px)} 80.00%{transform:translate(960px,262.8px)} 80.83%{transform:translate(970px,263.7px)} 81.67%{transform:translate(980px,263.9px)} 82.50%{transform:translate(990px,263.4px)}
    83.33%{transform:translate(1000px,262.9px)} 84.17%{transform:translate(1010px,263.4px)} 85.00%{transform:translate(1020px,263.9px)} 85.83%{transform:translate(1030px,262.7px)} 86.67%{transform:translate(1040px,260.6px)}
    87.50%{transform:translate(1050px,258.6px)} 88.33%{transform:translate(1060px,254.7px)} 89.17%{transform:translate(1070px,248.4px)} 90.00%{transform:translate(1080px,244.0px)} 90.83%{transform:translate(1090px,242.8px)}
    91.67%{transform:translate(1100px,242.6px)} 92.50%{transform:translate(1110px,241.6px)} 93.33%{transform:translate(1120px,239.4px)} 94.17%{transform:translate(1130px,236.8px)} 95.00%{transform:translate(1140px,234.1px)}
    95.83%{transform:translate(1150px,231.5px)} 96.67%{transform:translate(1160px,228.9px)} 97.50%{transform:translate(1170px,226.3px)} 98.33%{transform:translate(1180px,224.2px)} 99.17%{transform:translate(1190px,221.9px)}
    100.00%{transform:translate(1200px,219.1px)}
  }
  .signal .rider   { opacity: 1; animation: ride-ecg    6s linear infinite; filter: drop-shadow(0 0 8px var(--pulse)); }
  .signal .rider-m { opacity: 1; animation: ride-motion 6s linear infinite; filter: drop-shadow(0 0 8px var(--breath)); }

  /* the record dot blinks on the same clock, one pulse per second */
  @keyframes blink { 0%, 55% { opacity: 1; } 60%, 95% { opacity: 0.25; } 100% { opacity: 1; } }
  .signals .key.live::before { animation: blink 1s steps(1, end) infinite; }

  /* the instant under decision sweeps the 6 s loop, crossing both lanes */
  @keyframes sweep { to { transform: translate(1200px, 0); } }
  .signal .sweep { animation: sweep 6s linear infinite; }

  /* the target rides the same cardio-respiratory motion as the cyan trace */
  @keyframes track {
    0.00%{transform:translate(15.3px,103.2px)} 0.83%{transform:translate(15.9px,107.3px)} 1.67%{transform:translate(15.9px,107.4px)} 2.50%{transform:translate(15.8px,106.4px)} 3.33%{transform:translate(15.8px,106.9px)}
    4.17%{transform:translate(15.9px,107.4px)} 5.00%{transform:translate(18.4px,101.3px)} 5.83%{transform:translate(23.3px,90.4px)} 6.67%{transform:translate(25.7px,86.0px)} 7.50%{transform:translate(24.6px,86.5px)}
    8.33%{transform:translate(21.7px,86.5px)} 9.17%{transform:translate(18.2px,87.2px)} 10.00%{transform:translate(15.3px,90.1px)} 10.83%{transform:translate(13.8px,92.0px)} 11.67%{transform:translate(13.3px,89.5px)}
    12.50%{transform:translate(12.6px,85.2px)} 13.33%{transform:translate(12.1px,81.7px)} 14.17%{transform:translate(11.6px,78.0px)} 15.00%{transform:translate(10.7px,72.5px)} 15.83%{transform:translate(9.9px,66.7px)}
    16.67%{transform:translate(9.4px,63.3px)} 17.50%{transform:translate(9.1px,61.3px)} 18.33%{transform:translate(8.4px,56.8px)} 19.17%{transform:translate(7.4px,49.7px)} 20.00%{transform:translate(6.4px,43.2px)}
    20.83%{transform:translate(5.7px,38.5px)} 21.67%{transform:translate(7.6px,28.7px)} 22.50%{transform:translate(12.0px,13.5px)} 23.33%{transform:translate(13.8px,5.9px)} 24.17%{transform:translate(12.7px,6.3px)}
    25.00%{transform:translate(10.0px,7.6px)} 25.83%{transform:translate(6.4px,7.5px)} 26.67%{transform:translate(3.1px,7.8px)} 27.50%{transform:translate(1.5px,8.9px)} 28.33%{transform:translate(1.2px,8.3px)}
    29.17%{transform:translate(0.9px,6.1px)} 30.00%{transform:translate(0.6px,4.1px)} 30.83%{transform:translate(0.5px,3.4px)} 31.67%{transform:translate(0.3px,2.2px)} 32.50%{transform:translate(-0.1px,-0.7px)}
    33.33%{transform:translate(-0.4px,-2.5px)} 34.17%{transform:translate(-0.1px,-0.4px)} 35.00%{transform:translate(0.5px,3.5px)} 35.83%{transform:translate(0.8px,5.6px)} 36.67%{transform:translate(1.0px,6.9px)}
    37.50%{transform:translate(1.5px,10.1px)} 38.33%{transform:translate(4.8px,9.6px)} 39.17%{transform:translate(10.5px,3.9px)} 40.00%{transform:translate(13.6px,4.6px)} 40.83%{transform:translate(14.0px,14.8px)}
    41.67%{transform:translate(13.2px,28.9px)} 42.50%{transform:translate(11.4px,41.6px)} 43.33%{transform:translate(9.6px,51.4px)} 44.17%{transform:translate(9.1px,60.1px)} 45.00%{transform:translate(10.1px,68.3px)}
    45.83%{transform:translate(11.2px,75.5px)} 46.67%{transform:translate(12.0px,81.1px)} 47.50%{transform:translate(12.9px,87.2px)} 48.33%{transform:translate(13.8px,93.5px)} 49.17%{transform:translate(14.4px,97.2px)}
    50.00%{transform:translate(14.6px,98.3px)} 50.83%{transform:translate(14.9px,100.5px)} 51.67%{transform:translate(15.5px,105.0px)} 52.50%{transform:translate(16.1px,108.7px)} 53.33%{transform:translate(16.2px,109.1px)}
    54.17%{transform:translate(16.0px,108.2px)} 55.00%{transform:translate(18.6px,102.7px)} 55.83%{transform:translate(23.5px,91.2px)} 56.67%{transform:translate(25.3px,83.5px)} 57.50%{transform:translate(24.1px,83.0px)}
    58.33%{transform:translate(21.9px,87.5px)} 59.17%{transform:translate(19.0px,93.0px)} 60.00%{transform:translate(16.0px,94.5px)} 60.83%{transform:translate(13.8px,91.8px)} 61.67%{transform:translate(13.0px,87.9px)}
    62.50%{transform:translate(12.6px,84.7px)} 63.33%{transform:translate(11.9px,80.3px)} 64.17%{transform:translate(11.2px,75.6px)} 65.00%{transform:translate(10.7px,72.4px)} 65.83%{transform:translate(10.3px,69.3px)}
    66.67%{transform:translate(9.4px,63.4px)} 67.50%{transform:translate(8.3px,56.1px)} 68.33%{transform:translate(7.6px,51.2px)} 69.17%{transform:translate(7.2px,48.9px)} 70.00%{transform:translate(6.8px,46.1px)}
    70.83%{transform:translate(6.1px,41.5px)} 71.67%{transform:translate(8.1px,32.0px)} 72.50%{transform:translate(12.6px,18.1px)} 73.33%{transform:translate(14.2px,8.1px)} 74.17%{transform:translate(12.3px,3.4px)}
    75.00%{transform:translate(9.4px,3.5px)} 75.83%{transform:translate(6.4px,7.9px)} 76.67%{transform:translate(3.7px,11.3px)} 77.50%{transform:translate(1.7px,10.1px)} 78.33%{transform:translate(0.9px,6.2px)}
    79.17%{transform:translate(0.6px,4.0px)} 80.00%{transform:translate(0.4px,2.5px)} 80.83%{transform:translate(0.0px,0.3px)} 81.67%{transform:translate(-0.1px,-0.5px)} 82.50%{transform:translate(0.2px,1.1px)}
    83.33%{transform:translate(0.4px,2.5px)} 84.17%{transform:translate(0.2px,1.3px)} 85.00%{transform:translate(0.0px,0.2px)} 85.83%{transform:translate(0.4px,2.6px)} 86.67%{transform:translate(1.1px,7.6px)}
    87.50%{transform:translate(1.8px,12.2px)} 88.33%{transform:translate(5.1px,11.6px)} 89.17%{transform:translate(11.1px,7.6px)} 90.00%{transform:translate(14.3px,9.4px)} 90.83%{transform:translate(14.1px,15.5px)}
    91.67%{transform:translate(12.4px,23.7px)} 92.50%{transform:translate(10.6px,36.1px)} 93.33%{transform:translate(9.5px,50.5px)} 94.17%{transform:translate(9.3px,61.4px)} 95.00%{transform:translate(10.0px,67.7px)}
    95.83%{transform:translate(10.9px,73.9px)} 96.67%{transform:translate(12.0px,80.9px)} 97.50%{transform:translate(12.8px,86.6px)} 98.33%{transform:translate(13.5px,90.9px)} 99.17%{transform:translate(14.3px,96.5px)}
    100.00%{transform:translate(15.3px,103.2px)}
  }
  .plan .track { animation: track 6s linear infinite; }

  /* the beam is released only where diastole meets end-expiration, twice
     in the loop, at 1.75-2.15 s and 4.75-5.15 s of the 6 s cycle */
  @keyframes beam-on {
    0%, 29.07%, 35.93%, 79.07%, 85.93%, 100% { opacity: 0; }
    29.17%, 35.83%, 79.17%, 85.83%           { opacity: 1; }
  }
  @keyframes beam-off {
    0%, 29.07%, 35.93%, 79.07%, 85.93%, 100% { opacity: 1; }
    29.17%, 35.83%, 79.17%, 85.83%           { opacity: 0; }
  }
  .chip.on   { animation: beam-on  6s steps(1, end) infinite; }
  .chip.off  { animation: beam-off 6s steps(1, end) infinite; }
  .plan .flood { animation: beam-on 6s steps(1, end) infinite; }

  /* scroll-linked progress bar */
  @keyframes grow { to { scale: 1 1; } }
  @supports (animation-timeline: scroll()) {
    .progress { animation: grow linear both; animation-timeline: scroll(root block); }
  }

  /* content reveals as it enters the viewport */
  @keyframes rise { from { opacity: 0; translate: 0 1.4rem; } to { opacity: 1; translate: 0 0; } }
  @supports (animation-timeline: view()) {
    .rise {
      animation: rise linear both;
      animation-timeline: view();
      animation-range: entry 8% cover 26%;
    }
  }

  /* .develop: the roster's own reveal, in place of the .rise fade. A portrait
     enters the viewport in the grey it is held at by default and gains its
     colour as it rises, so the reveal is made of the thing the photographs
     are actually about rather than of opacity.

     The animation sets filter, so it outranks the .who:hover colour swap for
     the whole run: once a face has scrolled in it is already in colour, and
     there is nothing left for the hover to reveal. */
  @keyframes develop {
    from { filter: grayscale(1) contrast(1.06); }
    to   { filter: grayscale(0) contrast(1); }
  }
  @supports (animation-timeline: view()) {
    .develop .who img {
      animation: develop linear both;
      animation-timeline: view();
      /* measured against cover, not entry: the first row is already on screen
         at scroll 0, and an entry-based range would have it land there in
         full colour, with nothing left to reveal. Starting at cover 20% holds
         it grey at the top of the page, full colour by the time it has risen
         to the middle. */
      animation-range: cover 20% cover 60%;
    }
  }

  /* prefers-reduced-motion, read narrowly and on purpose.

     The instrument loop (ultrasound, ECG, gate window) is not decoration. It is
     the product doing the one thing the page exists to explain, and it is no
     more motion than a video of the same thing would be. It keeps running, so
     the page still makes its point on a machine with animations switched off.

     What stops is the layer that moves because the visitor moved: scroll-driven
     reveals, the scroll progress bar, the blinking LIVE dot, hover displacement.
     That is the motion the setting is actually asking us to drop.

     These rules sit at the end of @layer motion, so they win on order alone and
     need no !important. */
  @media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    .rise { animation: none; opacity: 1; translate: none; }
    /* with the scroll animation off, the portraits fall back to the grey they
       are held at by default, and hover brings the colour back as it does
       everywhere else. Nothing is lost but the scroll-driven part. */
    .develop .who img { animation: none; }
    .progress { animation: none; scale: 1 1; }
    .signals .key.live::before { animation: none; opacity: 1; }
    .btn:hover, a.tile:hover .go::after { translate: none; }
    .who:hover img { scale: none; }
  }
}

/* ---------------------------------------------------------- */
@layer components {
  /* Skip link: the first tab stop on every page. Off-screen until focused,
     so it costs sighted visitors nothing and saves keyboard visitors the
     seven-item nav on each of seven pages. */
  .skip {
    position: fixed;
    top: 0.5rem;
    left: 0.5rem;
    z-index: 40;
    padding: 0.7rem 1.1rem;
    background: var(--paper);
    color: var(--ink);
    border: 1px solid var(--blue);
    border-radius: 2px;
    font-family: var(--mono);
    font-size: 0.68rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    text-decoration: none;
    translate: 0 -250%;
  }
  .skip:focus { translate: 0 0; }
}
