/* HealthX Journal — the shared chassis.
 *
 * Layering rule (see landing-page/README.md):
 *   1. This file is the chassis. Every page loads it. Nothing here is post-specific.
 *   2. /blog/index.html loads ONLY this file. The index is the constant the reader
 *      returns to, so it never carries a per-page look.
 *   3. A post may add /blog/<slug>/post.css after this file and scope every rule to
 *      body[data-post="<slug>"]. That file may retheme tokens and add instruments the
 *      post invents. It may not restyle the nav, the footer, the reading measure, or
 *      the serif/mono pairing — those are what make separate pages one publication.
 */

:root {
  --ground: #F0F2EF;
  --surface: #E4E8E3;
  --surface-2: #FBFCFA;
  --ink: #14191A;
  --ink-2: #414A48;
  --ink-3: #6E7A77;
  --rule: #C9D0CB;
  --accent: #5A2A4E;
  --accent-soft: #E8DCE5;
  --amber: #9A5D12;
  --good: #33604A;
  --good-bg: #DCE7DF;
  --bad: #8E2731;
  --bad-bg: #F0DBDC;
  --hold: #12706F;
  --hold-bg: #DCE9E9;
  --flat: #5C6A65;
  --flat-bg: #E6EAE8;
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;
  --ui: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --data: #173B31;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", "Segoe UI Mono", Menlo, Consolas, monospace;
  --measure: 34rem;
  --measure-wide: 50rem;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground: #101413;
    --surface: #191F1E;
    --surface-2: #0A0D0C;
    --ink: #E7EAE5;
    --ink-2: #B3BCB8;
    --ink-3: #7E8A86;
    --rule: #2C3533;
    --accent: #C48FB3;
    --accent-soft: #2E2030;
    --amber: #D99A45;
    --good: #7BAA88;
    --good-bg: #1C2B22;
    --bad: #D4707A;
    --bad-bg: #2E1A1D;
    --hold: #6FB6B4;
    --hold-bg: #16292A;
    --flat: #9AA6A1;
    --flat-bg: #202826;
    --data: #7BAA88;
  }
}
:root[data-theme="dark"] {
  --ground: #101413;
  --surface: #191F1E;
  --surface-2: #0A0D0C;
  --ink: #E7EAE5;
  --ink-2: #B3BCB8;
  --ink-3: #7E8A86;
  --rule: #2C3533;
  --accent: #C48FB3;
  --accent-soft: #2E2030;
  --amber: #D99A45;
  --good: #7BAA88;
  --good-bg: #1C2B22;
  --bad: #D4707A;
  --bad-bg: #2E1A1D;
  --hold: #6FB6B4;
  --hold-bg: #16292A;
  --flat: #9AA6A1;
  --flat-bg: #202826;
  --data: #7BAA88;
}

* { box-sizing: border-box; }
html { color-scheme: light dark; }
body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--serif);
  font-size: 1.0625rem;
  line-height: 1.68;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--accent); text-decoration-color: var(--accent-soft); text-underline-offset: .15em; }
a:hover { text-decoration-color: var(--accent); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.wrap { max-width: 72rem; margin: 0 auto; padding: 0 1.5rem 6rem; }
.col { max-width: var(--measure); margin-inline: auto; }
/* Instruments, tables and figures need more room than running text. */
.col--wide { max-width: var(--measure-wide); }
.col > * + * { margin-top: 1.15em; }

/* ---- site nav ---- */
.site-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.4rem 1.5rem;
  border-bottom: 1px solid var(--rule);
}
.site-nav .wordmark {
  font-family: var(--mono);
  font-size: .78rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: none;
  font-weight: 500;
}
.site-nav .back {
  font-family: var(--mono);
  font-size: .72rem;
  letter-spacing: .06em;
  color: var(--ink-3);
  text-decoration: none;
}
.site-nav .back:hover { color: var(--accent); }

/* ---- masthead ---- */
header.masthead {
  padding: 5rem 0 2.5rem;
  border-bottom: 1px solid var(--rule);
  margin-bottom: 3rem;
}
.eyebrow {
  font-family: var(--mono);
  font-size: .7rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0 0 1.6rem;
}
h1 {
  font-size: clamp(2.6rem, 7vw, 4.6rem);
  line-height: 1.02;
  letter-spacing: -.02em;
  font-weight: 400;
  margin: 0;
  text-wrap: balance;
}
h1 em { font-style: italic; color: var(--accent); }
.standfirst {
  font-size: 1.2rem;
  line-height: 1.5;
  color: var(--ink-2);
  font-style: italic;
  margin: 1.4rem 0 0;
  max-width: 32rem;
}
.byline {
  font-family: var(--mono);
  font-size: .72rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-top: 2.2rem;
  display: flex;
  flex-wrap: wrap;
  gap: 1.4rem;
}

/* ---- entry list (journal index) ---- */
.entries { display: grid; gap: 0; }
.entry {
  display: block;
  position: relative;
  padding: 2.4rem 0;
  border-bottom: 1px solid var(--rule);
  text-decoration: none;
  color: inherit;
}
.entry:first-child { padding-top: 0; }
.entry.entry--no-thumb { display: block; }
.entry-thumb {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  margin: 0 0 1.25rem;
  overflow: hidden;
  border: 1px solid var(--rule);
  border-radius: 3px;
  background: var(--surface);
}
.entry-thumb[hidden] { display: none; }
.entry-thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 48%;
  filter: saturate(.78) contrast(.95);
  transition: transform .22s ease-out;
}
.entry:hover .entry-thumb img { transform: scale(1.025); }
.entry-body { min-width: 0; }
@media (min-width: 36rem) and (max-width: 54.999rem) {
  .entry {
    display: grid;
    grid-template-columns: 8.5rem minmax(0, 1fr);
    gap: 1.5rem;
    align-items: start;
  }
  .entry-thumb {
    width: 100%;
    aspect-ratio: 4 / 5;
    margin: 0;
  }
}
@media (min-width: 55rem) {
  .entry-thumb {
    position: absolute;
    top: 0;
    right: calc(100% + clamp(1.5rem, 3vw, 3rem));
    width: clamp(7.5rem, 12vw, 11rem);
    aspect-ratio: 4 / 5;
    margin: 0;
  }
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .entry-thumb img {
    filter: saturate(.68) contrast(.93) brightness(.78);
  }
}
:root[data-theme="dark"] .entry-thumb img {
  filter: saturate(.68) contrast(.93) brightness(.78);
}
@media (prefers-reduced-motion: reduce) {
  .entry-thumb img { transition: none; }
}
.entry-kicker {
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 .7rem;
}
.entry-title {
  font-size: 1.7rem;
  line-height: 1.18;
  font-weight: 400;
  letter-spacing: -.01em;
  margin: 0;
  text-wrap: balance;
}
.entry:hover .entry-title { color: var(--accent); }
.entry-dek {
  font-size: 1.02rem;
  color: var(--ink-2);
  margin: .7rem 0 0;
  line-height: 1.55;
  max-width: 40rem;
}
.entry-meta {
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-top: 1rem;
  display: flex;
  gap: 1.1rem;
  flex-wrap: wrap;
}

/* ---- essay ---- */
h2 {
  font-size: 1.9rem;
  line-height: 1.16;
  font-weight: 400;
  letter-spacing: -.01em;
  margin: 3.6rem 0 0;
  text-wrap: balance;
}
h2[data-mark]::before {
  content: attr(data-mark);
  display: block;
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: .9rem;
}
.lede { font-size: 1.22rem; line-height: 1.55; }
.beat {
  font-size: 1.24rem;
  line-height: 1.4;
  font-style: italic;
  color: var(--accent);
  margin-block: 1.7em;
  text-wrap: balance;
}
blockquote {
  margin: 1.6em 0;
  padding-left: 1.3rem;
  border-left: 2px solid var(--accent);
  font-style: italic;
  color: var(--ink-2);
}
blockquote .attrib {
  display: block;
  margin-top: .5rem;
  font-style: normal;
  font-family: var(--mono);
  font-size: .72rem;
  color: var(--ink-3);
}
strong { font-weight: 600; }

/* ---- instruments ----
 *
 * A figure in a print publication is ruled off from the text and sits on the
 * same paper. It is not a panel floating above the page. The fill, the 14px
 * radius and the drop shadow made these read as app widgets pasted into a
 * reading surface — and they cost real height too: a filled box needs interior
 * padding on all four sides to stop the type touching its edge, so the frame
 * was buying its own overhead.
 *
 * What replaces it: a rule above and below, the figure on the page ground, and
 * horizontal padding only. The card's remaining job — telling the reader where
 * the figure starts and stops — is what the two rules do, at 2px instead of
 * ~40px of box.
 */
.instrument {
  container-type: inline-size;
  border-top: 1px solid var(--ink);
  border-bottom: 1px solid var(--rule);
  padding: .9rem 0 1.1rem;
  margin: 3rem 0;
  font-family: var(--ui);
}

/* The wordmark signs the figure, because these get screenshotted out of the page
   and an unsigned chart reads as the study's own. It used to sit in a band of its
   own with a rule under it — 46px and a horizontal line spent on a line of text
   that carries no information about the data. It now rides at the top of the head
   at kicker weight, so the signature survives and the chrome does not. */
.mark {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  font-family: var(--mono);
  font-size: .62rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  /* --ink-2, not --ink-3: at this size --ink-3 on --surface-2 is 4.32:1, under
     AA, and 10px mono is nowhere near the large-text exemption. */
  color: var(--ink-2);
}
.mark-bar { width: 14px; height: 2px; background: var(--data); }

/* Signature, title, note — one stack, one rule under all three. The note sits
   with the title on a wide column and drops below it when there is no room. */
.instrument-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  column-gap: .8rem;
  row-gap: .3rem;
  padding-bottom: .7rem;
  border-bottom: 1px solid var(--rule);
  margin-bottom: .95rem;
}
.instrument-head .mark { grid-column: 1 / -1; }
.instrument-title {
  font-family: var(--ui);
  font-size: 1.05rem;
  font-weight: 800;
  line-height: 1.25;
  letter-spacing: -.01em;
  color: var(--ink);
  margin: 0;
  text-wrap: balance;
}
/* An editorial aside about how to read the figure — not a verdict on the data.
   It used to wear --good-bg/--good, the chassis' "this result was favourable"
   pair, which put a green pill in the head of a chart whose own marks use that
   same green to mean a real positive finding. Neutral, and no capsule. */
.instrument-note {
  font-family: var(--ui);
  font-size: .72rem;
  font-weight: 650;
  line-height: 1.35;
  color: var(--ink-2);
  text-align: right;
}
@container (max-width: 30rem) {
  .instrument-head { grid-template-columns: minmax(0, 1fr); }
  .instrument-note { text-align: left; }
}
.instrument figure { margin: 0; }
figcaption, .caption {
  font-family: var(--ui);
  font-size: .76rem;
  line-height: 1.55;
  color: var(--ink-3);
  margin: 1rem 0 0;
  padding-top: .85rem;
  border-top: 1px solid var(--rule);
}
/* The caption's rule exists to cut it off from the figure above. When an
   inspector follows, that panel draws its own rule 18px lower — two hairlines
   with a strip of nothing between them. The panel's rule wins, since it is the
   one that has to hold when the panel sticks to a phone viewport. */
.caption:has(+ .inspect) {
  border-top: 0;
  padding-top: 0;
}
.instrument .dek {
  font-family: var(--ui);
  font-size: .82rem;
  line-height: 1.45;
  color: var(--ink-3);
  margin: .4rem 0 0;
}

/* Controls used to sit on the instrument's --surface-2 fill, which read as raised
   because the card behind them was lighter than the page. With the card gone they
   are on --ground, where --surface-2 lands within 1.1:1 of it in dark mode — a
   button with no visible edge. --control-face gives them a face of their own,
   one step off the page in whichever direction the theme needs.
   Set on .instrument so a post retheming its tokens gets this for free. */
.instrument {
  --control-face: var(--surface-2);
  --control-face-hover: var(--surface);
  --control-edge: var(--rule);
}
/* Dark needs a bigger step than --surface gives: on --ground it lands at 1.11:1,
   which is a button you cannot see the edge of. --rule is the lightest neutral
   the palette has below text, and puts the face at ~1.6:1 — visible as a face
   while still reading as furniture rather than as data. */
/* Dark needs a bigger step than --surface gives: on --ground it lands at 1.11:1,
   which is a button you cannot see the edge of. --rule takes the face to 1.42:1;
   the border then has to leave --rule too, or the edge matches the fill exactly
   and the control loses its outline instead of gaining a face. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .instrument {
    --control-face: var(--rule);
    --control-face-hover: var(--surface);
    --control-edge: var(--ink-3);
  }
}
:root[data-theme="dark"] .instrument {
  --control-face: var(--rule);
  --control-face-hover: var(--surface);
  --control-edge: var(--ink-3);
}

/* controls shared by every instrument */
.pick, .preset, .ghost, .food {
  appearance: none;
  font: inherit;
  font-family: var(--ui);
  cursor: pointer;
  transition: background .16s ease-out, border-color .16s ease-out, color .16s ease-out, transform .12s ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .pick, .preset, .ghost, .food, .lane-dot, .goal-fill, .budget-fill { transition: none; }
}
.pick {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: .7rem;
  text-align: left;
  min-height: 44px;
  padding: 0 .85rem;
  border-radius: 8px;
  border: 1px solid var(--control-edge, var(--rule));
  background: var(--control-face, var(--surface-2));
  color: var(--ink-2);
  font-size: .88rem;
  font-weight: 650;
}
.pick:hover { border-color: var(--data); color: var(--ink); transform: translateY(-1px); }
.pick:active { transform: none; }
.pick[aria-pressed="true"] { background: var(--data); border-color: var(--data); color: var(--surface-2); }
.pick em { font-style: normal; font-size: .74rem; font-weight: 800; font-variant-numeric: tabular-nums; opacity: .7; }
.pick:focus-visible, .preset:focus-visible, .ghost:focus-visible, .food:focus-visible {
  outline: 2px solid var(--data); outline-offset: 2px;
}

/* ---- timeline ---- */
/* ---- timeline ----
 * The node was a 38px disc against a 49px two-line text block, so the ornament
 * stood nearly as tall as the entry it marked — and the icon inside carries
 * nothing the year and the sentence do not already say. At 26px it still anchors
 * the rail and still reads as a step, without competing with the type.
 */
.timeline { display: grid; gap: 0; }
.tl-row {
  display: grid;
  grid-template-columns: 3rem 26px minmax(0, 1fr);
  gap: .7rem;
  padding-bottom: .85rem;
  align-items: start;
}
.tl-row:last-child { padding-bottom: 0; }
.tl-year {
  font-family: var(--ui);
  font-size: .76rem;
  font-weight: 800;
  color: var(--data);
  font-variant-numeric: tabular-nums;
  text-align: right;
  padding-top: .32rem;
}
.tl-node { position: relative; display: grid; place-items: center; }
.tl-node .dot {
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--control-face, var(--surface-2)); border: 1.5px solid var(--control-edge, var(--rule));
  display: grid; place-items: center;
  margin-top: .1rem;
}
.tl-node .dot svg { width: 15px; height: 15px; stroke: var(--data); fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.tl-node .rail { position: absolute; top: calc(26px + .1rem); bottom: -.85rem; width: 1.5px; background: var(--rule); }
.tl-row:last-child .rail { display: none; }
.tl-what { padding-top: .2rem; font-family: var(--ui); font-size: .92rem; font-weight: 650; line-height: 1.35; color: var(--ink); }
.tl-what span { display: block; font-weight: 400; font-size: .78rem; color: var(--ink-3); margin-top: .25rem; line-height: 1.45; }

/* ---- dot plot (instrument 02) ---- */
.dotplot-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1.2rem; flex-wrap: wrap; }
.dotplot-svg { width: 100%; height: auto; display: block; margin-top: .8rem; }
.dotplot-svg .axis { stroke: var(--rule); stroke-width: 1; }
.dotplot-svg .conn { stroke: var(--rule); stroke-width: 2; }
.dotplot-svg .ref-dot { fill: var(--ink-3); opacity: .45; }
.dotplot-svg text { font-family: var(--ui); }
.pick-col { display: grid; gap: .4rem; flex: 0 0 auto; }
@container (max-width: 30rem) { .pick-col { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ---- lane chart (instrument 03) ---- */
.lane-group {
  font-family: var(--ui);
  font-size: .64rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 800;
  padding: 1.1rem 0 .5rem;
  border-bottom: 1px solid var(--rule);
  margin-bottom: .3rem;
}
.lane-group:first-child { padding-top: 0; }
.lane-row {
  display: grid;
  grid-template-columns: minmax(0, 9.5rem) minmax(0, 1fr) 4.4rem 7.2rem;
  gap: .8rem;
  align-items: center;
  padding: .3rem .4rem;
  margin: 0 -.4rem;
  border-radius: 6px;
}
.lane-row:hover { background: var(--control-face-hover, var(--surface)); }
.lane-name { font-size: .84rem; font-weight: 650; line-height: 1.3; color: var(--ink); }
.lane-name span { display: block; font-size: .68rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-3); margin-top: .15rem; }
.lane { width: 100%; height: auto; display: block; }
.lane-val {
  font-family: var(--ui);
  font-size: .74rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}
.lane .grid { stroke: var(--rule); stroke-width: 1; }
.lane .zero { stroke: var(--ink-3); stroke-width: 1.4; opacity: .5; }
.lane .link { stroke: var(--ink-3); stroke-width: 1.4; stroke-dasharray: 3 3; opacity: .5; }
/* The hollow ring reads as hollow by matching whatever is behind it — the page
   ground now, not a card fill. */
.lane .ring { fill: var(--ground); stroke-width: 2; }
.lane text { font-family: var(--ui); font-size: 10px; font-weight: 800; fill: var(--ink-3); }
.lane-dot { transition: cx .4s cubic-bezier(.22, .9, .3, 1); }
/* In a narrow column each row costs two lines instead of one, so seventeen rows
   is where per-row padding stops being a detail. Tightened rather than trimmed:
   the null results are the point of this chart, and dropping rows to save
   scroll would be an editorial cut disguised as a layout fix. */
@container (max-width: 30rem) {
  .lane-row { grid-template-columns: minmax(0, 1fr) auto auto; row-gap: .25rem; padding-bottom: .4rem; }
  .lane { grid-column: 1 / -1; order: 3; }
  .lane-val { order: 2; }
  .lane-group { padding-top: .7rem; }
}
.lane-key { display: flex; flex-wrap: wrap; gap: 1rem; font-family: var(--ui); font-size: .72rem; font-weight: 700; color: var(--ink-3); margin-top: .9rem; }
@container (max-width: 30rem) {
  .lane-key { column-gap: .9rem; row-gap: .3rem; font-size: .68rem; margin-top: .7rem; }
}
.lane-key span { display: inline-flex; align-items: center; gap: .4rem; }
.lane-key i { width: 10px; height: 10px; border-radius: 50%; background: var(--good); }
.lane-key i.hollow { background: var(--ground); border: 2px solid var(--ink-3); }

/* ---- shop (instrument 04) ---- */
.shop { display: grid; grid-template-columns: 1fr; gap: 1.3rem; }
@container (min-width: 36rem) { .shop { grid-template-columns: minmax(0, 1fr) 16rem; } }
.aisle { font-family: var(--ui); font-size: .62rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); margin: 0 0 .45rem; }
.shelf { display: flex; flex-wrap: wrap; gap: .45rem; margin-bottom: .9rem; }
.food {
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: .25rem;
  width: 4.6rem; min-height: 4.3rem; padding: .5rem .25rem;
  background: var(--control-face, var(--surface-2)); border: 1px solid var(--control-edge, var(--rule)); border-radius: 10px;
  color: var(--ink-2); font-size: .68rem; font-weight: 700; line-height: 1.2; text-align: center;
}
.food svg { width: 26px; height: 26px; stroke: var(--ink-3); fill: none; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.food:hover { border-color: var(--line-strong, var(--ink-3)); transform: translateY(-2px); }
.food:hover svg { stroke: var(--ink); }
.food[aria-pressed="true"] { background: var(--good-bg); border-color: var(--good); color: var(--good); }
.food[aria-pressed="true"] svg { stroke: var(--good); }
.food .price { display: block; font-size: .6rem; font-weight: 800; color: var(--amber); }
.preset { background: var(--control-face, var(--surface-2)); border: 1px dashed var(--control-edge, var(--rule)); border-radius: 999px; color: var(--ink-2); font-size: .74rem; font-weight: 700; min-height: 44px; padding: 0 .9rem; }
.preset:hover { border-color: var(--good); color: var(--good); }
.presets { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: .9rem; }

.basket { background: var(--surface); border: 1px solid var(--rule); border-radius: 12px; padding: .8rem .85rem; }
.basket-svg { width: 100%; height: auto; display: block; }
.basket-svg .weave { stroke: var(--rule); fill: none; stroke-width: 1.4; }
.basket-svg .pan { fill: var(--ground); }
.basket-svg .rim { stroke: var(--data); fill: none; stroke-width: 2; stroke-linecap: round; }
.basket-svg .item { stroke: var(--good); fill: none; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.basket-svg .item.pricey { stroke: var(--amber); }
.basket-svg text { font-family: var(--ui); font-size: 11px; font-weight: 750; fill: var(--ink-3); }
/* The drawn basket is recognition, not measurement — every item in it is also a
   goal row below it. Once the shop drops to one column it costs 169px above the
   readings it decorates, so it goes and the score line leads. Must sit after the
   display:block above, or that later rule wins on source order. */
@container (max-width: 36rem) {
  .basket-svg { display: none; }
  .basket .score { margin-top: 0; }
}
.score { display: flex; align-items: baseline; gap: .45rem; margin: .6rem 0 .2rem; font-family: var(--ui); }
.score b { font-size: 1.6rem; font-weight: 850; line-height: 1; letter-spacing: -.02em; font-variant-numeric: tabular-nums; color: var(--ink); }
.score b.done { color: var(--good); }
.score span { font-size: .74rem; font-weight: 700; color: var(--ink-3); }
.goal { display: grid; gap: .25rem; padding: .3rem 0; font-family: var(--ui); }
.goal-top { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; font-size: .74rem; }
.goal-name { color: var(--ink-2); font-weight: 650; }
.goal-val { font-variant-numeric: tabular-nums; font-weight: 800; color: var(--ink-3); }
.goal[data-state="met"] .goal-val { color: var(--good); }
.goal[data-state="over"] .goal-val { color: var(--bad); }
.track { position: relative; height: 5px; border-radius: 999px; background: var(--rule); overflow: hidden; }
.goal-fill { position: absolute; inset: 0 auto 0 0; border-radius: 999px; background: var(--ink-3); transition: width .34s cubic-bezier(.22, .9, .3, 1), background .2s ease-out; }
.goal[data-state="met"] .goal-fill { background: var(--good); }
.goal[data-state="over"] .goal-fill { background: var(--bad); }
.budget { margin-top: .7rem; padding-top: .6rem; border-top: 1px solid var(--rule); }
.budget-track { position: relative; height: 7px; border-radius: 999px; background: var(--rule); overflow: hidden; }
.budget-fill { position: absolute; inset: 0 auto 0 0; border-radius: 999px; background: var(--hold); transition: width .34s cubic-bezier(.22, .9, .3, 1), background .2s ease-out; }
.budget-fill.over { background: var(--amber); }
.budget-mark { position: absolute; top: -3px; bottom: -3px; left: 62%; width: 2px; background: var(--ink-3); opacity: .55; }
.budget-note { display: block; font-family: var(--ui); font-size: .64rem; color: var(--ink-3); margin-top: .35rem; }
.verdict { margin-top: .7rem; border-radius: 999px; padding: .5rem .7rem; text-align: center; font-family: var(--ui); font-size: .74rem; font-weight: 800; }
.verdict[data-state="ok"] { background: var(--good-bg); color: var(--good); }
.verdict[data-state="part"] { background: var(--accent-soft); color: var(--amber); }
.verdict[data-state="no"] { background: var(--flat-bg); color: var(--flat); }
.ghost { width: 100%; background: transparent; border: 1px solid var(--control-edge, var(--rule)); border-radius: 8px; color: var(--ink-3); font-size: .74rem; font-weight: 700; min-height: 44px; margin-top: .6rem; }
.ghost:hover { border-color: var(--ink-3); color: var(--ink); }

/* ---- results table ---- */
/* A wide table scrolls inside its own box, never the page. The two gradients
   are painted on the scroller: the fixed pair sits still while the local pair
   scrolls away with the content, so a shadow appears on whichever edge still
   has table hidden behind it. Pure CSS — the affordance survives a dead script. */
.table-scroll {
  overflow-x: auto;
  background:
    linear-gradient(to right, var(--surface-2) 30%, transparent) left center / 2.5rem 100% no-repeat,
    linear-gradient(to left, var(--surface-2) 30%, transparent) right center / 2.5rem 100% no-repeat,
    radial-gradient(farthest-side at 0 50%, rgba(20, 25, 26, .14), transparent) left center / .8rem 100% no-repeat,
    radial-gradient(farthest-side at 100% 50%, rgba(20, 25, 26, .14), transparent) right center / .8rem 100% no-repeat;
  background-attachment: local, local, scroll, scroll;
}
.table-scroll thead th { position: sticky; top: 0; background: var(--surface-2); z-index: 1; }
table { border-collapse: collapse; width: 100%; font-size: .92rem; min-width: 34rem; }
th, td { text-align: left; padding: .72rem .7rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
th {
  font-family: var(--mono);
  font-size: .66rem;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 500;
}
td.num { font-family: var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.chip {
  display: inline-block;
  font-family: var(--mono);
  font-size: .64rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  padding: .22rem .5rem;
  border-radius: 2px;
  white-space: nowrap;
}
.chip-good { background: var(--good-bg); color: var(--good); }
.chip-null { background: var(--bad-bg); color: var(--bad); }
.chip-part { background: var(--accent-soft); color: var(--accent); }
.chip-hold { background: var(--hold-bg); color: var(--hold); }
.chip-flat { background: var(--flat-bg); color: var(--flat); }

/* group header inside a results table */
.tbl-group {
  padding-top: 1.5rem;
  font-family: var(--mono);
  font-size: .64rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 500;
  border-bottom: 1px solid var(--rule);
}
tbody tr:first-child .tbl-group { padding-top: .5rem; }
.td-note {
  display: block;
  font-family: var(--mono);
  font-size: .62rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-top: .25rem;
}
td span.num { font-family: var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---- table of contents ----
 * Built by script from the h2s, so it can never drift from the headings it
 * points at. It is navigation, not content: if the script never runs the
 * reader loses a shortcut, not a word of the argument. */
.toc {
  font-family: var(--ui);
  border-left: 2px solid var(--rule);
  padding-left: 1rem;
  margin: 2.4rem 0;
}
.toc-label {
  display: block;
  font-family: var(--mono);
  font-size: .64rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: .7rem;
}
.toc ol { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; counter-reset: toc; }
.toc li { counter-increment: toc; }
.toc a { display: flex; gap: .6rem; font-size: .88rem; line-height: 1.35; color: var(--ink-2); text-decoration: none; }
.toc a::before {
  content: counter(toc, decimal-leading-zero);
  font-family: var(--mono);
  font-size: .68rem;
  color: var(--ink-3);
  padding-top: .15rem;
  flex: 0 0 auto;
}
.toc a:hover { color: var(--ink); }
.toc a[aria-current="true"] { color: var(--accent); font-weight: 600; }
.toc a[aria-current="true"]::before { color: var(--accent); }

/* ---- aside ----
 * An explanatory pull-out that sits beside the argument instead of inside it.
 * Authored in the markup, never generated: it carries prose, and prose that
 * only exists once a script runs is prose a reader can lose. */
.aside {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--accent);
  border-radius: 3px;
  padding: 1.1rem 1.2rem;
  margin: 2rem 0;
}
.aside-label {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: .8rem;
  font-family: var(--mono);
  font-size: .64rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: .7rem;
}
.aside-label em { font-style: normal; color: var(--ink-3); letter-spacing: .06em; }
.aside h4 { font-family: var(--serif); font-size: 1.12rem; font-weight: 400; line-height: 1.3; margin: 0 0 .5rem; }
.aside p { font-size: .95rem; line-height: 1.6; color: var(--ink-2); margin: 0; }
.aside p + p { margin-top: .7rem; }

/* Side furniture clears the WIDEST thing on the page, not the reading column.
   Instruments sit in .col--wide (50rem), so anything parked in a gutter has to
   start outside 25rem from the centre line — measuring against the 34rem prose
   column puts the contents list straight through the middle of a chart. Below
   this width there is no gutter worth the name and both fall back into flow. */
@media (min-width: 90rem) {
  .toc {
    position: fixed;
    top: 6.5rem;
    left: max(1.5rem, calc(50vw - 40rem));
    width: 13rem;
    margin: 0;
    z-index: 2;
  }
  .toc.is-tail { display: none; }

  .aside-anchor { position: relative; }
  /* The anchor's left edge is 17rem left of centre and the wide measure ends
     25rem right of it, so 44rem from the anchor lands 2rem clear of the widest
     instrument — both columns share the same centre line, which makes this
     exact rather than approximate. */
  .aside {
    position: absolute;
    top: 0;
    left: 44rem;
    margin: 0;
    width: min(15rem, calc(50vw - 28.5rem));
    font-size: .92rem;
  }
}
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }
h2[id] { scroll-margin-top: 2rem; }

/* ---- inspector ----
 * A panel inside an instrument that reads out whatever the reader is pointing
 * at. It is a persistent panel rather than a floating tooltip on purpose: a
 * tooltip vanishes the moment you look away from it, which makes comparing two
 * items impossible, and it has nowhere to put a source line. Idle state carries
 * the instruction, so the panel is never a blank box waiting to be discovered. */
.inspect {
  margin-top: 1.1rem;
  padding-top: .9rem;
  border-top: 1px solid var(--rule);
  font-family: var(--ui);
}

/* On a narrow column the panel sits at the foot of an instrument that is five
 * or six screens tall, so pointing at the first lane row put its explanation
 * 1.73 screens below the finger — the reader taps, nothing appears to happen,
 * and the shop's photograph was 515px past the last shelf item. The panel now
 * sticks to the bottom of the viewport while its instrument is on screen, so
 * the readout is always beside the thing being pointed at.
 *
 * sticky rather than fixed, and scoped to the instrument's own container: it
 * cannot outlive the figure it belongs to, needs no scroll listener, and where
 * sticky is unsupported it simply stays in flow where it is now.
 *
 * The panel must be the last child for this to work — sticky positions within
 * the parent's box, and anything after it (the caption) would clip its travel.
 * The caption is therefore lifted out and given to the reader before the panel.
 */
@container (max-width: 36rem) {
  .inspect {
    position: sticky;
    bottom: 0;
    z-index: 3;
    margin-top: .9rem;
    padding: .75rem .9rem .85rem;
    /* Its own face, because it now overlaps the figure it reads from. */
    background: var(--surface-2);
    border: 1px solid var(--rule);
    border-radius: 10px;
    box-shadow: 0 -6px 18px rgba(20, 25, 26, .10);
  }
  /* Idle it is only an instruction, so it should not cover the chart it is
     explaining. Collapsed to a single line until the reader points at something. */
  .inspect[data-idle="true"] {
    box-shadow: none;
    background: transparent;
    border-color: transparent;
    padding-left: 0;
    padding-right: 0;
  }
  .inspect[data-idle="true"] .inspect-bars,
  .inspect[data-idle="true"] .inspect-photo,
  .inspect[data-idle="true"] .inspect-legend { display: none; }
  .inspect[data-idle="true"] .inspect-note {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .inspect { --inspect-shadow: 0 -6px 18px rgba(0, 0, 0, .5); }
}
:root[data-theme="dark"] .inspect { --inspect-shadow: 0 -6px 18px rgba(0, 0, 0, .5); }
@container (max-width: 36rem) {
  .inspect { box-shadow: var(--inspect-shadow, 0 -6px 18px rgba(20, 25, 26, .10)); }
  .inspect[data-idle="true"] { box-shadow: none; }
}
.inspect-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: .6rem;
  margin-bottom: .7rem;
}
.inspect-kicker {
  font-size: .62rem;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.inspect-src { font-size: .66rem; font-weight: 700; color: var(--ink-3); }
.inspect-body { display: grid; gap: 1.1rem; align-items: start; }
@container (min-width: 34rem) { .inspect-body { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
.inspect-name {
  font-family: var(--ui);
  font-size: 1rem;
  font-weight: 800;
  line-height: 1.25;
  color: var(--ink);
  margin: 0;
}
.inspect-note { font-size: .78rem; line-height: 1.5; color: var(--ink-2); margin: .35rem 0 0; }
/* A qualifier line under the name — which trial a row came from, how far a
   goal sits from the balanced design. Provenance belongs in the panel's one
   source line, not restated under every item. */
.inspect-srcrow {
  font-family: var(--mono);
  font-size: .64rem;
  line-height: 1.45;
  color: var(--ink-3);
  margin: .5rem 0 0;
}
.inspect-bars { display: grid; gap: .5rem; }
.inspect-row { display: grid; gap: .22rem; }
.inspect-row-top { display: flex; align-items: baseline; justify-content: space-between; gap: .6rem; font-size: .72rem; }
.inspect-label { color: var(--ink-2); font-weight: 650; }
.inspect-val { font-variant-numeric: tabular-nums; font-weight: 800; color: var(--ink); }
.inspect-track { position: relative; height: 6px; border-radius: 999px; background: var(--rule); overflow: hidden; }
.inspect-fill {
  position: absolute; inset: 0 auto 0 0; border-radius: 999px;
  background: var(--data);
  transition: width .28s cubic-bezier(.22, .9, .3, 1);
}
.inspect-row[data-ref="true"] .inspect-fill { background: var(--ink-3); opacity: .45; }
/* A value past its own cap gets a cut edge, so a full bar never silently means
   "this is the maximum" when it means "this ran off the scale". */
.inspect-row[data-over="true"] .inspect-fill {
  border-radius: 999px 0 0 999px;
  background: linear-gradient(90deg, var(--data) 78%, transparent);
}
.inspect-row[data-missing="true"] .inspect-val { color: var(--ink-3); font-weight: 700; }
.inspect-row[data-missing="true"] .inspect-track { opacity: .45; }
.inspect-legend { font-size: .66rem; line-height: 1.5; color: var(--ink-3); margin: .8rem 0 0; }
.inspect[data-idle="true"] .inspect-bars { opacity: .5; }
.inspect[data-idle="true"] .inspect-name { color: var(--ink-3); }
@media (prefers-reduced-motion: reduce) { .inspect-fill { transition: none; } }

/* Pointing at a row should look like pointing at it. */
.dotplot-svg .dp-hit { fill: transparent; cursor: pointer; }
.dotplot-svg .dp-hit:hover, .dotplot-svg .dp-hit:focus-visible { fill: var(--surface); }
.dotplot-svg .dp-hit:focus-visible { outline: 2px solid var(--data); outline-offset: -2px; }
.lane-row[tabindex]:focus-visible { outline: 2px solid var(--data); outline-offset: 2px; }
.lane-row[data-active="true"] { background: var(--control-face-hover, var(--surface)); }

/* ---- references ---- */
.ref { font-size: .62em; line-height: 0; vertical-align: super; margin-left: .12em; white-space: nowrap; }
.ref a { font-family: var(--mono); text-decoration: none; color: var(--accent); font-variant-numeric: tabular-nums; }
.ref a:hover { text-decoration: underline; }
.sources-note { font-size: .88rem; line-height: 1.55; color: var(--ink-3); margin: 0 0 1.4rem; }
.reflist { margin: 0; padding-left: 1.6rem; display: grid; gap: .8rem; }
.reflist li { font-size: .88rem; line-height: 1.5; color: var(--ink-2); padding-left: .2rem; }
.reflist li::marker { font-family: var(--mono); font-size: .78rem; color: var(--accent); }
.reflist li:target { background: var(--accent-soft); border-radius: 2px; }
.reflist em { font-style: italic; }

/* ---- sources ---- */
.sources { margin-top: 4rem; padding-top: 2rem; border-top: 1px solid var(--rule); }
.sources h3 {
  font-family: var(--mono);
  font-size: .7rem;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 500;
  margin: 0 0 1.2rem;
}
.sources ul { list-style: none; padding: 0; margin: 0; display: grid; gap: .75rem; }
.sources li { font-size: .9rem; line-height: 1.5; color: var(--ink-2); padding-left: 1.1rem; position: relative; }
.sources li::before { content: "—"; position: absolute; left: 0; color: var(--accent); }
.sources b { color: var(--ink); font-weight: 600; }

/* ---- site footer ---- */
.site-footer {
  border-top: 1px solid var(--rule);
  padding: 2.5rem 1.5rem;
  margin-top: 4rem;
  text-align: center;
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
