/* Hydroponics floating plate — page chrome and stage.
   Base tokens (--bg, --fg, --sans, --mono, --line, --dim, --faint, --r-lg,
   --ease) come from ../css/polish.css, which is shared by every instrument.
   Only the accent and the page's own layout live here. */
:root {
  /* The accent slot is named --amber across the instrument pages. Green is
     DiOPAL's and blue is the bioreactor's, so this one takes violet — the
     fourth hue that still clears AA on the page's near-black background. */
  --amber: #b18cff;
  --amber-deep: #5b31a8;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* `clip`, not `hidden` — a non-visible value on one axis forces the other to
     `auto`, which quietly makes <body> a scroll container and kills every
     window-level scroll listener on the page. */
  overflow-x: clip;
  font-feature-settings: "tnum" 1, "ss01" 1;
}

h1, h2 { font-weight: 600; letter-spacing: -0.035em; margin: 0; }

/* ---------- atmosphere ---------- */
.bg-glow, .grid-lines, .grain, .vignette { position: fixed; inset: 0; pointer-events: none; }
.bg-glow {
  z-index: 0;
  background:
    radial-gradient(52% 40% at 50% 38%, rgba(177, 140, 255, .07), transparent 72%),
    radial-gradient(90% 60% at 50% 110%, rgba(38, 62, 104, .18), transparent 70%);
}
.grid-lines {
  z-index: 0; opacity: .16;
  background-image:
    linear-gradient(var(--line) 1px, transparent 1px),
    linear-gradient(90deg, var(--line) 1px, transparent 1px);
  background-size: 80px 80px;
  mask-image: radial-gradient(66% 56% at 50% 42%, #000 30%, transparent 100%);
  -webkit-mask-image: radial-gradient(66% 56% at 50% 42%, #000 30%, transparent 100%);
}
.grain {
  z-index: 45; opacity: .30; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}
.vignette { z-index: 44; box-shadow: inset 0 0 min(16vw, 190px) rgba(0, 0, 0, .38); }

/* ---------- chrome ---------- */
.topbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 50;
  display: flex; justify-content: space-between; align-items: center;
  padding: 20px clamp(18px, 3.2vw, 34px);
  font-family: var(--mono); font-size: .62rem;
  letter-spacing: .22em; text-transform: uppercase;
}
.backbtn { color: var(--dim); text-decoration: none; }
.backbtn:hover { color: var(--fg); }
.backbtn i { font-style: normal; margin-right: .5em; }
.topbar-meta { color: var(--dim); }
.topbar-meta span { color: var(--faint); margin: 0 .35em; }
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 60;
  background: var(--fg); color: var(--bg); padding: .6rem 1rem;
}
.skip-link:focus { left: 8px; top: 8px; }

/* ---------- intro ---------- */
.intro {
  position: relative; z-index: 2;
  display: grid; place-content: center; text-align: center;
  padding: 7rem 2rem 3rem;
}
.eyebrow {
  font-family: var(--mono); font-size: .65rem;
  letter-spacing: .3em; text-transform: uppercase;
  color: var(--amber); margin-bottom: 1.6rem;
}
.intro h1 { font-size: clamp(2.2rem, 6vw, 4.2rem); line-height: 1.04; margin-bottom: 1.5rem; }
.intro h1 span { color: var(--amber); }
.intro > p.lede {
  max-width: 56ch; margin: 0 auto;
  color: var(--muted); font-size: 1rem; line-height: 1.7;
}
.working-name {
  display: inline-block; margin-top: 1.4rem;
  font-family: var(--mono); font-size: .58rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--faint);
  border: 1px dashed var(--line); border-radius: 999px; padding: .45rem .9rem;
}

/* ---------- the scroll story ----------
   A tall track with a sticky stage inside it. Scroll position through the
   track is the animation's only input, so the whole thing is deterministic:
   the same scroll offset always draws the same frame, forwards or backwards. */
.story { position: relative; height: 900vh; z-index: 2; }
.story-stage {
  position: sticky; top: 0; height: 100vh; width: 100%;
  overflow: hidden;
}
.story-stage #gl { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

.loader {
  position: absolute; inset: 0; display: grid; place-content: center;
  font-family: var(--mono); font-size: .6rem; letter-spacing: .24em;
  text-transform: uppercase; color: var(--dim);
  background: var(--bg); transition: opacity .6s var(--ease), color .16s linear; z-index: 6;
  /* "Loading " and the percentage are two grid items: in one row they read
     as one line. The trailing .24em of tracking is given back on the left,
     as on the title eyebrow, so the line sits on the true centre. */
  grid-auto-flow: column; gap: .6em; padding-left: .24em;
}
/* The words leave first. With only the veil fading, "Loading 100%" hung
   across the centre bores for ~300 ms while the plate came up behind it. */
.loader.hide { opacity: 0; color: transparent; pointer-events: none; }

/* ---- arrival ----
   As the veil lifts the title rises in, a line at a time, and the scroll cue
   comes last, instead of being uncovered already set. Only on the CHILDREN:
   story.js owns #title's and #cue's inline opacity and transform, and a CSS
   animation outranks inline style while it runs. `backwards` only, so nothing
   is held once it has played and the scroll fade is untouched from then on.
   Keyed on the loader's own .hide, so the error path (no .hide) keeps its
   static title under the error text. One-shot on load; no per-frame cost. */
@keyframes titleRise { from { opacity: 0; transform: translateY(14px); } }
@keyframes cueIn { from { opacity: 0; } }
.story-stage:has(.loader.hide) .story-title > * { animation: titleRise .95s var(--ease) backwards; }
/* `.story-h`, not `h1`: the headline became a <p> when the record took the
   page's one h1, and the stagger had silently stopped applying to it. */
.story-stage:has(.loader.hide) .story-title > .story-h { animation-delay: .07s; }
.story-stage:has(.loader.hide) .scroll-cue > span { animation: titleRise .8s var(--ease) .34s backwards; }
/* The cue's line keeps its pulse; the fade-in is listed after it so it wins
   while it runs, and its open end hands over to whatever the pulse is doing. */
.story-stage:has(.loader.hide) .scroll-cue > i {
  animation: cuedrop 2.4s var(--ease) infinite, cueIn .8s var(--ease) .42s backwards;
}
@media (prefers-reduced-motion: reduce) {
  /* the global rule shortens durations but keeps delays: without this the
     h1 and rule would blink in 70-160 ms after the (instant) veil */
  .story-stage:has(.loader.hide) .story-title > *,
  .story-stage:has(.loader.hide) .scroll-cue > span { animation: none; }
  .story-stage:has(.loader.hide) .scroll-cue > i { animation: cuedrop 2.4s var(--ease) infinite; }
}

/* ---- title card ----
   Set in the photometer's and LPA's type (their css/style.css, "title card"):
   a mono eyebrow in the accent, then the line in Oxanium at 600. Only the
   position is this page's own: centred over the plate, not a left column. The
   "Final build" rule that sat under the line is gone (owner, 7 Oct 2026). */
@font-face {
  font-family: "Oxanium Title";
  src: url("../../fonts/oxanium-600-latin.woff") format("woff");
  font-weight: 600; font-style: normal; font-display: block;
  unicode-range: U+0020-007E, U+00B7, U+2013-2014, U+2018-2019, U+201C-201D, U+2026;
}
.story-title {
  position: absolute; left: 50%; top: clamp(84px, 11%, 130px); transform: translateX(-50%);
  text-align: center; z-index: 4; pointer-events: none; width: min(90vw, 900px);
}
.story-title .eyebrow {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .42em;
  text-transform: uppercase; color: var(--amber); margin-bottom: calc(1.1rem * var(--ck, 1));
  /* tracking leaves .42em after the last letter; this re-centres the caps */
  padding-left: .42em;
}
/* The other two heroes' size, clamp(2.1rem, 6.2vw, 4.4rem), with its ends
   scaled by the stage height (--ck, written on #story by story.js; 1 without
   it, and 1 at every window 810 px tall or more, so 1440x900 and a 390x844
   phone get exactly their size). The plate scales with height, so a fixed-px
   card overlapped it by 18 px at 720 tall and 25 at 680. The top is clamped
   so the eyebrow stays 9 px clear of the 75 px topbar on short windows. */
.story-title :is(h1, .story-h) {
  margin: 0;
  font-family: "Oxanium Title", var(--sans);
  font-weight: 600; letter-spacing: -.02em; line-height: 1.02;
  font-size: clamp(calc(2.1rem * var(--ck, 1)), 6.2vw, calc(4.4rem * var(--ck, 1)));
  text-shadow: 0 0 64px rgba(0, 0, 0, .85);
}

/* ---- scroll cue ----
   The word in the other heroes' grey (.56rem, .38em, --faint); the pulse
   under it keeps the accent, as theirs does. */
.scroll-cue {
  position: absolute; left: 50%; bottom: 22px; transform: translateX(-50%);
  z-index: 4; pointer-events: none; text-align: center;
  font-family: var(--mono); font-size: .56rem; letter-spacing: .38em;
  text-transform: uppercase; color: var(--faint);
}
.scroll-cue i {
  display: block; width: 1px; height: 30px; margin: 9px auto 0;
  background: linear-gradient(var(--amber), transparent);
  animation: cuedrop 2.4s var(--ease) infinite;
}
@keyframes cuedrop {
  0%, 100% { opacity: .25; transform: scaleY(.4); transform-origin: top; }
  50%      { opacity: 1;   transform: scaleY(1);  transform-origin: top; }
}
/* A phone on its side (~390 px tall). --ck bottoms out at .78 and the headline
   runs on 6.4vw, so the title card was 99 px of a 390 px frame (47% with the
   topbar) and the hero band left the part 130 px wide, 15% of the frame. On
   short landscape windows the headline also stops at 8.8vh (its 1440x810
   proportion) and the cue's pulse line is shorter; every taller window is
   untouched (8.8vh is above the clamp's own max from 632 px tall up). */
@media (orientation: landscape) and (max-height: 540px) {
  .story-title :is(h1, .story-h) {
    font-size: min(clamp(calc(2.1rem * var(--ck, 1)), 6.2vw, calc(4.4rem * var(--ck, 1))), 8.8vh);
  }
  .scroll-cue i { height: 14px; margin-top: 6px; }
}

/* ---- captions ----
   The photometer's and LPA's caption type (story-core.js builds theirs as
   .cap-tag / .cap-name / .cap-rule / p): one mono eyebrow in the accent,
   "01 —— BORE PATTERN", the name in Oxanium at 600, a short hairline, then
   the copy in the neutral body grey. Only the seat is this page's own: a
   centred band at the top of the frame instead of their lower-left column.
   No boxes and no leader lines — the type sits on the frame. */
.hud { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
/* Centred at the TOP of the frame, with the model dropped out from under it.
   The side column cost a third of the frame width and forced the model
   sideways to dodge it; a top band costs a quarter of the height, which the
   camera can simply give back by lowering the horizon. */
.panel {
  /* `top` is written per act by story.js — this is only the fallback */
  /* No transition on `top`: the seat is rewritten only when the caption's
     content swaps, which happens while it is faded out, so an animated move
     buys nothing and can leave the type under the fixed topbar in transit. */
  position: absolute; top: 5%; left: 50%;
  width: min(62ch, 84vw);
  transform: translateX(-50%);
  text-align: center;
  opacity: 0;
  /* One eyebrow line (number, hairline, role), then the name, then the copy.
     The DOM order is tag, name, role, rule, body; flex order seats the role
     beside the number without touching the script. */
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  /* story.js writes opacity and transform on every frame; on its own layer
     neither repaints the type or its shadow */
  will-change: opacity, transform;
  /* a soft dark halo, not a box: it holds the copy where it crosses lit water
     or leaves without drawing an edge anyone could read as a label panel */
  text-shadow: 0 1px 2px rgba(3, 4, 8, .6), 0 0 18px rgba(3, 4, 8, .55);
}
.panel.right { text-align: center; }
.panel .tag {
  /* The number is the eyebrow's first word, in the accent like the rest of
     the line (theirs: .cap-tag). The record's bare .tag rule below would draw
     a full-width outlined bar around it; every property that rule sets is
     restated here, so it cannot leak back in. */
  order: 0; flex: none;
  display: inline-flex; align-items: center; gap: .7rem;
  font-family: var(--mono); font-size: .6rem; font-weight: 400;
  letter-spacing: .26em; line-height: normal; text-transform: uppercase;
  color: var(--amber); -webkit-text-stroke: 0;
  border: 0; border-radius: 0; padding: 0; max-width: none;
  margin: 0 .7rem .75rem 0;
}
/* the hairline between number and role (theirs: .cap-tag i) */
.panel .tag::after {
  content: ""; width: 1.9rem; height: 1px;
  background: var(--amber); opacity: .6;
}
.panel .pname {
  order: 2; flex: 0 0 100%;
  font-family: "Oxanium Title", var(--sans); font-weight: 600;
  font-size: clamp(1.35rem, 2.5vw, 2.05rem); letter-spacing: -.015em; line-height: 1.1;
  margin: 0; color: var(--fg);
}
.panel .prole {
  order: 1; flex: none;
  font-family: var(--mono); font-size: .6rem; line-height: normal;
  letter-spacing: .26em; text-transform: uppercase;
  /* the tracking trails the last letter; pulled back so the line centres */
  color: var(--amber); margin: 0 -.26em .75rem 0;
}
/* The short hairline under the name (theirs: .cap-rule). Drawn by the name
   itself, not by the empty .rule div: as a flex item that div shared a line
   with the copy (54ch wide, so both fit) and sat beside it, not under the name. */
.panel .rule { display: none; }
.panel .pname::after {
  content: ""; display: block;
  width: 3.4rem; height: 1px; background: var(--line);
  margin: .85rem auto .8rem;
}
.panel p {
  order: 4; flex: 0 0 100%;
  margin: 0 auto; font-size: clamp(.86rem, 1.05vw, .98rem); line-height: 1.62;
  max-width: 54ch; color: #c3cbd6;
  text-wrap: pretty; text-align: left;
}
.panel p b, .panel p strong { color: var(--fg); font-weight: 600; }
.panel p em { color: var(--amber); font-style: normal; }
.panel .nw { white-space: nowrap; }

/* the ring that marks a bore in act 2 */
.mark-ring {
  position: absolute; border: 1px solid var(--amber); border-radius: 50%;
  transform: translate(-50%, -50%); opacity: 0;
  box-shadow: 0 0 14px rgba(177, 140, 255, .35), inset 0 0 10px rgba(177, 140, 255, .18);
}

/* ---------- mobile ---------- */
@media (max-width: 900px) {
  .topbar { font-size: .55rem; padding: 14px 16px; }
  .topbar-meta { display: none; }
  .intro { padding: 5.5rem 1.2rem 2rem; }
  .stage { aspect-ratio: 4 / 3; margin-top: 2rem; }
  .legend-row { gap: 1rem; }
}

/* A 30vw column on a phone is four words wide. Below this the caption stops
   being a side panel and becomes a band across the foot of the frame; the
   script still sets left/right, so both are overridden here. */
@media (max-width: 720px) {
  .panel, .panel.right {
    width: calc(100% - 40px);
  }
  .panel p { max-width: none; }
  .story-title { top: 15%; }
}
/* ---- caption scale ----
   The model's size on screen is a fixed fraction of the stage HEIGHT (the fov
   is vertical); the caption type was fixed px, so a short window gave the same
   block a bigger bite of the frame. story.js writes --ck = clamp(h/810, .78, 1)
   on #story (measureCaps), and every caption size and gap is multiplied by it:
   the block keeps the proportion it has at 1440x810 and never grows past it.
   The width is in ch of the scaled font, so lines break in the same places.
   Floors keep it legible: body copy 12 px, the mono eyebrow 9 px. This block
   replaces the old max-height: 720px tightening, which it makes redundant. It
   comes after the mobile block on purpose and restates its sizes with --ck.
   At --ck 1 (any window 810 px tall, and every phone) every size here is
   exactly the photometer's and LPA's, including their 860 px phone step. */
.panel { font-size: calc(1rem * var(--ck, 1)); }
.panel .tag {
  font-size: max(9px, calc(.6rem * var(--ck, 1)));
  gap: calc(.7rem * var(--ck, 1));
  margin: 0 calc(.7rem * var(--ck, 1)) calc(.75rem * var(--ck, 1)) 0;
}
.panel .tag::after { width: calc(1.9rem * var(--ck, 1)); }
.panel .prole {
  font-size: max(9px, calc(.6rem * var(--ck, 1)));
  margin: 0 -.26em calc(.75rem * var(--ck, 1)) 0;
}
.panel .pname {
  font-size: clamp(calc(1.35rem * var(--ck, 1)), 2.5vw, calc(2.05rem * var(--ck, 1)));
}
.panel .pname::after {
  width: calc(3.4rem * var(--ck, 1));
  margin: calc(.85rem * var(--ck, 1)) auto calc(.8rem * var(--ck, 1));
}
.panel p { font-size: max(12px, clamp(calc(.86rem * var(--ck, 1)), 1.05vw, calc(.98rem * var(--ck, 1)))); }
@media (max-width: 860px) {
  .panel .pname { font-size: calc(1.3rem * var(--ck, 1)); }
  .panel p { font-size: max(12px, calc(.9rem * var(--ck, 1))); }
}
/* Phone floor. ../css/wiki-align.css lifts this eyebrow to 11 px with .16em
   tracking; the other heroes' eyebrow (.story .cap-tag) gets 11 px at .2em
   there, so this takes the same .2em and re-centres the role on it. */
@media (max-width: 700px) {
  body .story .panel .tag, body .story .panel .prole { font-size: 11px; letter-spacing: .2em; }
  body .story .panel .prole { margin-right: -.2em; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* The story canvas is transparent, so the page graticule reads THROUGH the
   render — most visibly as fixed horizontal rules lying across the reservoir.
   It is page furniture, not part of the shot. */
body.story-on .grid-lines { opacity: 0; }
.grid-lines { transition: opacity .45s ease; }

/* The grain is a full-viewport layer with mix-blend-mode: overlay sitting ON
   TOP of the canvas. A blend mode forces the compositor to read the backdrop
   back for that layer on every composited frame, which is a cost no JS-side
   frame timing can see — and over a full-screen WebGL canvas it is the most
   expensive thing on the page that is not the render itself. The scene has its
   own dust and its own film grain in the floor shader; this layer earns its
   keep over the written record below, not over the story. */
body.story-on .grain { opacity: 0; }
.grain { transition: opacity .45s ease; }

/* The record's light theme switches on while the bottom third of the stage is
   still on screen. The canvas is transparent, so that band became a grey slab
   of water with a curtain of roots drawn on white paper. The stage keeps its
   own night instead (the dark theme's --bg) and leaves the screen as a dark
   band above the white record. It turns on at once with the theme; on the way
   back it is held until the body's .55s fade to night has finished, so the
   band never shows the page mid-fade behind the water either. */
.story-stage { transition: background-color 0s linear .55s; }
body.doc-light .story-stage { background-color: #05060a; transition: none; }
/* polish.js drops the body fade under reduced motion, so there is nothing to wait out */
@media (prefers-reduced-motion: reduce) { .story-stage { transition: none; } }

/* Over the live canvas the topbar's backdrop blur is re-sampled on every frame
   the render changes, and over the night sky it has nothing to soften: the bar
   keeps its gradient scrim and gets the blur back on the record. The jump hint
   sat on the frame's corner; it fades out for the story and returns on the
   record, and the "/" key works throughout. */
body.story-on:not(.doc-light) .topbar { -webkit-backdrop-filter: none; backdrop-filter: none; }
body.story-on:not(.doc-light) .palette-hint {
  opacity: 0; visibility: hidden;
  transition: opacity .3s var(--ease-io), visibility 0s linear .3s;
}
/* On the white record the pill kept its night fill (rgba(10,13,18,.82)) at
   .55 opacity: a grey smudge whose own label measured 1.1:1. Over paper it
   takes the light surface the record's other controls use (.jump-btn). */
body.doc-light .palette-hint {
  background: #fff; color: var(--muted); opacity: 1;
  border-color: var(--line-2);
  box-shadow: 0 2px 14px rgba(15, 19, 25, .10);
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
body.doc-light .palette-hint:hover, body.doc-light .palette-hint:focus-visible { color: var(--fg); }

/* ---- section map ------------------------------------------------------ */
/* Built by js/toc.js from the document's own parts. Doubles as the table of
   contents below 1100px, where the sticky rail is hidden. */
.doc-map {
  display: grid; gap: .8rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  margin: 0 0 4.5rem;
}
.map-card {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas: "n t" ". s";
  column-gap: .75rem; row-gap: .5rem;
  padding: 1.05rem 1.1rem 1.15rem;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: rgba(255,255,255,.015);
  text-decoration: none; color: inherit;
  transition: border-color .3s var(--ease), background .3s var(--ease),
              transform .3s var(--ease);
}
.map-card:hover, .map-card:focus-visible {
  border-color: color-mix(in srgb, var(--amber) 34%, transparent);
  background: color-mix(in srgb, var(--amber) 4%, transparent);
  transform: translateY(-2px);
}
.map-n {
  grid-area: n;
  font-family: var(--mono); font-size: .6rem; letter-spacing: .14em;
  color: var(--amber); padding-top: .2em;
}
.map-t {
  grid-area: t;
  font-size: .92rem; font-weight: 600; line-height: 1.3;
  letter-spacing: -.012em;
}
.map-sub {
  grid-area: s;
  display: flex; flex-direction: column; gap: .28rem;
}
.map-sub > span {
  font-size: .72rem; line-height: 1.45; color: var(--dim);
  padding-left: .72rem; position: relative;
}
.map-sub > span::before {
  content: ""; position: absolute; left: 0; top: .62em;
  width: 3px; height: 1px; background: var(--faint);
}
.map-more { color: var(--faint) !important; font-family: var(--mono);
            font-size: .6rem !important; letter-spacing: .1em; }

@media (prefers-reduced-motion: reduce) {
  .map-card { transition: none; }
  .map-card:hover, .map-card:focus-visible { transform: none; }
}

/* ═══ technical record ═══════════════════════════════════════════════
   Ported verbatim from the DiOPAL stylesheet so this page's record matches
   the other instrument pages. The story above keeps its own rules: the only
   selector these share is a bare .tag, which .panel .tag outranks. ═══ */

/* --- record layout, header, sections and components --- */
.doc-wrap {
  position: relative; z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 760px) minmax(0, 1fr);
  padding: 0 clamp(18px, 4vw, 48px) 6rem;
  border-top: 1px solid var(--line);
}

/* sticky contents rail */
.doc-nav {
  grid-column: 1;
  position: sticky; top: 0; height: 100vh;
  display: flex; flex-direction: column; justify-content: center;
  padding-right: 2rem;
}
.doc-nav-label {
  font-family: var(--mono); font-size: .55rem;
  letter-spacing: .26em; text-transform: uppercase;
  color: var(--faint); margin-bottom: 1.1rem;
}
.doc-nav ol { list-style: none; margin: 0; padding: 0; }
.doc-nav li { margin-bottom: .7rem; }
.doc-nav a {
  display: flex; gap: .7rem; align-items: baseline;
  text-decoration: none; color: var(--dim);
  font-size: .8rem; line-height: 1.3;
  transition: color .25s var(--ease);
  border-left: 1px solid transparent; padding-left: .8rem;
  margin-left: -.8rem;
}
.doc-nav a b {
  font-family: var(--mono); font-size: .62rem; color: var(--faint);
  transition: color .25s var(--ease);
}
.doc-nav a:hover { color: var(--fg); }
.doc-nav a.on { color: var(--fg); border-left-color: var(--amber); }
.doc-nav a.on b { color: var(--amber); }

.doc { grid-column: 2; padding-top: 5rem; }

/* ---- document header ---- */
.doc-head { margin-bottom: 5rem; }
.doc-head h1 {
  font-size: clamp(2rem, 5vw, 3.4rem);
  line-height: 1.03; margin-bottom: 1.6rem;
}
.standfirst {
  font-size: 1.1rem; line-height: 1.62; color: var(--fg);
  margin: 0 0 1.4rem; font-weight: 350;
}
.doc-aim {
  color: var(--muted); line-height: 1.75; font-size: .95rem;
  margin: 0 0 2rem; padding-left: 1.1rem;
  border-left: 2px solid var(--line);
}
.status {
  display: flex; align-items: flex-start; gap: .7rem;
  font-size: .82rem; line-height: 1.6; color: var(--muted);
  background: color-mix(in srgb, var(--amber) 5%, transparent);
  border: 1px solid color-mix(in srgb, var(--amber) 16%, transparent);
  border-radius: var(--r-xs); padding: .85rem 1rem;
}
.status-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--amber); flex: none; margin-top: .5rem;
}

/* ---- part dividers ---- */
.part {
  margin: 7rem 0 3rem; padding-top: 2.2rem;
  border-top: 1px solid var(--line);
}
.part:first-of-type { margin-top: 0; }
.part-mark {
  display: flex; align-items: baseline; gap: .6rem; margin-bottom: .7rem;
}
.part-mark span {
  font-family: var(--mono); font-size: .58rem;
  letter-spacing: .26em; text-transform: uppercase; color: var(--faint);
}
.part-mark b {
  font-family: var(--mono); font-size: 1.1rem; color: var(--amber); font-weight: 600;
}
.part h2 { font-size: clamp(1.6rem, 3.4vw, 2.4rem); line-height: 1.08; }

/* ---- sections ---- */
.sec { margin-bottom: 3.4rem; }
.sec h3 {
  font-size: 1.42rem; font-weight: 600; letter-spacing: -.02em;
  margin: 0 0 1.1rem; line-height: 1.3;
  display: flex; gap: .75rem; align-items: baseline;
}
.sec h3 i, .sec h4 i {
  font-family: var(--mono); font-style: normal;
  font-size: .72em; color: var(--amber); font-weight: 500;
  flex: none;
}
.sec h3 em, .sec h4 em { font-style: normal; color: var(--dim); font-weight: 400; }
.sec h4 {
  font-size: 1.1rem; font-weight: 600; margin: 2.2rem 0 .8rem;
  display: flex; gap: .7rem; align-items: baseline;
}
.sec h5 { font-size: .87rem; font-weight: 600; margin: 0 0 .4rem; }
.sec p {
  /* Body was .95rem/68ch, which measures 15.2px at 70 characters — small for
     long-form and at the top of the readable range. 1.06rem is about 17px, and
     because the cap is in ch it has to come down with it or the measure just
     stays where it was: 64ch lands near the 66-character optimum. */
  color: var(--muted); line-height: 1.8; font-size: 1.05rem;
  margin: 0 0 1.25rem; max-width: 64ch;
}
.sec p b, .sec li b { color: var(--fg); font-weight: 600; }
.sec li { font-size: 1.05rem; line-height: 1.8; }
.sec a { color: var(--amber); text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--amber) 30%, transparent); }
.sec a:hover { border-bottom-color: var(--amber); }
.sec abbr { text-decoration: none; border-bottom: 1px dotted var(--dim); cursor: help; }
.lead-note {
  font-size: 1.02rem !important; color: var(--fg) !important;
  border-left: 2px solid var(--amber); padding-left: 1.1rem;
}
.fineprint { font-size: .85rem !important; color: var(--dim) !important; }

/* ---- pending value chip ---- */
.pending {
  display: inline-block; vertical-align: baseline;
  min-width: 4.4em; height: 1.05em;
  border: 1px dashed color-mix(in srgb, var(--amber) 42%, transparent);
  border-radius: var(--r-xs);
  background: repeating-linear-gradient(-45deg,
    color-mix(in srgb, var(--amber) 5%, transparent) 0 4px, transparent 4px 8px);
}
.pending.s { min-width: 2.4em; }
.pending.eq { min-width: 12em; height: 1.5em; }

/* ---- open item ---- */
.openitem {
  display: flex; gap: .9rem; align-items: flex-start;
  border: 1px solid rgba(255,255,255,.08);
  border-left: 2px solid var(--amber-deep);
  border-radius: var(--r-xs); padding: .9rem 1.1rem;
  margin: 1.4rem 0; background: rgba(255,255,255,.017);
}
.openitem.tight { margin: .9rem 0 0; padding: .7rem .9rem; }
.openitem-tag {
  font-family: var(--mono); font-size: .55rem;
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--amber); flex: none; margin-top: .28rem;
}
.openitem p, .openitem ul { margin: 0; font-size: .85rem !important; line-height: 1.65; }
.openitem ul { padding-left: 1.1rem; color: var(--muted); }
.openitem li { margin-bottom: .3rem; font-size: .85rem; line-height: 1.6; }

/* ---- blank slot ---- */
.blank-slot {
  border: 1px dashed rgba(255,255,255,.11);
  border-radius: var(--r-sm); min-height: 130px;
  display: grid; place-content: center;
  margin: 1.2rem 0;
  background: repeating-linear-gradient(-45deg,
    rgba(255,255,255,.012) 0 8px, transparent 8px 16px);
}
.blank-slot::after {
  content: attr(data-label) " — to be written";
  font-family: var(--mono); font-size: .6rem;
  letter-spacing: .2em; text-transform: uppercase; color: var(--faint);
}

/* ---- keynote ---- */
.keynote {
  display: flex; gap: 1.6rem; align-items: center;
  margin: 2rem 0; padding: 1.4rem 1.6rem;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: linear-gradient(100deg, color-mix(in srgb, var(--amber) 6%, transparent), transparent 60%);
}
.keynote-fig {
  font-size: 2.6rem; font-weight: 650; letter-spacing: -.04em;
  color: var(--amber); line-height: 1; flex: none;
}
/* The unit suffix rides on the keynote's dark panel, so it cannot use the
   deep variant — that is tuned for white and lands near 2.9:1 here. Use
   --amber: the light theme already promotes it to --amber-deep. */
.keynote-fig em { font-style: normal; font-size: .38em; margin-left: .12em; color: var(--amber); }
.keynote p { margin: 0 !important; font-size: .9rem !important; }

/* ---- tables ---- */
.table-scroll { overflow-x: auto; margin: 1.6rem 0; }
table { border-collapse: collapse; width: 100%; font-size: .84rem; min-width: 560px; }
table caption {
  text-align: left; font-family: var(--mono); font-size: .55rem;
  letter-spacing: .22em; text-transform: uppercase; color: var(--faint);
  padding-bottom: .7rem;
}
table th {
  text-align: left; font-family: var(--mono); font-size: .56rem;
  letter-spacing: .16em; text-transform: uppercase; color: var(--dim);
  font-weight: 500; padding: .6rem .8rem; border-bottom: 1px solid var(--line);
}
table td {
  padding: .72rem .8rem; border-bottom: 1px solid rgba(255,255,255,.05);
  color: var(--muted); line-height: 1.5; vertical-align: top;
}
table tbody tr:hover td { background: rgba(255,255,255,.017); }
table td:first-child { color: var(--fg); font-weight: 500; }
table .num { text-align: right; white-space: nowrap; }
table tfoot td {
  border-bottom: none; border-top: 1px solid var(--line);
  color: var(--fg); font-weight: 600; padding-top: .9rem;
}
.compare tr.ours td { background: color-mix(in srgb, var(--amber) 5%, transparent); }
.compare tr.ours td:first-child { color: var(--amber); }

/* ---- spec card ---- */
.spec-card {
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 1.3rem 1.5rem; margin: 1.8rem 0;
}
.spec-card-title {
  font-family: var(--mono); font-size: .56rem;
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--amber); margin-bottom: 1rem;
}
.spec-card dl {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: .9rem 1.6rem; margin: 0 0 1rem;
}
.spec-card dt {
  font-family: var(--mono); font-size: .55rem;
  letter-spacing: .16em; text-transform: uppercase; color: var(--dim);
  margin-bottom: .25rem;
}
.spec-card dd { margin: 0; font-size: .87rem; font-weight: 500; }
.spec-note { font-size: .84rem !important; color: var(--dim) !important; margin: 0 !important; }

/* ---- iterations ---- */
.iters { list-style: none; margin: 2rem 0; padding: 0; }
.iters li { display: flex; gap: 1.4rem; padding-bottom: 2.2rem; position: relative; }
.iters li::before {
  content: ""; position: absolute; left: 17px; top: 40px; bottom: 0;
  width: 1px; background: var(--line);
}
.iters li:last-child::before { display: none; }
.iter-n {
  font-family: var(--mono); font-size: .68rem; font-weight: 600;
  color: var(--amber); flex: none;
  width: 35px; height: 35px; border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--amber) 30%, transparent);
  display: grid; place-content: center;
  background: var(--bg); position: relative; z-index: 1;
}
.iter-body { flex: 1; min-width: 0; }
.iter-body h4 { margin: .35rem 0 .6rem; font-size: 1rem; }
.iter-cause { font-size: .87rem !important; color: var(--dim) !important; }

/* delta */
.delta {
  display: flex; align-items: center; gap: 1.1rem; flex-wrap: wrap;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 1rem 1.2rem; margin: 1.1rem 0;
}
.delta > div { text-align: center; }
.delta span {
  display: block; font-family: var(--mono); font-size: .52rem;
  letter-spacing: .18em; text-transform: uppercase; color: var(--faint);
  margin-bottom: .25rem;
}
.delta b { font-size: 1.5rem; font-weight: 650; letter-spacing: -.03em; color: var(--muted); }
.delta b.good { color: var(--amber); }
.delta-arrow { color: var(--faint); font-size: 1.1rem; }
.delta p {
  flex: 1; min-width: 190px; margin: 0 !important;
  font-size: .78rem !important; color: var(--dim) !important; line-height: 1.55;
}

/* ---- callout ---- */
.callout {
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 1.2rem 1.4rem; margin: 2rem 0;
  background: rgba(255,255,255,.017);
}
.callout h5 { color: var(--fg); font-size: .92rem; margin-bottom: .5rem; }
.callout h5 em { font-style: italic; color: var(--amber); }
.callout p { margin: 0 !important; font-size: .89rem !important; }
.callout.warn { border-color: color-mix(in srgb, var(--amber) 22%, transparent); background: color-mix(in srgb, var(--amber) 4.5%, transparent); }

/* ---- filter steps ---- */
.filter-steps {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 1px; background: var(--line);
  border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden;
  margin: 1.6rem 0;
}
.filter-steps > div { background: var(--bg); padding: 1rem; }
.filter-steps b {
  display: block; font-family: var(--mono); font-size: .62rem;
  color: var(--amber); margin-bottom: .5rem;
}
.filter-steps p { margin: 0 !important; font-size: .82rem !important; line-height: 1.55; }

/* ---- protocol ---- */
.protocol { counter-reset: p; list-style: none; margin: 1.4rem 0; padding: 0; }
.protocol li {
  counter-increment: p; position: relative;
  padding-left: 2.4rem; margin-bottom: 1.5rem;
}
.protocol li::before {
  content: counter(p); position: absolute; left: 0; top: 0;
  font-family: var(--mono); font-size: .62rem; color: var(--amber);
  width: 1.5rem; height: 1.5rem; border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--amber) 28%, transparent);
  display: grid; place-content: center;
}
.protocol p { margin: 0 !important; font-size: .9rem !important; }

/* ---- equation ---- */
.equation {
  display: flex; align-items: center; gap: .9rem; flex-wrap: wrap;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 1.3rem 1.5rem; margin: 1.4rem 0;
  background: rgba(255,255,255,.017);
  font-family: var(--mono);
}
.eq-lhs { font-size: 1.05rem; color: var(--fg); }
.eq-eq { color: var(--dim); }
.eq-range { font-size: .85rem !important; color: var(--dim) !important; }

/* ---- metrics ---- */
.metrics { display: flex; gap: 2.4rem; flex-wrap: wrap; margin: 1.2rem 0 1.4rem; }
.metrics > div { display: flex; align-items: baseline; gap: .55rem; }
.metrics span {
  font-family: var(--mono); font-size: .58rem;
  letter-spacing: .16em; text-transform: uppercase; color: var(--dim);
}
.metrics b { font-size: 1.15rem; font-weight: 600; }
.metrics em { font-style: normal; font-size: .72rem; color: var(--faint); }

/* ---- figures ---- */
.fig { margin: 2rem 0; }
.fig-slot {
  border: 1px dashed rgba(255,255,255,.11); border-radius: var(--r-sm);
  min-height: 230px; display: grid; place-content: center;
  background: repeating-linear-gradient(-45deg,
    rgba(255,255,255,.012) 0 8px, transparent 8px 16px);
}
.fig-slot span {
  font-family: var(--mono); font-size: .6rem;
  letter-spacing: .2em; text-transform: uppercase; color: var(--faint);
}
.fig figcaption {
  font-size: .82rem; color: var(--dim); line-height: 1.6;
  padding-top: .8rem; max-width: 62ch;
}

/* ---- flow path ---- */
.flow { color: var(--fg); font-weight: 500; white-space: nowrap; }
.flow i { color: var(--amber); font-style: normal; margin: 0 .18em; }

/* ---- parameters ---- */
.params { display: grid; gap: 1rem; margin: 1.6rem 0; }
.param {
  border: 1px solid var(--line); border-radius: var(--r-sm); padding: 1.2rem 1.4rem;
}
.param.prio { border-color: color-mix(in srgb, var(--amber) 24%, transparent); background: color-mix(in srgb, var(--amber) 3.5%, transparent); }
.param-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; margin-bottom: .7rem; flex-wrap: wrap;
}
.param-head h4 { margin: 0; font-size: 1rem; }
/* scoped to the record: bare, it also matched the story's #panel .tag */
.doc .tag {
  font-family: var(--mono); font-size: .52rem;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--dim); border: 1px solid var(--line);
  border-radius: var(--r-xs); padding: .22rem .5rem;
  /* `flex: none` here used to let a long tag ("Highest priority", widened by the
     .18em tracking) push past the page edge on a phone. Shrinkable, and allowed
     to wrap, so it can never be the widest thing on the page. */
  flex: 0 1 auto; max-width: 100%;
}
.tag.hot { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 32%, transparent); }
.param p { margin: 0 0 .7rem !important; font-size: .89rem !important; }
.param-note { color: var(--dim) !important; font-size: .84rem !important; }

/* ---- headline stats ---- */
.headline-stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 1px; background: var(--line);
  border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden;
  margin: 1.8rem 0;
}
.headline-stats > div { background: var(--bg); padding: 1.2rem 1rem; text-align: center; }
.headline-stats b {
  display: block; font-size: 1.7rem; font-weight: 650;
  letter-spacing: -.04em; color: var(--amber); line-height: 1;
}
.headline-stats b em { font-style: normal; font-size: .45em; margin-left: .06em; }
.headline-stats span {
  display: block; margin-top: .5rem;
  font-family: var(--mono); font-size: .52rem;
  letter-spacing: .16em; text-transform: uppercase; color: var(--dim);
}

/* ---- phases ---- */
.phases {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1px; background: var(--line);
  border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden;
  margin: 1.6rem 0;
}
.phases > div { background: var(--bg); padding: .9rem 1rem; }
.phases span {
  display: block; font-family: var(--mono); font-size: .55rem;
  letter-spacing: .14em; color: var(--amber); margin-bottom: .35rem;
}
.phases b { font-size: .85rem; font-weight: 500; color: var(--muted); }

/* ---- limits ---- */
.limits { display: grid; gap: 1.4rem; margin: 1.4rem 0 1.6rem; }
.limits > div { padding-left: 1.1rem; border-left: 2px solid var(--line); }
.limits h4 { margin: 0 0 .45rem; font-size: .95rem; }
.limits p { margin: 0 !important; font-size: .89rem !important; }

/* ---- pull quote ---- */
.pull {
  margin: 2rem 0; padding: 1.6rem 0;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  font-size: clamp(1.05rem, 2vw, 1.35rem); line-height: 1.5;
  font-weight: 350; color: var(--fg); letter-spacing: -.015em;
}
.pull b { color: var(--amber); font-weight: 650; }

.doc-foot { margin-top: 5rem; padding-top: 2rem; border-top: 1px solid var(--line); }

/* ---- condition matrix ---- */
.matrix {
  display: grid;
  grid-template-columns: auto repeat(3, 1fr);
  gap: 1px; background: var(--line);
  border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden;
  margin: 1.8rem 0;
}
.matrix > div { background: var(--bg); padding: .75rem .9rem; }
.mx-corner { background: var(--bg); }
.mx-h {
  font-family: var(--mono); font-size: .55rem;
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--dim); text-align: center;
}
.mx-w {
  display: flex; align-items: center; gap: .5rem;
  font-family: var(--mono); font-size: .58rem;
  letter-spacing: .12em; color: var(--muted); white-space: nowrap;
}
.mx-w span { width: 7px; height: 7px; border-radius: 50%; box-shadow: 0 0 8px currentColor; }
.mx-w.green span { background: #4dff7a; color: #4dff7a; }
.mx-w.red span { background: #ff5f4a; color: #ff5f4a; }
.mx-c { text-align: center; }
.mx-dots { display: flex; gap: 4px; justify-content: center; margin-bottom: .4rem; }
.mx-dots i { width: 7px; height: 7px; border-radius: 50%; display: block; }
.mx-c.green .mx-dots i { background: #4dff7a; }
.mx-c.red .mx-dots i { background: #ff5f4a; }
.mx-c.low .mx-dots i { opacity: .28 }
.mx-c.mid .mx-dots i { opacity: .60 }
.mx-c.high .mx-dots i { opacity: 1; box-shadow: 0 0 8px currentColor; }
.mx-c.green.high .mx-dots i { color: #4dff7a }
.mx-c.red.high .mx-dots i { color: #ff5f4a }
.mx-v { font-family: var(--mono); font-size: .68rem; color: var(--fg); }
.mx-v em { font-style: normal; color: var(--dim); font-size: .8em; margin-left: .2em; }
.mx-note {
  grid-column: 1 / -1; text-align: center;
  font-size: .68rem; color: var(--dim);
}

/* ---- I² chart ---- */
#i2-chart { display: grid; gap: 1.4rem; }
.i2-panel {
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 1rem 1.1rem .4rem;
}
.i2-h {
  display: flex; align-items: center; gap: .55rem;
  font-family: var(--mono); font-size: .6rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
  margin-bottom: .5rem;
}
.i2-sw { width: 8px; height: 8px; border-radius: 50%; box-shadow: 0 0 8px currentColor; }
.i2-panel.green .i2-sw { background: #4dff7a; color: #4dff7a; }
.i2-panel.red .i2-sw { background: #ff5f4a; color: #ff5f4a; }
.i2-h b { margin-left: auto; color: var(--fg); font-weight: 600; }
.i2-h i { font-style: normal; color: var(--faint); }
svg.i2 { width: 100%; height: auto; display: block; overflow: visible; }
.i2-ax { stroke: rgba(255, 255, 255, .12); stroke-width: 1; }
.i2-tick { fill: var(--dim); font-family: var(--mono); font-size: 9px; text-anchor: middle; }
.i2-unit { fill: var(--faint); font-family: var(--mono); font-size: 8px; text-anchor: end; }
.i2-band { fill: rgba(255, 255, 255, .035); stroke: rgba(255, 255, 255, .07); }
.i2-band-l {
  font-family: var(--mono); font-size: 8.5px; text-anchor: middle;
  letter-spacing: .12em; text-transform: uppercase; fill: var(--dim);
}
.i2-dot.rej { fill: rgba(255, 255, 255, .14); }
.i2-panel.green .i2-dot.sel { fill: #4dff7a; }
.i2-panel.red .i2-dot.sel { fill: #ff5f4a; }
.i2-dot.sel.t0 { opacity: .42 }
.i2-dot.sel.t1 { opacity: .72 }
.i2-dot.sel.t2 { opacity: 1 }

/* misc */
.swatch {
  display: inline-block; width: 9px; height: 9px; border-radius: var(--r-xs);
  margin-right: .5rem; vertical-align: -1px;
  border: 1px solid rgba(255, 255, 255, .16);
}
.swatch.black { background: #16181b }
.swatch.grey { background: #6a6f77 }
.swatch.white { background: #d9dbdd }
ul.tight { margin: .4rem 0 .9rem; padding-left: 1.1rem; color: var(--muted); }
ul.tight li { font-size: .9rem; line-height: 1.7; margin-bottom: .2rem; }

/* --- photo frames --- */
/* ---- photo frames ---- */
.frames {
  display: grid; gap: 1rem;
  /* capped, not 1fr — a lone portrait frame stretched to the full column runs
     most of a screen tall */
  grid-template-columns: repeat(auto-fit, minmax(220px, 330px));
  justify-content: start;
  margin: 1.6rem 0;
}
.frame { margin: 1.6rem 0; }
.frames .frame { margin: 0; }
.frame-img {
  position: relative; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: #0b0d10;
  /* the real photos are portrait phone shots, so portrait is the default —
     cropping them into a landscape box throws most of the device away */
  aspect-ratio: 3 / 4;
}
.frame-img img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  filter: saturate(.92) contrast(1.04);
  object-position: center 45%;
}
.frame.land .frame-img { aspect-ratio: 4 / 3; }
.frame.wide .frame-img { aspect-ratio: 16 / 9; }
.frame.square .frame-img { aspect-ratio: 1 / 1; }
/* a single feature photo should not run the full column height */
.frame.hero { max-width: 430px; margin-left: auto; margin-right: auto; }

/* empty state — a labelled slot, so a gap reads as deliberate */
.frame.empty .frame-img {
  border-style: dashed; border-color: rgba(255,255,255,.11);
  background: repeating-linear-gradient(-45deg,
    rgba(255,255,255,.012) 0 8px, transparent 8px 16px);
  display: grid; place-content: center; text-align: center; padding: 1rem;
}
.frame.empty .frame-img::after {
  content: attr(data-label);
  font-family: var(--mono); font-size: .58rem;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--faint); line-height: 1.9; max-width: 26ch;
}
.frame figcaption {
  padding-top: .65rem; font-size: .78rem; line-height: 1.6; color: var(--dim);
}
.frame figcaption b {
  color: var(--muted); font-weight: 600; font-family: var(--mono);
  font-size: .62rem; letter-spacing: .16em; text-transform: uppercase;
  margin-right: .5rem;
}
/* a frame sitting inside an iteration entry */
.iter-body .frames { margin: 1.1rem 0 .2rem; }
.iter-body .frame figcaption { font-size: .72rem; }

/* upload reminder for iGEM */
.frame-note {
  font-family: var(--mono); font-size: .55rem;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--faint); margin-top: .3rem;
}

/* --- figure and table numbering --- */
/* ---- figure & table numbering ---------------------------------------- */
/* Numbers come from js/figures.js, in document order. The rule above the
   caption is what makes a figure read as a figure rather than a stray image. */
.frame figcaption {
  border-top: 1px solid var(--line);
  margin-top: .55rem;
}
.fignum {
  font-family: var(--mono); font-size: .58rem;
  letter-spacing: .17em; text-transform: uppercase;
  color: var(--amber); font-weight: 600;
  margin-right: .1rem;
}
.fignum::after { content: "."; }
table caption .fignum { color: var(--amber); }

/* a cross-reference in the prose: "as ⟨Figure 4⟩ shows" */
a.xref {
  color: var(--amber); text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--amber) 34%, transparent);
}
a.xref:hover { border-bottom-color: var(--amber); }
/* a reference whose target vanished should be loud in review, not silent */
a.xref-broken { color: #ff5f56; }
a.xref-broken::after { content: "[missing figure]"; }

/* ---- footer ------------------------------------------------------------ */
/* The licence notice and repository link are required on every page for judging,
   and this page was the one left without a rule for them: they rendered at the
   browser default beside three records that set them in the page's own type. */
.outro-meta {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .2em;
  text-transform: uppercase; color: var(--faint);
}
.legal {
  margin-top: 1.4rem; padding-top: 1.2rem;
  border-top: 1px solid var(--line);
  font-size: .72rem; line-height: 1.7; color: var(--faint);
  max-width: 70ch;
}
.legal p { margin: 0 0 .3rem; }
.legal a { color: var(--dim); }
.legal a:hover { color: var(--amber); }

/* The opening line is display type, not the document's heading: the instrument's name in the
   record below is the page's one h1. Its type is set with the title card above (Oxanium, as on
   the photometer and LPA); this rule used to pin the old Inter tracking (-.045em) over it. */

/* ═══ record, rebuilt 2 Oct 2026 (night) ══════════════════════════════════
   The document half now follows the team's writing doc (sections 1-10) and is
   illustrated from the CAD. Everything below is this page's own: the drawings
   (.dw), the moving fixed-versus-floating figure (.hp-level), the requirement
   list, the numbered feature list keyed to the cut-open render, and the
   design-evolution strip. Shared styles (polish.css, wiki-align.css) are not
   touched; the selectors here carry .doc where they must beat .sec p / .sec li. */

/* Ten parts. At the default 220px card they fall 3-3-3-1 and the sub-headings make
   every card in a row as tall as the longest, which put 600px of grey boxes between
   the header and the first section. Here the map is a contents strip: two even rows
   of five numbered tiles (two columns on a phone), titles only. The sub-headings are
   one scroll away and the rail lists the parts beside the text. */
.doc-map { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: .6rem; margin-bottom: 4rem; }
.doc-map .map-card { grid-template-columns: minmax(0, 1fr); grid-template-areas: "n" "t"; row-gap: .4rem; padding: .85rem .9rem 1rem; }
.doc-map .map-sub { display: none; }
.doc-map .map-t { font-size: .875rem; }
@media (max-width: 760px) { .doc-map { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* long amber notes flow with the sentence instead of breaking into a box */
.doc .todo.long { display: inline; -webkit-box-decoration-break: clone; box-decoration-break: clone; line-height: 1.9; }

.doc .hp-up { margin-top: -.2rem; }
.doc .hp-up a { font-family: var(--mono); font-size: .75rem; letter-spacing: .08em; text-transform: uppercase; }

/* a render in a 16:10 well: the image carries its own studio sweep */
.frame.r16x10 .frame-img { aspect-ratio: 16 / 10; }
.frame.r16x10 .frame-img img { object-position: center; }

/* numbered pins on the cut-open render, keyed to the list under it */
.hp-pin {
  position: absolute; z-index: 3; transform: translate(-50%, -50%);
  width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center;
  font: 700 12px/1 var(--mono); color: var(--amber);
  background: #fff; border: 2px solid var(--amber);
  box-shadow: 0 2px 8px rgba(15, 19, 25, .28);
  pointer-events: none;
}
@media (max-width: 700px) { .hp-pin { width: 22px; height: 22px; font-size: 11px; } }
.doc .hp-feats { list-style: none; margin: 1.6rem 0 0; padding: 0; display: grid; gap: .8rem; max-width: var(--w-measure, 68ch); }
.doc .hp-feats li { display: grid; grid-template-columns: 1.6rem 1fr; gap: .8rem; align-items: start; margin: 0;
                    font-size: 1rem; line-height: 1.7; color: var(--muted); }
.doc .hp-feats li b { color: var(--fg); }
.hp-fn { width: 1.5rem; height: 1.5rem; margin-top: .14rem; border-radius: 50%; display: grid; place-items: center;
         font: 700 .72rem/1 var(--mono); color: var(--dim); border: 1.5px dashed rgba(15, 19, 25, .28); }
.hp-feats li.has-pin .hp-fn { color: var(--amber); border: 2px solid var(--amber); background: #fff; }
.hp-feats li.is-gone > div { opacity: .86; }

/* requirements, each with the place it is answered */
.doc .hp-reqs { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; border-top: 1px solid var(--hair, rgba(15, 19, 25, .17)); }
.doc .hp-reqs li { display: grid; grid-template-columns: 2.6rem minmax(0, 1.05fr) minmax(0, 1fr); gap: .3rem 1.4rem; align-items: baseline;
                   margin: 0; padding: .95rem 0; border-bottom: 1px solid var(--hair, rgba(15, 19, 25, .17)); font-size: 1rem; line-height: 1.6; }
.hp-req-n { font: 700 .72rem/1 var(--mono); letter-spacing: .1em; color: var(--amber); }
.doc .hp-req-t { color: var(--fg); font-weight: 650; }
.hp-req-a { color: var(--muted); font-size: .94rem; }
.doc .hp-req-a a { font-family: var(--mono); font-size: .78rem; margin-left: .35rem; white-space: nowrap; }
@media (max-width: 700px) {
  .doc .hp-reqs li { grid-template-columns: 2.2rem minmax(0, 1fr); }
  .hp-req-a { grid-column: 2; }
}

/* print settings: the card's pairs sit in a grid */
.hp-print dl > div { min-width: 0; }

/* tables on this page are two to four columns; they fit a phone without scrolling */
.doc table.hp-geom, .doc table.hp-parts { min-width: 0; }
.doc table.hp-geom td:first-child { width: 28%; }
.doc table.hp-geom td { font-size: .9rem; }
.doc table.hp-parts td { font-size: .9rem; }
@media (max-width: 700px) {
  .doc table.hp-geom td:first-child { width: 34%; }
  .doc table.hp-geom td, .doc table.hp-parts td { padding-left: .5rem; padding-right: .5rem; }
}
/* ---- drawings: every outline is a slice of the STLs ------------------------- */
.doc-wrap { --hp-air: #9a7bff; --hp-air-tint: rgba(154, 123, 255, .16); --hp-pla: #3c434d;
          --hp-hold: #8d5f3b; --hp-handle: #a3aab3; --hp-water: #e1eef8; --hp-water-ink: #2d6c9c;
          --hp-leaf: #4f9c6f; --hp-leaf-ink: #23684a; --hp-root: #9b7a55; --hp-weight: #9a3d22; }
.hp-fig svg { display: block; width: 100%; height: auto; overflow: visible; }
.hp-sheet { display: grid; gap: 1.2rem 1.2rem; grid-template-columns: minmax(0, 46fr) minmax(0, 27fr); align-items: center; }
.hp-sheet > .dw-plan { grid-column: 1; grid-row: 1; }
.hp-sheet > .dw-rail { grid-column: 2; grid-row: 1; max-width: 312px; justify-self: center; }
.hp-sheet > .dw-sec  { grid-column: 1 / -1; grid-row: 2; max-width: 520px; justify-self: center; }
@media (max-width: 700px) {
  .hp-sheet { grid-template-columns: minmax(0, 1fr); }
  .hp-sheet > .dw-plan, .hp-sheet > .dw-rail, .hp-sheet > .dw-sec { grid-column: 1; grid-row: auto; }
  .hp-sheet > .dw-rail { max-width: 312px; }
}
.hp-legend { list-style: none; margin: 1rem 0 .2rem; padding: .7rem 0 0; display: flex; flex-wrap: wrap; gap: .45rem 1.3rem;
             border-top: 1px dashed rgba(15, 19, 25, .14); font-size: .8125rem; line-height: 1.4; color: var(--muted); }
.doc .hp-legend li { display: flex; align-items: center; gap: .5rem; margin: 0; font-size: inherit; line-height: inherit; }
.hp-legend .sw { flex: none; width: 22px; height: 12px; border-radius: 2px; }
.hp-legend .sw-air { background: var(--hp-air-tint); border: 1px dashed var(--hp-air); }
.hp-legend .sw-airsec { background: var(--hp-air); }
.hp-legend .sw-cut { background: var(--hp-pla); }
.hp-legend .sw-hidden { border: 0; border-top: 1px dashed var(--muted); height: 0; border-radius: 0; }
.hp-legend .sw-handle { background: #dfe3e8; border: 1px solid var(--fg); }
.hp-legend .sw-hold { background: var(--hp-hold); }
.dw .dw-slab { fill: #f3f5f8; stroke: var(--fg); stroke-width: 1.15; stroke-linejoin: round; }
.dw .dw-bore { fill: #fff; stroke: var(--fg); stroke-width: 1; }
.dw .dw-hidden { fill: none; stroke: var(--muted); stroke-width: .85; stroke-dasharray: 4 3; }
.dw .dw-airplan { fill: var(--hp-air-tint); stroke: var(--hp-air); stroke-width: 1; stroke-dasharray: 5 3; }
.dw .dw-handle { fill: #dfe3e8; stroke: var(--fg); stroke-width: .9; }
.dw .dw-handle2 { fill: var(--hp-handle); stroke: var(--fg); stroke-width: .9; }
.dw .dw-axis { stroke: var(--dim); stroke-width: .7; stroke-dasharray: 12 3 2 3; opacity: .7; }
.dw .dw-secline { stroke: var(--amber); stroke-width: 1.6; }
.dw .dw-secah { fill: var(--amber); }
.dw .dw-secl { font-family: var(--mono); font-size: 15px; font-weight: 700; fill: var(--amber); }
.dw .dw-cut { fill: var(--hp-pla); }
.dw .dw-air { fill: var(--hp-air); }
.dw .dw-holdcut, .dw .dw-hold { fill: var(--hp-hold); }
.dw .dw-handlecut { fill: var(--hp-handle); }
.dw .dw-break { fill: none; stroke: var(--muted); stroke-width: .9; }
.dw .dw-dim { stroke: var(--muted); stroke-width: .8; }
.dw .dw-ext { stroke: var(--muted); stroke-width: .6; opacity: .8; }
.dw .dw-ah { fill: var(--muted); }
.dw .dw-onair { stroke: var(--fg); } .dw .dw-onair-ah { fill: var(--fg); }
.dw .dw-lead { fill: none; stroke: var(--muted); stroke-width: .8; }
.dw .dw-dot { fill: var(--fg); }
.dw text { font-family: var(--mono); fill: var(--fg); }
.dw .dw-dt { font-size: 13.5px; fill: var(--muted); letter-spacing: .02em; }
.dw .dw-dt-onair { fill: var(--fg); font-weight: 700; }
.dw .dw-l { font-size: 13px; letter-spacing: .08em; text-transform: uppercase; font-weight: 600; }
.dw .dw-s { font-size: 13px; fill: var(--dim); }
.dw .dw-title { font-size: 13px; letter-spacing: .14em; text-transform: uppercase; fill: var(--amber); font-weight: 700; }
@media (max-width: 700px) {
  /* the plan and the section shrink to ~0.7 of their drawn size on a phone; their type grows to hold 11 px */
  .dw-plan .dw-dt, .dw-sec .dw-dt { font-size: 18px; }
  .dw-sec .dw-s { font-size: 17.5px; }
  .dw-sec .dw-title { font-size: 17px; }
  .dw-plan .dw-secl { font-size: 19px; }
}
/* ---- section 4: the plate afloat ---------------------------------------- */
.hp-buoy-wrap { max-width: 560px; margin: 0 auto; }
.dw .hp-water { fill: var(--hp-water); }
.dw .hp-wline { stroke: var(--hp-water-ink); stroke-width: 1.6; }
.dw .hp-hatchline { stroke: var(--hp-water-ink); stroke-width: 1.4; }
.dw .hp-displaced { fill: url(#hp-hatch); opacity: .7; }
.dw .hp-root { fill: none; stroke: var(--hp-root); stroke-width: 1.5; stroke-linecap: round; }
.dw .hp-root-l { stroke-width: 1.05; }
.dw .hp-stem { stroke: var(--hp-leaf-ink); stroke-width: 1.6; }
.dw .hp-leaf { fill: var(--hp-leaf); stroke: var(--hp-leaf-ink); stroke-width: 1; }
.dw .hp-force { stroke-width: 3; stroke-linecap: round; }
.dw .hp-weight { stroke: var(--hp-weight); } .dw .hp-ah-weight { fill: var(--hp-weight); }
.dw .hp-buoy { stroke: var(--hp-water-ink); } .dw .hp-ah-buoy { fill: var(--hp-water-ink); }
.dw .hp-fl { font-size: 14px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; fill: var(--fg); }
.dw .hp-fl-weight { fill: var(--hp-weight); }
.dw .hp-fl-buoy, .dw .hp-fl-water { fill: var(--hp-water-ink); }
.dw .hp-fl-leaf { fill: var(--hp-leaf-ink); }
.dw .hp-fl-root { fill: #7a5c3a; }
@media (max-width: 700px) { .hp-buoy .hp-fl { font-size: 19.5px; } }
.hp-key { list-style: none; margin: 1rem 0 .2rem; padding: .75rem 0 0; display: grid; gap: .55rem;
          border-top: 1px dashed rgba(15, 19, 25, .14); }
.doc .hp-key li { display: grid; grid-template-columns: 7.2rem 1fr; gap: .8rem; margin: 0; font-size: .875rem; line-height: 1.55; color: var(--muted); }
.doc .hp-key li b { font-family: var(--mono); font-size: .72rem; letter-spacing: .1em; text-transform: uppercase; font-weight: 700; padding-top: .15em; }
.hp-key .k-weight b { color: var(--hp-weight); } .hp-key .k-buoy b { color: var(--hp-water-ink); } .hp-key .k-free b { color: var(--fg); }
@media (max-width: 700px) { .doc .hp-key li { grid-template-columns: 1fr; gap: .1rem; } }
/* ---- section 2.2: fixed plate against floating plate -------------------- */
.hp-level { --lvl: 0; }
.hp-lvl-panels { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem 1.6rem; }
.hp-lvl-panel { margin: 0; }
.hp-lvl-state { display: flex; align-items: center; gap: .5rem; margin: .35rem 0 0; min-height: 1.6em;
                font-family: var(--mono); font-size: .75rem; letter-spacing: .06em; text-transform: uppercase; font-weight: 600; color: var(--hp-leaf-ink); }
.hp-lvl-state::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: currentColor; flex: none; }
.hp-lvl-state.is-bad { color: var(--w-amber-700, #92610c); }
@media (max-width: 700px) { .hp-lvl-panels { grid-template-columns: minmax(0, 1fr); } }
.dw .hp-res { fill: none; stroke: var(--fg); stroke-width: 2.2; stroke-linejoin: round; }
.dw .hp-slab { fill: var(--hp-pla); }
.dw .hp-airmini { fill: var(--hp-air); }
.dw .hp-hold { fill: var(--hp-hold); }
.dw .hp-window { fill: rgba(79, 156, 111, .13); }
.dw .hp-endline { stroke: var(--hp-water-ink); stroke-width: 1.1; stroke-dasharray: 5 4; opacity: .75; }
.dw .hp-bracket { fill: none; stroke: var(--muted); stroke-width: 1.2; }
.dw .hp-bracket-ok { stroke: var(--hp-leaf-ink); }
.dw .hp-tag { font-size: 12.5px; fill: var(--fg); letter-spacing: .04em; }
.dw .hp-tag-dim { fill: var(--dim); }
.dw .hp-tag-warn { fill: var(--w-amber-700, #92610c); font-weight: 700; }
.dw .hp-tag-ok { fill: var(--hp-leaf-ink); font-weight: 700; }
.dw .hp-title { font-size: 13px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; fill: var(--amber); }
.dw .lvl-root.is-dry { stroke: var(--w-amber-700, #92610c); stroke-width: 1.8; }
.hp-lvl-ctl { display: grid; grid-template-columns: auto 1fr; gap: .4rem 1rem; align-items: center; margin: 1.1rem 0 .1rem; padding-top: .8rem;
              border-top: 1px dashed rgba(15, 19, 25, .14); }
.hp-lvl-ctl[hidden] { display: none; }
.hp-lvl-play { font: 600 .72rem/1 var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--amber); background: #fff;
               border: 1px solid color-mix(in srgb, var(--amber) 45%, transparent); border-radius: 999px; padding: .55rem .95rem; cursor: pointer; min-height: 44px; }
.hp-lvl-play:hover, .hp-lvl-play:focus-visible { background: color-mix(in srgb, var(--amber) 8%, #fff); }
.hp-lvl-range { width: 100%; accent-color: var(--hp-water-ink); min-height: 44px; margin: 0; }
.hp-lvl-ends { grid-column: 2; display: flex; justify-content: space-between; margin-top: -.35rem;
               font-family: var(--mono); font-size: .6875rem; letter-spacing: .06em; text-transform: uppercase; color: var(--dim); }
/* ---- section 6: design evolution ---------------------------------------- */
.hp-evo-fig { margin: 0 -.2rem; }
.hp-evo-svg { display: block; width: 100%; height: auto; }
.dw .hp-pole { stroke: var(--fg); stroke-width: 1.6; }
.dw .hp-washer { fill: #c4c9d0; stroke: var(--fg); stroke-width: .8; }
.dw .hp-handle-mini { fill: var(--hp-handle); stroke: var(--fg); stroke-width: .7; }
.dw .hp-evo-note { fill: none; stroke: var(--hp-water-ink); stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }


/* ---- the record on small screens --------------------------------------------
   DiOPAL's stylesheet collapses the record's grid and hides the contents rail
   below 1100px; this page's copy of that stylesheet never got the block, so on a
   phone the rail was drawn on top of the text. Same rules, same reasons (see the
   comment in diopal/css/style.css). */
@media (max-width: 1100px) {
  .doc-wrap { grid-template-columns: minmax(0, 760px); justify-content: center; }
  .doc-nav { display: none; }
  .doc { grid-column: 1; min-width: 0; }
}
@media (max-width: 900px) {
  .doc-wrap { padding-bottom: 4rem; }
  .doc { padding-top: 3rem; }
  .part { margin: 4.5rem 0 2.2rem; }
  .sec p, .sec li { font-size: 1rem; }
  .doc-head h1 { font-size: clamp(1.7rem, 8vw, 2.4rem); }
  .standfirst { font-size: 1rem; }
}

/* the status legend (polish.css: a marker | sample | meaning grid) puts its meaning
   under the sample on a phone instead of squeezing it into a third column */
@media (max-width: 700px) {
  .doc-head .status { grid-template-columns: auto 1fr; justify-items: start; }
  .doc-head .status-label { grid-column: 1 / -1; }
}
/* amber marks inside table cells stay on one line */
.doc table.hp-parts td .todo { white-space: nowrap; }
/* polish.css raises the first letter of a part's first paragraph; a note under a table is not one */
body.doc-light .doc .part + .sec > p.fineprint:first-of-type::first-letter { float: none; font-size: inherit; color: inherit; padding: 0; line-height: inherit; font-weight: inherit; }

/* phone floor for the two micro-labels polish.css's floor does not list: table
   captions and the map's numbers (8.8px and 9.6px on a 390px screen) */
@media (max-width: 700px) {
  .doc-map .map-n { font-size: 11px !important; }
}

/* ═══ after the judgement, 3 Oct 2026 ═════════════════════════════════════
   (JUDGE.md / FIX-LOG.md in the night folder.) The record's own accents take
   the team wiki's leaf, as wiki-align.css already did for the shared ones, so
   violet means one thing on this page: sealed air. The tables take the team's
   dress, as DiOPAL's and the photometer's do. Panels lose their shadow. */

/* violet is the sealed air; everything decorative is leaf */
body.doc-light .hp-pin { color: var(--w-leaf-700); border-color: var(--w-leaf-700); }
body.doc-light .hp-feats li.has-pin .hp-fn { color: var(--w-leaf-700); border-color: var(--w-leaf-700); }
body.doc-light .hp-req-n { color: var(--w-leaf-700); }
body.doc-light .dw .dw-title, body.doc-light .dw .hp-title { fill: var(--w-leaf-900); }
body.doc-light .dw .dw-secline { stroke: var(--w-leaf-700); }
body.doc-light .dw .dw-secah, body.doc-light .dw .dw-secl { fill: var(--w-leaf-700); }
body.doc-light .hp-lvl-play { color: var(--w-leaf-900); border-color: var(--w-leaf-200); }
body.doc-light .hp-lvl-play:hover, body.doc-light .hp-lvl-play:focus-visible { background: var(--w-leaf-50); border-color: var(--w-leaf-500); }
/* the rail showed two markers on its live entry: the shared leaf fill and this page's violet edge */
body.doc-light .doc-nav a.on { border-left-color: transparent; }
body.doc-light .doc-nav a.on b { color: var(--w-leaf-700); }
/* the status line is the key to the orange marks: paper and a hairline, not a violet wash */
body.doc-light .doc-head .status { background: #fff; border-color: var(--w-line); }
body.doc-light .doc-head .status-dot { background: #c2410c; }


/* requirements: one rule under the heading, and the cross-links read as section references */
.doc .hp-reqs { border-top: 0; }
.hp-req-x { display: block; margin-top: .3rem; font-size: .8125rem; color: var(--dim); }
.doc .hp-req-a .hp-req-x a { font-family: inherit; font-size: inherit; margin-left: 0; }

/* the way back to the walkthrough is an ordinary link, not a mono label */
.doc .hp-up a { font-family: inherit; font-size: .9375rem; letter-spacing: 0; text-transform: none; }

/* table captions read like the figure captions (wiki-align.css now dresses the header row and the
   hairlines the team's way; polish.css still set the captions as 8.8px mono labels) */
.doc :is(table.hp-geom, table.hp-parts) caption {
  font-family: var(--sans); font-size: .875rem; letter-spacing: 0; text-transform: none;
  color: var(--muted); padding-bottom: .55rem; text-align: left;
}
.doc :is(table.hp-geom, table.hp-parts) caption .fignum {
  font-family: var(--sans); font-size: inherit; letter-spacing: 0; text-transform: none; font-weight: 700; }
.doc :is(table.hp-geom, table.hp-parts) caption .fignum::after { content: "."; }
body.doc-light .doc :is(table.hp-geom, table.hp-parts) caption .fignum { color: var(--w-leaf-700); }
/* polish.css's HUD bracket on .table-scroll landed on the last cell */
body.doc-light .doc .table-scroll::after { display: none; }

@media (max-width: 700px) {
  /* polish.css floors caption numbers at 11px; here they lead a 14px caption, so they take its size */
  body.doc-light .doc :is(table.hp-geom, table.hp-parts) caption .fignum { font-size: inherit !important; }
  /* the plan's title was 9.2px at 390 (13px in a 460-unit box drawn 322px wide) */
  .dw-plan .dw-title { font-size: 16px; }
}

/* ═══ polishing round, 3 Oct 2026 ═════════════════════════════════════════
   (polish/hydroponics/LOG.md.) DBTL made visible, the iteration story drawn as a path,
   the displacement sum as a load line, the parts shown as files, the walkthrough indexed. */

/* ---- §6: the three versions as a path. A leaf rail runs node to node; between two versions sits the
   doc's own reason for the change, with an arrow down the rail. The drawings are the night's schematics. */
body.doc-light .doc-wrap section.sec > ol.hp-path { max-width: none; }
.doc ol.hp-path { list-style: none; margin: 1.9rem 0 1rem; padding: 0; }
.doc ol.hp-path > li { margin: 0; padding: 0; }
.hp-v-row {
  position: relative; display: grid; grid-template-columns: 2.5rem minmax(0, 14.5rem) minmax(0, 1fr);
  column-gap: 1.3rem; align-items: start; padding-bottom: 1.2rem;
}
.hp-path > li:last-child .hp-v-row { padding-bottom: 0; }
.hp-v-row::before, .hp-v-why::before {
  content: ""; position: absolute; left: calc(1.25rem - 1px); width: 2px; background: var(--w-leaf-200);
}
.hp-v-row::before { top: 0; bottom: 0; }
.hp-path > li:first-child .hp-v-row::before { top: 1.25rem; }
.hp-path > li:last-child .hp-v-row::before { bottom: auto; height: 1.25rem; }
.hp-v-node {
  position: relative; z-index: 1; width: 2.5rem; height: 2.5rem; border-radius: 50%; display: grid; place-items: center;
  background: #fff; border: 2px solid var(--w-leaf-700); font: 700 .8125rem/1 var(--sans); color: var(--w-leaf-900);
}
.hp-path > li.is-now .hp-v-node { background: var(--w-leaf-700); color: #fff; }
.hp-v-row .hp-evo-fig { margin: 0; padding: .5rem .55rem .25rem; border: 1px solid var(--w-line); border-radius: .375rem; background: #fff; }
.hp-path > li.is-now .hp-evo-fig { border-color: var(--w-leaf-500); }
.doc .hp-v-tx h4 { display: block; margin: .5rem 0 .35rem; font-size: 1.06rem; line-height: 1.3; letter-spacing: -.01em; color: var(--w-leaf-900); }
.hp-v-tag {
  display: inline-block; margin-left: .45rem; padding: .18rem .45rem; border-radius: .25rem; vertical-align: .12em;
  font: 600 .75rem/1.2 var(--sans); letter-spacing: .02em; color: var(--w-leaf-900); background: var(--w-leaf-50); border: 1px solid var(--w-leaf-200);
}
body.doc-light .doc-wrap .hp-v-tx p { margin: 0; max-width: 60ch; font-size: .97rem; line-height: 1.65; color: #262626; }
body.doc-light .doc-wrap ol.hp-path p.hp-v-why {
  position: relative; margin: 0; max-width: none; padding: 0 0 1.35rem calc(2.5rem + 1.3rem);
  font-size: .92rem; line-height: 1.6; color: #4a5560;
}
.hp-v-why::before { top: 0; bottom: 0; }
.hp-v-why::after {
  content: ""; position: absolute; left: calc(1.25rem - 5px); bottom: .5rem;
  border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 7px solid var(--w-leaf-500);
}
.hp-v-why > b {
  display: block; margin-bottom: .2rem; font: 700 .75rem/1.3 var(--sans); letter-spacing: .09em; text-transform: uppercase;
  color: var(--w-leaf-700);
}
.doc p.fineprint.hp-v-note { margin-top: 1rem; }
@media (max-width: 760px) {
  .hp-v-row { grid-template-columns: 2.1rem minmax(0, 1fr); column-gap: .9rem; row-gap: .5rem; }
  .hp-v-row > .hp-evo-fig { grid-column: 2; max-width: 300px; }
  .hp-v-row > .hp-v-tx { grid-column: 2; }
  .hp-v-node { width: 2.1rem; height: 2.1rem; font-size: .75rem; }
  .hp-v-row::before, .hp-v-why::before { left: calc(1.05rem - 1px); }
  .hp-path > li:first-child .hp-v-row::before { top: 1.05rem; }
  .hp-path > li:last-child .hp-v-row::before { height: 1.05rem; }
  .hp-v-why::after { left: calc(1.05rem - 5px); }
  body.doc-light .doc-wrap ol.hp-path p.hp-v-why { padding-left: calc(2.1rem + .9rem); }
  .doc .hp-v-tx h4 { margin-top: .15rem; }
}

/* ---- §4: the load line. The SVG is stretched to the plot box (non-scaling strokes); ticks, dots and notes
   are HTML placed in % of the box, so their type keeps its real size at every width. Single series: no legend,
   the axis titles and the caption name it. Gridlines are hairlines; the one dashed line is a bound. */
.hp-load { position: relative; margin: .2rem 0 0; padding: 0 .3rem 0 2.4rem; }
body.doc-light .doc-wrap .doc .hp-load p.hp-load-ylab, body.doc-light .doc-wrap .doc .hp-load p.hp-load-xlab {
  margin: 0; max-width: none; font: 600 .8125rem/1.3 var(--sans); color: #4a5560; }
body.doc-light .doc-wrap .doc .hp-load p.hp-load-ylab { margin: 0 0 .7rem -2.4rem; }
body.doc-light .doc-wrap .doc .hp-load p.hp-load-xlab { margin-top: 2.1rem; text-align: right; }
.hp-load-plot { position: relative; height: 310px; cursor: crosshair; touch-action: pan-y; }
.hp-fig svg.hp-load-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }  /* outranks .hp-fig svg's height: auto */
.hp-load-grid line { stroke: #e8ebef; stroke-width: 1; vector-effect: non-scaling-stroke; }
.hp-load-axis { stroke: #b8bfc8; stroke-width: 1; vector-effect: non-scaling-stroke; }
.hp-load-pla { stroke: #6b7580; stroke-width: 1.25; stroke-dasharray: 5 4; vector-effect: non-scaling-stroke; }
.hp-load-curve { fill: none; stroke: var(--hp-water-ink); stroke-width: 2.25; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.hp-load-tick { position: absolute; font: 500 12px/1 var(--mono); color: #5f6b78; white-space: nowrap; pointer-events: none; }
.hp-load-tick.y { left: -.6rem; transform: translate(-100%, -50%); }
.hp-load-tick.x { top: calc(100% + .55rem); transform: translateX(-50%); }
.hp-load-dot {
  position: absolute; width: 8px; height: 8px; margin: -4px 0 0 -4px; border-radius: 50%;
  background: var(--hp-water-ink); box-shadow: 0 0 0 2px #fff; pointer-events: none; transition: transform .15s ease;
}
.hp-load-dot.is-key { width: 12px; height: 12px; margin: -6px 0 0 -6px; background: var(--w-leaf-700); }
.hp-load-dot.is-pla { width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; background: #fff; box-shadow: inset 0 0 0 2px #6b7580, 0 0 0 2px #fff; }
.hp-load-dot.is-on { transform: scale(1.5); }
.hp-load-note {
  position: absolute; font: 600 .8125rem/1.3 var(--sans); color: #262626; white-space: nowrap; pointer-events: none;
}
.hp-load-note small { display: block; font: 500 .75rem/1.35 var(--mono); color: #4a5560; }
.hp-load-note.n-drawn, .hp-load-note.n-slab, .hp-load-note.n-end { text-align: right; }
.hp-load-note.n-drawn { color: var(--w-leaf-900); }
body.doc-light .doc-wrap .doc p.hp-load-off { margin: .9rem 0 0; max-width: none; font-size: .875rem; line-height: 1.5; color: #4a5560; text-align: right; }
/* the reading layer (js/record.js): a hairline that snaps to the nearest computed point, and its values */
.hp-load-hit { position: absolute; inset: -8px 0; z-index: 2; border-radius: .25rem; outline: none; }
.hp-load-hit:focus-visible { box-shadow: 0 0 0 2px var(--w-leaf-500); }
.hp-load-x { position: absolute; top: 0; bottom: 0; width: 0; border-left: 1px solid #8a939e; pointer-events: none; z-index: 1; }
.hp-load-cur { position: absolute; z-index: 1; width: 10px; height: 10px; margin: -5px 0 0 -5px; border-radius: 50%;
  background: var(--hp-water-ink); box-shadow: 0 0 0 2px #fff, 0 0 0 3px var(--hp-water-ink); pointer-events: none; }
.hp-load-read {
  position: absolute; z-index: 3; top: .35rem; padding: .45rem .6rem; border-radius: .3125rem; pointer-events: none;
  background: #fff; border: 1px solid var(--w-line); box-shadow: 0 6px 18px -10px rgba(15, 19, 25, .35);
  font: 500 .75rem/1.45 var(--sans); color: #4a5560; white-space: nowrap;
}
.hp-load-read b { display: block; font: 700 .9375rem/1.25 var(--sans); color: #1d232b; }
.hp-load-x[hidden], .hp-load-cur[hidden], .hp-load-read[hidden] { display: none; }
/* Table 1's rows are the chart's dots: a row under the pointer lights its dot */
.doc table.hp-disp tbody tr.is-on td { background: var(--w-leaf-50); }
@media (max-width: 700px) {
  .hp-load { padding-left: 2rem; }
  body.doc-light .doc-wrap .doc .hp-load p.hp-load-ylab { margin-left: -2rem; }
  .hp-load-plot { height: 240px; }
  .hp-load-tick { font-size: 11px; }
  .hp-load-tick.y { left: -.45rem; }
  .hp-load-note { font-size: 11px; }
  .hp-load-note small { font-size: 11px; }
}
@media (prefers-reduced-motion: reduce) { .hp-load-dot { transition: none; } }
/* the load line's three landmarks: pins like the cut-open render's, set off the point toward the empty side */
.hp-load-pin {
  display: inline-grid; place-items: center; width: 20px; height: 20px; border-radius: 50%; flex: none;
  background: #fff; border: 2px solid var(--w-leaf-700); font: 700 11px/1 var(--mono); color: var(--w-leaf-900);
}
.hp-load-marks { position: absolute; inset: 0; pointer-events: none; }
.hp-load-marks > .hp-load-pin { position: absolute; z-index: 1; margin: 0; transform: translate(5px, -27px); pointer-events: none; }
.doc .hp-load-key {
  list-style: none; margin: 1.1rem 0 0; padding: .8rem 0 0; border-top: 1px dashed rgba(15, 19, 25, .14);
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .55rem 1.6rem; max-width: none;
}
.doc .hp-load-key li { display: flex; gap: .6rem; align-items: flex-start; margin: 0; font-size: .875rem; line-height: 1.5; color: #4a5560; }
.doc .hp-load-key li b { color: #262626; font-weight: 600; }
.hp-load-key .hp-load-pin { margin-top: .05rem; }
.hp-load-sw { flex: none; width: 20px; height: 20px; position: relative; }
.hp-load-sw::before { content: ""; position: absolute; left: 9px; top: 0; bottom: 0; border-left: 1.5px dashed #6b7580; }
@media (max-width: 700px) { .doc .hp-load-key { grid-template-columns: minmax(0, 1fr); } }

/* ---- Table 3: each part beside its name, rendered from its own STL (photos/parts/, three.js, the hero's
   print colours: plate 2a2f37, handles 6b727b, holder 6b3f17; the same three-quarter view as the
   photometer's and the LPA's part renders) */
.hp-pc { display: inline-flex; align-items: center; gap: .7rem; }
.hp-thumb { flex: none; display: inline-flex; align-items: center; justify-content: center; gap: 2px; width: 3.4rem; height: 2.6rem; }
.hp-thumb img { display: block; max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; }
.hp-thumb.is-pair img { max-width: 48%; }
@media (max-width: 700px) { .hp-pc { gap: .5rem; } .hp-thumb { width: 2.7rem; height: 2.2rem; } }

/* ---- §9: the design files, as the photometer's and the LPA's cards (photometer/css/style.css, "9 the design
   files"): each part in its card's corner, cut by the edge and fading toward the text. No STL is published
   yet, so where their download button sits these carry the orange mark. */
body.doc-light .doc-wrap section.sec > ol.files { max-width: none; }
.doc .sec ol.files {
  list-style: none; margin: 1.3rem 0 1.6rem; padding: 0;
  display: grid; gap: .7rem; grid-template-columns: repeat(2, minmax(0, 1fr));
}
.doc .sec ol.files > li {
  position: relative; overflow: hidden; isolation: isolate;
  display: flex; gap: .8rem; margin: 0; padding: .85rem .95rem .8rem; min-height: 8.4rem;
  border: 1px solid var(--w-line); border-radius: .3125rem; background: #fff;
}
.files .fn {
  flex: none; width: 1.6rem; height: 1.6rem; border-radius: 50%; display: grid; place-content: center; margin-top: .05rem;
  border: 1.5px solid var(--w-leaf-700); font: 700 .8rem/1 var(--sans); color: var(--w-leaf-900);
}
.files .fb { position: relative; z-index: 1; min-width: 0; }
.files .fb b { display: block; font-size: .98rem; color: #1d232b; }
body.doc-light .doc-wrap .doc .sec .files .fb p { margin: .2rem 0 0; font-size: .88rem; line-height: 1.5; max-width: calc(100% - 5.2rem); color: #4a5560; }
body.doc-light .doc-wrap .doc .sec .files .fb p.dl { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .9rem; margin-top: .6rem; max-width: none; }
.files .f-part {
  position: absolute; z-index: 0; right: -18px; bottom: -22px; width: 150px; height: 118px;
  object-fit: contain; object-position: right bottom;
  pointer-events: none; user-select: none; opacity: .9;
  -webkit-mask-image: radial-gradient(130% 130% at 100% 100%, #000 45%, rgba(0,0,0,.6) 64%, transparent 86%);
          mask-image: radial-gradient(130% 130% at 100% 100%, #000 45%, rgba(0,0,0,.6) 64%, transparent 86%);
  transition: transform .45s cubic-bezier(.2,.8,.2,1), opacity .3s ease;
}
.doc .sec ol.files > li:hover .f-part, .doc .sec ol.files > li:focus-within .f-part { transform: translate(-6px, -6px); opacity: 1; }
@media (max-width: 700px) {
  .doc .sec ol.files { grid-template-columns: minmax(0, 1fr); }
  .files .f-part { width: 128px; height: 104px; right: -16px; bottom: -18px; }
}
@media (prefers-reduced-motion: reduce) { .files .f-part { transition: none; } }
@media print { .files .f-part { display: none; } }
/* the handles are tall: lift them so the foot that grips the slab shows; the holder is small, so smaller */
.files .f-part.is-tall { bottom: -6px; height: 112px; }
.files .f-part.is-small { width: 112px; height: 92px; right: -14px; bottom: -16px; }
@media (max-width: 700px) {
  .files .f-part.is-tall { bottom: -4px; height: 100px; }
  .files .f-part.is-small { width: 100px; height: 84px; }
}
/* Table 3's cells centre on their picture */
.doc table.hp-parts:not(.hp-disp) td { vertical-align: middle; }


/* ---- ties between words and figures (js/record.js) ---- */
.hp-pin { transition: transform .18s ease, background-color .18s ease, color .18s ease; }
body.doc-light .hp-pin.is-on { transform: translate(-50%, -50%) scale(1.3); background: var(--w-leaf-700); color: #fff; }
.hp-legend li.is-tie { cursor: default; border-radius: .25rem; padding: .1rem .3rem; margin: -.1rem -.3rem; }
.hp-legend li.is-on { background: var(--w-leaf-50); color: #262626; }
.hp-sheet .dw > *, .hp-buoy-wrap .dw > * { transition: opacity .18s ease; }
.is-tied .dw > :not(.tie-on):not(defs):not(g) { opacity: .16; }
.is-tied .dw > g > :not(.tie-on) { opacity: .16; }
.hp-key li.is-tie { cursor: default; border-radius: .25rem; }
.hp-key li.is-on { background: var(--w-leaf-50); }
@media (prefers-reduced-motion: reduce) { .hp-pin, .hp-sheet .dw > *, .hp-buoy-wrap .dw > * { transition: none; } }

/* ---- the status line's section numbers are links to the gaps they name */
body.doc-light .doc-head .status-label a { color: var(--w-leaf-700); text-decoration: underline; text-decoration-color: var(--w-leaf-200); text-underline-offset: 2px; border: 0; }
body.doc-light .doc-head .status-label a:hover { text-decoration-color: currentColor; }

/* ---- Table 2 in three groups: the plate, its flotation, the printed parts */
.doc table.hp-geom tr.hp-grp th {
  padding: .9rem .75rem .35rem; border-bottom: 1px solid var(--w-leaf-200); background: #fff; text-align: left;
  font: 700 .75rem/1.2 var(--sans); letter-spacing: .09em; text-transform: uppercase; color: var(--w-leaf-700);
}

/* ---- header: the plate in four numbers (all from the record below) */
.doc .hp-keynums {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; margin: 1.6rem 0 1.5rem; max-width: none;  /* the column, like the status box below (judge pass) */
  border-top: 1px solid var(--w-line); border-bottom: 1px solid var(--w-line);
}
.doc .hp-keynums > div { padding: .85rem 1rem .8rem; }
.doc .hp-keynums > div + div { border-left: 1px solid var(--w-line); }
.doc .hp-keynums dt { margin: 0 0 .3rem; font: 700 .75rem/1.2 var(--sans); letter-spacing: .09em; text-transform: uppercase; color: var(--w-leaf-700); }
.doc .hp-keynums dd { margin: 0; }
.doc .hp-keynums b { display: block; font: 750 1.75rem/1.05 var(--sans); letter-spacing: -.02em; color: var(--w-leaf-900); }
.doc .hp-keynums b em { font-style: normal; font-size: .55em; font-weight: 650; letter-spacing: 0; color: var(--w-leaf-700); }
.doc .hp-keynums span { display: block; margin-top: .3rem; font-size: .8125rem; line-height: 1.4; color: #4a5560; }
@media (max-width: 700px) {
  .doc .hp-keynums { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .doc .hp-keynums > div { padding: .75rem .8rem; }
  .doc .hp-keynums > div:nth-child(3) { border-left: 0; }
  .doc .hp-keynums > div:nth-child(n+3) { border-top: 1px solid var(--w-line); }
  .doc .hp-keynums b { font-size: 1.5rem; }
}

/* the version's title and its tag sit on one line; the tag stays out of the heading */
.hp-v-h { display: flex; flex-wrap: wrap; align-items: baseline; gap: .15rem .5rem; margin: .5rem 0 .35rem; }
.hp-v-h > h4 { margin: 0 !important; }
.hp-v-h > .hp-v-tag { margin-left: 0; }
@media (max-width: 760px) { .hp-v-h { margin-top: .15rem; } }

/* 9 Oct 2026 polish: section numbers sit next to their titles, as on the photometer page
   (photometer/css/style.css, "section numbers sit next to their titles, not in a 59px column").
   ../css/polish.css gives `.sec h3 i` a 2.6em min-width, which left a wide gap ("3.1      Design")
   on this page only. A heading that wraps keeps its number on the first line, as on the bioreactor page
   (`align-items: first baseline`; polish.css centres it). Delete this block to revert. */
body.doc-light[data-instrument="hydroponics"] .doc-wrap section.sec > h3 { gap: 0; align-items: first baseline; }
body.doc-light[data-instrument="hydroponics"] .doc-wrap section.sec > h3 > i { min-width: 0; }

/* 15 Oct 2026. The three versions are stacked prototypes now (hwdoc.css 4b), each with its side view
   as a small white sheet at the head of its Design. */
.doc .cyc .hp-evo-fig--cyc {
  max-width: 340px; margin: 0 0 1rem; padding: .5rem .55rem .25rem;
  border: 1px solid var(--w-line); border-radius: .5rem; background: #fff;
}

/* ---- the four design files, published 18 October 2026 ---------------------
   The parts were always drawn here and never downloadable; the meshes now sit
   in models/ and each one gets the same download chip the photometer's parts
   list uses, in this page's own leaf palette. */
body.doc-light .doc-wrap .doc .sec .files .dl a {
  display: inline-flex; align-items: baseline; gap: .45rem;
  padding: .28rem .6rem; border-radius: .3125rem;
  border: 1px solid var(--w-leaf-200, #cfe4d8); background: var(--w-leaf-50, #f4f9f6);
  font: 700 .8rem/1.3 var(--sans); color: var(--w-leaf-900, #14402b); text-decoration: none;
}
body.doc-light .doc-wrap .doc .sec .files .dl a span { font: 500 .74rem/1.3 var(--mono); color: var(--w-leaf-700, #23684a); }
body.doc-light .doc-wrap .doc .sec .files .dl a:hover,
body.doc-light .doc-wrap .doc .sec .files .dl a:focus-visible {
  border-color: var(--w-leaf-500, #4f9c6f); background: #fff;
}
