/* ==========================================================================
   Components
   Cards, connectors and ports follow the 2024 ISM Design Kit: white
   round-rect on a pastel or paper ground, 1px #E8E8E8 outline, #C3C3C3 rules.
   ========================================================================== */

/* Component-layer tokens. The elevation is the product card shadow: barely
   there at rest, so the 1px outline still does the separating. --caution-ink
   used to darken the kit amber for text; the caution value is a graphite now
   and clears 5.9:1 on white unaided, so the two are the same colour and the
   name is kept only because several components ask for it by that name. */
:root {
  --shadow-card: 0 1px 2px rgba(16, 20, 24, 0.04), 0 8px 24px rgba(16, 20, 24, 0.04);
  --caution-ink: var(--caution);
}

/* --- Icons --------------------------------------------------------------
   The set is line art on a 96-unit canvas. Detailed glyphs (senescence,
   hallmarks, clocks, chromatin) turn to noise below 40px; simple glyphs
   (check, warning, enzyme, gene, pill) hold down to 18px. */
.icon { width: 1.4em; height: 1.4em; flex: none; color: inherit; }
.icon--sm { width: 1.15em; height: 1.15em; }
/* Section-head icons. The official set is drawn on a 96-unit grid and holds
   crisp at slide sizes, so it is sized to the heading it sits beside rather
   than to a safe minimum: at 56px the mark read as a bullet next to a 32px
   section title and the drawing inside it — the chromatin coil, the clock
   face — was lost. 76px is close to how large the company runs them on a
   slide and is the smallest size at which the detailed marks stay legible. */
.icon--lg { width: 76px; height: 76px; }
.icon--xl { width: 5.5rem; height: 5.5rem; }

/* --- Card --------------------------------------------------------------- */
.card {
  background: var(--card);
  border: var(--card-border);
  border-radius: var(--card-radius);
  padding: var(--s-5);
  box-shadow: var(--shadow-card);
}
.card > :last-child { margin-bottom: 0; }
.card__title {
  font-family: var(--font-text);
  font-size: var(--step-0);
  font-weight: 600;
  line-height: var(--lh-snug);
  color: var(--ink-strong);
  margin: 0 0 var(--s-2);
  display: flex; align-items: baseline; gap: var(--s-2);
}
.card__title .icon { align-self: center; color: var(--accent-ink); }
.card p { max-width: none; }
/* The kit has no grey card: a card is white with the outline. The sunk ground
   stays for true wells (zebra, inactive chips), not for cards. */
.card--sunk { background: var(--card); border-color: var(--rule); }

/* Grouping container: pastel tint, no border — the kit's outer box.
   The kit draws this in a brand pastel. Here the brand pastel is the supports
   wash, so a box drawn in it tinted everything inside as evidence in favour,
   whatever was in it. The sunk paper is the same device — a ground a shade
   off the page — without the claim. */
.group-box {
  background: var(--paper-sunk);
  border-radius: 10px;
  padding: var(--s-5);
}
.group-box__label {
  font-family: var(--font-data);
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: var(--s-4);
}

/* Fixed track counts, not auto-fit. Auto-fit lets a set of four cards break
   three-and-one at widths where all four still fit, which reads as a mistake
   rather than as a layout. The step-downs below keep the break deliberate. */
.grid { display: grid; gap: var(--s-4); }
.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* Five cards in three tracks leave a hole where a sixth would go, which reads
   as a missing card rather than as the end of the set. Six half-tracks let the
   last two widen to fill the row instead. */
.grid--tail2 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
.grid--tail2 > * { grid-column: span 2; }
.grid--tail2 > :nth-last-child(-n + 2) { grid-column: span 3; }

@media (max-width: 1000px) {
  .grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 780px) {
  .grid--3, .grid--tail2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid--tail2 > *, .grid--tail2 > :nth-last-child(-n + 2) { grid-column: auto; }
}
@media (max-width: 560px) {
  .grid--2, .grid--3, .grid--4, .grid--tail2 { grid-template-columns: minmax(0, 1fr); }
}
/* A grid of cards that each hold a sentence of quotation cannot take its
   column count from the window. On a 1440px laptop the reading column is
   about 700px once the contents rail and the notes rail are drawn, and six
   source cards laid out four-across put a full quotation into 160px — three
   words a line, which is a column of confetti, not a card. So the count comes
   from the space the grid actually has: as many columns as fit at a width a
   sentence can be read in, and one column when that is all there is room for.
   The min() keeps a card from overflowing a container narrower than 19rem. */
.grid:has(> .stance) {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 19rem), 1fr));
}

/* --- Statistic ----------------------------------------------------------
   Four numbers in a row is a reading device, not a control panel, so the box
   is gone: no fill, no border, no shadow, no radius. Boxing them forced every
   card in the row to the height of the wordiest one, which left two hundred
   pixels of empty white under three of the four in every section that used
   them — the emptiest thing on the page, repeated eleven times. A hairline
   above each column gives the row its structure instead, and it lands at the
   same height in every column whatever the text below does. */
.grid:has(> .card > .stat) { align-items: start; column-gap: var(--s-6); }
.card:has(> .stat) {
  background: transparent;
  border: 0;
  border-top: 1px solid var(--rule);
  border-radius: 0;
  box-shadow: none;
  padding: var(--s-4) 0 0;
}
.stat { display: flex; flex-direction: column; gap: var(--s-1); }
/* Number, label and note line up across the row, whatever the labels do.

   Stacked independently, each column is as tall as its own text, so a label
   that fits one line lifts the grey sentence under it above the sentences
   beside it. Two rows on this page do that: "LOEUF for KAT6A" sits alone on a
   line while its three neighbours wrap, and "of those including KAT6B" does
   the same later. The numbers align, the first line of each label aligns, and
   then the row comes apart nineteen pixels down — which reads as a typesetting
   accident precisely because everything above it is exact.

   Subgrid rather than a minimum height on the label: a minimum height is a
   guess at the longest label, correct until a pack arrives whose label runs to
   three lines, and wrong silently. Subgrid asks the row how tall each part
   actually is. The card is a pass-through so the stat's three parts reach the
   grid two levels up.

   Row gap moves to the grid because the subgridded rows take their gutters
   from it; the space between one row of statistics and the next is drawn by
   each card's own top rule and padding, not by the gap, so nothing collapses.
   Guarded by @supports because a browser without subgrid must keep the flex
   column above rather than inherit a grid it cannot lay out. */
@supports (grid-template-rows: subgrid) {
  .grid:has(> .card > .stat) { row-gap: var(--s-1); }
  .grid:has(> .card > .stat) > .card:has(> .stat) {
    display: grid;
    grid-template-rows: subgrid;
    grid-row: span 3;
    align-self: stretch;
  }
  .grid:has(> .card > .stat) > .card > .stat {
    display: grid;
    grid-template-rows: subgrid;
    grid-row: span 3;
  }
}
/* The kit prints metric accents in the brand green, bold — every number, so
   the colour is the metric voice and carries no direction. Text-safe ink
   variant, because the exact brand value is 3.0:1 on white. */
.stat__value {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-size: var(--step-3);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--accent-ink);
}
/* There were three modifiers here — accent, caution, flag — which used to tint
   numbers differentially, only the "strong" one going green. That read as a
   judgement on each figure (−0.341 green because the review liked it), so the
   tinting came out and the three rules were left restating the base colour.
   Restating it kept the classes resolving to something deliberate, and it also
   kept a field alive in the model that thirteen call sites set and no reader
   could see. Both are gone now; the rule above is the only one. */
.stat__unit { font-size: 0.52em; font-weight: 500; color: var(--ink-3); margin-left: 0.15em; }
/* Sentence case, at reading size. Tracked capitals are for a two-word section
   eyebrow; these labels run to four and five words — "with published clinical
   results", "largest change in either gene" — and set in capitals they wrapped
   to three lines of shouting under every number, which is the visual signature
   of a metrics dashboard and not of a document. */
.stat__label {
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-2);
  line-height: 1.35;
}
.stat__note { font-size: var(--step--2); color: var(--ink-3); }

/* --- Chip --------------------------------------------------------------- */
/* No fill and no radius, on the base and on every modifier below it.
   The tone modifiers lost theirs a while ago and the plain chip kept its grey
   capsule, which meant the pill was still on the page — as the "Note on the
   source" label on eight of the fourteen corrections, and beside a count in a
   dozen table cells — and a rounded tinted capsule is the single most reliable
   tell that a document was generated rather than set. A chip is a short label
   in the data face. That is all it needs to be: the face already distinguishes
   it from the prose around it, and it costs a cell nothing in height. */
.chip {
  display: inline-flex; align-items: center; gap: 0.3em;
  font-family: var(--font-data);
  font-size: var(--step--2);
  line-height: 1;
  padding: 0;
  border-radius: 0;
  background: none;
  color: var(--ink-2);
  font-weight: 500;
  white-space: nowrap;
}
/* Same reasoning as .stance__tone: a tinted pill is the generated-document
   look, and a table with a column of them reads as decoration before it reads
   as data. The tone becomes a small diamond and the word stays in ink, which
   also means a cell holding one is the same height as a cell holding plain
   text and the rows stop jumping. */
.chip--supports,
.chip--opposes,
.chip--caution,
.chip--flag {
  background: transparent;
  color: var(--ink-2);
  padding-left: 0;
  padding-right: 0;
}
.chip--supports::before,
.chip--opposes::before,
.chip--caution::before,
.chip--flag::before {
  content: "";
  width: 7px; height: 7px;
  flex: none;
  background: var(--tone-mark, var(--connector));
  transform: rotate(45deg);
  margin-right: 0.15em;
}
.chip--supports { --tone-mark: var(--supports); }
.chip--opposes  { --tone-mark: var(--opposes); }
.chip--caution  { --tone-mark: var(--caution); }
.chip--flag     { --tone-mark: var(--flag); }
/* The quiet chip: a label that is present but is not the point of the cell.
   It was an outlined capsule and is now simply lighter, which is the same
   demotion done with weight instead of with a box. */
.chip--outline  { color: var(--ink-3); }

/* --- Chips that are not verdicts ----------------------------------------
   The five modifiers above are the tone vocabulary, and a chip is where that
   vocabulary is loudest: it has a fill, so it reads before the word inside it
   does. Four columns in this report reach for those modifiers to draw
   something that is not a verdict at all, and the collisions are visible on
   one screen:

     Confidence  High      drew chip--supports, green
     Severity    High      drew chip--flag,     the darker grey
     Grade       Moderate  drew chip--caution,  taupe
     Confidence  Moderate  drew chip--caution,  hollow

   Same word, two colours, two tables, one screenful — and the reader has no
   way to know that one scale runs high-is-good and the other high-is-bad,
   nor that the last two lines above are the same scale printed twice.

   The second line read "claret" until the flag tone was taken off the wine it
   used to be and put on the darker of the two greys, which is a change made
   two files away for reasons that had nothing to do with this table. A comment
   that names a colour outlives the colour. This one now names the ramp step,
   which is the thing the rule below actually depends on.

   The renderer does not know it is drawing two scales; it is handed a tone per
   cell and it draws it. What it does emit is the column name, in the cell's
   data-label, because the narrow-window layout needs it there. That makes the
   column the hook: the same chip class can mean different things in different
   columns, and here it has to.

   Scoping by data-label rather than by a new class is the reason these rules
   sit here and not in the profile. It also means a column renamed in the
   profile silently drops back to the tone colours, which is the safe way for
   this to fail: a chip that has lost its scale looks like the rest of the
   page, rather than looking like a scale it is not. */

/* Confidence and Grade: how sure the source is, or how much its evidence
   carries — not which way either points. Ordered by the weight of the mark
   rather than by hue — solid, then hollow — so it carries no direction,
   survives greyscale, and cannot collide with the severity ramp two tables
   away.

   It used to be ordered by fill weight, and the fills were the pill this file
   has just spent a comment removing. The diamond is already the page's tone
   mark, so making it the scale's mark too costs a reader nothing to learn:
   solid is the stronger reading, hollow the weaker, and the word is printed
   beside it either way.

   Grade joined this rule late and the omission was visible: it grades ten
   indications Strong or Moderate, which is the same strength scale under
   another name, so "Moderate" printed hollow under Confidence and solid
   taupe under Grade — one word, two marks, and neither table said why.

   Severity was the third, and it was left out on purpose, on the reasoning
   that a risk column is a different kind of scale and the fill-weight mark
   should stay clear of it. That reasoning produced the collision it was
   written to avoid. Severity runs High, Moderate, Low to Moderate, Not rated
   — an ordered strength scale by any other name — and drew it in hue, so
   "Moderate" was a filled taupe diamond there while the same word was hollow
   in the two tables above. Three tables, one word, two marks.

   What it loses is the claret on "High", and that is worth losing. Colour on
   this page means direction: green for evidence that supports the case,
   claret against it. A severity is a magnitude, not a direction, and drawing
   a magnitude in the direction vocabulary is what put a risk table's four
   rows in the same claret the page uses for a finding that argues against
   the target. The word is printed beside the mark in every one of these
   cells; the mark's job is to say where the word sits on its scale.

   The four columns are listed together here because they are one scale. */
td[data-label="Confidence"] .chip--supports::before,
td[data-label="Confidence"] .chip--flag::before,
td[data-label="Grade"] .chip--supports::before,
td[data-label="Grade"] .chip--flag::before,
td[data-label="Severity"] .chip--supports::before,
td[data-label="Severity"] .chip--flag::before {
  background: var(--strength-hi-ink);
}
td[data-label="Confidence"] .chip--caution::before,
td[data-label="Confidence"] .chip--opposes::before,
td[data-label="Grade"] .chip--caution::before,
td[data-label="Grade"] .chip--opposes::before,
td[data-label="Severity"] .chip--caution::before,
td[data-label="Severity"] .chip--opposes::before {
  background: none;
  box-shadow: inset 0 0 0 1.5px var(--strength-mid-ink);
}
td[data-label="Confidence"] .chip,
td[data-label="Grade"] .chip,
td[data-label="Severity"] .chip { color: var(--ink); }

/* Stage: how far a programme has got. Phase 3 is further than Phase 1; it is
   not a better result, and in this table it sits four columns from a Go/No-go
   call drawn in the same green. The mark is a square rather than a diamond and
   it takes the deep azure of the Phase 3 bar in the figure above, which is
   what ties the column to the figure. A square and a diamond are the same
   shape at two rotations, so the two scales stay distinguishable in greyscale
   and neither reads as a badge. */
td[data-label="Stage"] .chip--supports::before {
  background: var(--stage-ink);
  transform: none;
}
td[data-label="Stage"] .chip { color: var(--ink); }

.chip-row { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-bottom: var(--s-4); }

/* --- Verdict ------------------------------------------------------------
   One per page at most. Carries the judgement and the reason it is hedged. */
.verdict {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--s-5);
  align-items: start;
  background: var(--card);
  border: var(--card-border);
  /* The fallback is grey, not the accent. A verdict block with no tone set is
     one whose direction the renderer could not determine, and defaulting that
     to green would have the page endorse a conclusion it has not reached. */
  border-left: 4px solid var(--verdict-color, var(--connector));
  border-radius: var(--card-radius);
  padding: var(--s-6);
  margin: var(--s-6) 0;
  box-shadow: var(--shadow-card);
}
.verdict__mark { color: var(--verdict-color, var(--connector)); }
/* The question the verdict answers, set as a question rather than as a label:
   sentence case, quiet ink, above the answer it belongs to. */
.verdict__q {
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--ink-3);
  margin: 0 0 var(--s-2);
  max-width: 52ch;
}
.verdict__head {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 800;
  line-height: var(--lh-snug);
  letter-spacing: -0.01em;
  color: var(--ink-strong);
  margin: 0 0 var(--s-3);
  max-width: 34ch;
}
.verdict p { color: var(--ink-2); max-width: 62ch; }
.verdict--supports { --verdict-color: var(--supports); }
.verdict--caution  { --verdict-color: var(--caution); }
.verdict--opposes  { --verdict-color: var(--opposes); }
.verdict--flag     { --verdict-color: var(--flag); }
@media (max-width: 640px) { .verdict { grid-template-columns: minmax(0, 1fr); padding: var(--s-5); } }

/* --- Note ---------------------------------------------------------------
   An aside, not an alert. A filled panel with an all-caps coloured heading
   reads as a warning banner, and a reader who meets four of them stops
   reading any of them. So: no fill, a hairline rule down the left, and a
   label set in ordinary sentence case at reading weight. Direction is still
   never carried by colour alone — every note opens with a word ("Supports",
   "Cuts against", "Caution") and the rule only repeats it. */
.note {
  background: transparent;
  border-left: 2px solid var(--note-color, var(--connector));
  border-radius: 0;
  padding: 0 0 0 var(--s-5);
  margin: var(--s-6) 0;
  font-size: var(--step--1);
  line-height: 1.6;
  color: var(--ink);
  max-width: var(--measure);
}
.note > :last-child { margin-bottom: 0; }
.note__label {
  display: block;
  font-family: var(--font-text);
  font-size: var(--step--1);
  font-weight: 650;
  letter-spacing: 0;
  text-transform: none;
  /* The 2px rule is a mark and takes the exact tone; the label is text and
     takes an ink that carries words on white. Every tone names an explicit
     ink below, and anything without one falls back to --ink-2, so no label
     can inherit a mark colour and end up at 2:1 against its own ground. */
  color: var(--note-ink, var(--ink-2));
  margin-bottom: var(--s-2);
}
/* A pointer to the section that carries the point in full. Set smaller and
   quieter than the note it closes, because it is a way out of the note rather
   than more of it. */
.note__see {
  margin-top: var(--s-3);
  font-size: var(--step--2);
  color: var(--ink-3);
}
.note__see a { color: var(--link); text-underline-offset: 0.2em; }
/* Setting the heading in the tone colour put a claret or amber sentence at the
   top of every aside, and a page carrying nine of them reads as a page of
   warnings. So the heading stays ink and the rule at the left edge carries the
   tone — but only where the tone is also named, which the renderer decides.

   This used to read "the tone lives in the rule down the left and nowhere
   else", and that was the defect rather than the design. It held only while a
   note kept its default heading and printed the word "Caution" above the rule
   repeating it. Every note on the KAT6 page gives its own heading instead,
   because "One listed structure is not KAT6A" tells the reader more than
   "Flag" does — so the word was never printed, and the rule was left encoding
   a severity in warm grey against near-black against grey, with no key on the
   page and no way to decode it. A reader cannot be asked to learn a legend
   that was never shown. Colour reinforces a stated label here; it does not
   replace one, and a note whose tone goes unnamed takes the neutral rule
   below. */
.note--supports { --note-color: var(--supports); --note-ink: var(--ink-strong); }
.note--opposes  { --note-color: var(--opposes);  --note-ink: var(--ink-strong); }
.note--caution  { --note-color: var(--caution);  --note-ink: var(--ink-strong); }
.note--flag     { --note-color: var(--flag);     --note-ink: var(--ink-strong); }
/* Method notes carry no direction: they say how a number was produced. The
   rule stays connector grey; the label takes readable ink via the fallback. */
.note--method   { --note-color: var(--connector); }
/* A note whose tone the reader is never told. Same rule, same weight, no hue:
   it marks an aside, which is the one thing the rule can say without a key. */
.note--plain    { --note-color: var(--connector); }

/* Words reproduced from a source report, set inside quotation marks.

   The class is on every element the checker needs to find, which is mostly
   table cells — 273 of the 296 on this page — and a quoted table is not a
   thing. Only prose in an aside takes the marks, and only in a list: a note
   whose body is a single reproduced sentence already carries them in the data,
   because the profile that writes it concatenates them itself.

   Without this, reproduced prose is set identically to the page's own, and two
   passages read as faults that they are not. One is an editorial cut — the
   evaluation names the same eight measures twice and the second list is
   elided — which unmarked reads as a sentence the pipeline truncated. The
   other names a tool and an environment ("raw logFC unavailable in this
   environment"), which unmarked reads as the page narrating its own build,
   and quoted reads as what it is: the evaluation stating a limit on its own
   ranking, in the vocabulary it used. Neither wants rewriting. Both want
   attributing, and the marks do it without touching a character of the text
   the verbatim check compares against. */
.note li.verbatim { quotes: "\201C" "\201D" "\2018" "\2019"; }
.note li.verbatim::before { content: open-quote; }
.note li.verbatim::after  { content: close-quote; }

/* --- Provenance ---------------------------------------------------------
   Every claim carries the document and page it came from. In the margin
   column at full width; folded into a small inline mark when narrow. */
.marginalia .prov {
  border-top: 1px solid var(--rule);
  padding-top: var(--s-3);
  margin-bottom: var(--s-5);
  font-size: var(--step--2);
  line-height: 1.45;
  color: var(--ink-3);
}
.prov__doc { display: block; font-weight: 500; color: var(--ink-2); }
/* The locator is grey and a shade small wherever it appears. What it is not,
   outside the margin column, is monospaced.

   The mono face was applied to every locator on the reasoning that a locator is
   an identifier. It is the right reasoning about the wrong strings. The margin
   column's locator is a filename — `kat6_longevityclaw_assessment.html` — which
   is a literal a reader could type, and mono is what says so. Every other
   locator on this page is a document title and a place in it: "Longevity
   evidence review, page 5". Four English words and a number. Set in monospace
   they stop being a citation and start looking like a variable name, and
   because a monospaced string is around forty per cent wider than the same
   words in the text face — "Longevity evidence review, page 10" measured 423
   pixels against something near 300 — the citation ended up the widest and
   loudest thing in its own footnote.

   The worst of it was under the confidence table, where the source line reads
   "Statements and estimates as written in the LongevityClaw assessment." The
   mono locator that follows begins with the same three words, could not fit
   beside the sentence at 391 pixels, and wrapped, so the reader met the phrase
   twice, one line under the other, the second time in a different typeface and
   heavier than the first. Nothing was gained by the face that is not carried by
   the grey, the smaller size and the position. */
.prov__where { color: var(--ink-3); }
.marginalia .prov__where { font-family: var(--font-mono); font-size: 0.95em; }

.cite {
  font-family: var(--font-data);
  font-size: 0.72em;
  vertical-align: 0.35em;
  color: var(--link);
  text-decoration: none;
  padding: 0 0.1em;
  cursor: help;
}
.cite:hover { text-decoration: underline; }

.source-line {
  font-size: var(--step--2);
  font-style: italic;
  color: var(--ink-3);
  margin-top: var(--s-3);
  text-align: left;
}
/* The sentence explaining a table is italic; the citation closing it is not.
   Fifteen of the nineteen locators on this page follow a sentence in the same
   line, and with the mono face gone they need some seam or the line runs on
   past its own full stop as one uninterrupted phrase. Roman against italic is
   the seam every printed caption has used for a century, it costs no colour
   and no size, and it does not introduce a third typeface to say a thing the
   two already present can say. */
.source-line .prov__where { font-style: normal; }

/* An identifier the source printed, carrying the address it resolves to.
   Deliberately quieter than a link: there are twenty-odd of them and half sit
   in one table, and drawing each in accent blue with a full underline would
   turn that column into the loudest thing on the page. A dotted rule is enough
   to say a thing is reachable, and the colour arrives on hover, when the
   reader has already decided to follow it. */
.ident {
  font-family: var(--font-data);
  color: inherit;
  text-decoration: none;
  border-bottom: 1px dotted var(--ink-4);
  white-space: nowrap;
}
.ident:hover,
.ident:focus-visible { color: var(--link); border-bottom-color: var(--link); }

/* --- Correction ---------------------------------------------------------
   Used where a source document is wrong or internally inconsistent and we
   have departed from it. Never silent.

   Drawn as a row in a list, not as a panel. One of these is a boxed warning;
   fourteen of them stacked — which is what this report carries — was fourteen
   cream boxes with dashed amber borders down a single column, identical to
   each other and to nothing else on the page, and the section read as an
   error log. The correction is not an error. It is an editorial note, and the
   page is more trustworthy for carrying fourteen of them than for carrying
   none, so the treatment should not look like fourteen alarms.

   What it looks like now: a hairline above each entry, the claim in ink, the
   detail folded away behind it. That is the same device the references and
   the glossary use, which is right — these are all back matter, and back
   matter earns its authority by being plain. The fold still opens, and an
   opened entry keeps its indent so a reader can see where it starts and ends
   without a border being drawn round it. */
.correction {
  border: 0;
  border-top: 1px solid var(--rule);
  background: none;
  border-radius: 0;
  padding: var(--s-4) 0;
  margin: 0;
  font-size: var(--step--1);
  color: var(--ink-2);
}
.correction summary {
  cursor: pointer;
  color: var(--ink-2);
  display: flex; align-items: flex-start; gap: var(--s-2);
  list-style: none;
}
.correction summary:hover .correction__what { color: var(--link); }
.correction summary .icon { flex: none; margin-top: 0.15em; }
/* The claim itself, so a closed panel says which one it is.
   Stacked, not wrapped. This was a wrapping flex row, which put the claim
   beside its label when the claim was short enough to fit the rest of the line
   and under it when it was not — so of fifteen entries four began at x=340 and
   eleven at x=470, and which one a reader got was decided by the length of a
   sentence. A list of findings whose left edge moves is read as a list that was
   not laid out, and the reader is looking at this section precisely to judge
   how carefully the page was made.
   One column rather than a fixed label column: the label is the shorter of the
   two and reserving a gutter wide enough for "Note on the source" would take
   140px off the measure of every claim, in the one section where the claims run
   long. Above and below costs four lines across the whole list and keeps every
   claim at the full width of the column, which is also how the section's own
   eyebrow and heading are set. */
.correction__line {
  flex: 1 1 auto; min-width: 0;
  display: grid; gap: var(--s-2); justify-items: start;
}
.correction__what { color: var(--ink); font-weight: 500; }
.correction summary::-webkit-details-marker { display: none; }
.correction summary::after {
  content: "";
  flex: none;
  width: 6px; height: 6px; margin-top: 0.35em;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--dur-2) var(--ease-out);
}
.correction[open] summary::after { transform: rotate(225deg) translate(-2px, -2px); }
.correction[open] summary { margin-bottom: var(--s-3); }
.correction dl { margin: 0; display: grid; grid-template-columns: auto minmax(0,1fr); gap: var(--s-2) var(--s-4); }
.correction dt {
  font-family: var(--font-data); font-size: var(--step--2);
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-3);
  padding-top: 0.15em;
}
.correction dd { margin: 0; }

/* --- Stance (one source's position) -------------------------------------
   The house card, plain: white, outline, soft shadow. The tone used to be a
   coloured top strip plus a headline sentence set in the tone colour, and a
   row of four read as a traffic light — full paragraphs of amber, green and
   claret prose. Running text is never set in a tone colour now: the sentence
   carries its judgement in words, in ink. The tone survives as exactly one
   compact labelled chip at the top of the card, drawn by ::before from the
   modifier, so the word does the work and the wash only agrees with it. */
.stance {
  background: var(--card);
  border: var(--card-border);
  border-radius: var(--card-radius);
  padding: var(--s-4);
  display: flex; flex-direction: column; gap: var(--s-2);
  box-shadow: var(--shadow-card);
}
/* The tone is one word from the renderer — Supports, Opposes, Caution, Not
   addressed — and it is set as text, not as a filled pill. A rounded badge
   with a tinted fill is the house style of every generated document on the
   internet, and four of them in a row is the first thing a reader recognises
   as machine-made. So: no fill, no border, no radius. A 7px diamond in the
   tone leads the word, because the diamond is the company's own mark and
   because a reader who cannot separate the four hues still has the word. */
.stance__tone {
  align-self: flex-start;
  display: inline-flex; align-items: center; gap: 0.5em;
  font-size: var(--step--1);
  font-weight: 600;
  line-height: 1;
  color: var(--ink-2);
}
.stance__tone::before {
  content: "";
  width: 7px; height: 7px;
  flex: none;
  background: var(--tone-mark, var(--connector));
  transform: rotate(45deg);
}
.stance__tone--supports { --tone-mark: var(--supports); }
.stance__tone--opposes  { --tone-mark: var(--opposes); }
.stance__tone--caution  { --tone-mark: var(--caution); }
.stance__tone--silent   { --tone-mark: var(--connector); color: var(--ink-3); }
.stance__src {
  font-family: var(--font-data);
  font-size: var(--step--2);
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.stance__pos {
  font-weight: 650; font-size: var(--step-0);
  color: var(--ink-strong);
  line-height: var(--lh-snug);
}
.stance__body { font-size: var(--step--1); color: var(--ink); line-height: 1.5; margin: 0; }
/* The card modifiers keep their tone hooks for anything that still reads
   them; nothing in the card body takes a tone colour any more. */
.stance--supports { --stance-color: var(--supports); }
.stance--opposes  { --stance-color: var(--opposes); }
.stance--caution  { --stance-color: var(--caution); }
.stance--silent   { --stance-color: var(--connector); }
.stance--silent .stance__pos { color: var(--ink-3); font-weight: 500; }

/* --- Disagreement axis -------------------------------------------------- */
.axis {
  border: var(--card-border);
  border-radius: var(--card-radius);
  background: var(--card);
  margin-bottom: var(--s-4);
  overflow: hidden;
  box-shadow: var(--shadow-card);
}
.axis__head {
  display: grid;
  grid-template-columns: minmax(0,1fr) auto;
  gap: var(--s-4);
  align-items: center;
  width: 100%;
  text-align: left;
  padding: var(--s-4) var(--s-5);
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  transition: background var(--dur-1) var(--ease-out);
}
.axis__head:hover { background: var(--paper-sunk); }
.axis__q {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 600;
  line-height: var(--lh-snug);
  letter-spacing: -0.008em;
  color: var(--ink-strong);
  margin: 0 0 var(--s-2);
}
/* One pip per report in the argument, all one grey, with the count spelled out
   beside them. The four tone colours came off these: see the note in the
   renderer, which is where the reasoning belongs. */
.axis__spread { display: flex; gap: 3px; align-items: center; flex: none; }
.axis__pip {
  width: 18px; height: 5px; border-radius: 2px;
  background: var(--connector);
}
.axis__n {
  font-family: var(--font-data);
  font-size: var(--step--2);
  color: var(--ink-3);
  margin-left: var(--s-2);
}
/* On a phone the pips go under the question rather than beside it. The right
   column is `auto`, so it takes what the pips and their count need — a hundred
   and twenty-three pixels, measured — and the question is left with whatever is
   over. In a 356-pixel card that is a hundred and seventy, and a question set
   at the display size in a hundred and seventy pixels is not a heading any
   more: "Is inducing senescence the right direction for an aging drug?" came
   out over six lines, each of three or four words, beside a strip of grey
   dashes and an empty half-column of air. The count is the smaller thing on
   the card and it was taking the larger share of the width.
   Stacked, the question has the full three hundred and looks like the heading
   it is, and the pips read as what they are: a footnote to it. This is what
   `.verdict` and `.glossary__row` do a few hundred lines either side of here,
   for the same reason and at the same width. */
@media (max-width: 640px) {
  .axis__head { grid-template-columns: minmax(0, 1fr); gap: var(--s-3); }
}
.axis__body { padding: 0 var(--s-5) var(--s-5); }
.axis__body[hidden] { display: none; }
.axis__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--s-3); }
.axis__read { margin-top: var(--s-4); }

/* --- Tables ------------------------------------------------------------- */
.table-wrap { overflow-x: auto; margin: var(--s-5) 0; border: var(--card-border); border-radius: var(--card-radius); background: var(--card); box-shadow: var(--shadow-card); }
table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--step--1);
}
/* Tabular figures belong to the columns of figures, and to nothing else.
   Set on the table they were inherited by every prose cell as well, and in
   Inter the `tnum` feature does not only widen the digits: it swaps the
   hyphen-minus for a tabular one a full digit wide, so that a column of
   negative numbers lines up under a column of positive ones. In a sentence
   that hyphen is a hyphen, and every compound in every table on the page
   was drawn with a digit's worth of air on each side of it — "senescence -
   associated", "non - embryonic", "peer - reviewed". Nineteen tables, and
   the words with the most to say were the worst hit, because a hyphenated
   compound is where a technical noun goes.

   `.n` is the class the renderer already puts on a cell it declared numeric
   or right-aligned, which is the same set of cells the alignment was for.

   `tbody` for the same reason, one level down. The class went onto the
   headings too when they were given their column's alignment, and a heading
   is not a figure: "KAT6A" and "KAT6B" are words that happen to contain a
   digit, and tabular figures widened that digit for nothing. Four headings
   over one table, five pixels in total, and at 960 pixels wide that was the
   five pixels by which the widest table on the page overflowed the panel
   holding it. A column of figures is what wants its digits on a common
   width; the word standing over the column wants to be as narrow as the
   font can set it. */
table.data tbody .n { font-variant-numeric: tabular-nums; }
/* The reserved fraction field. The renderer sizes it in `ch`, one per missing
   digit, which is the right unit only because the figures above are tabular:
   every digit in these cells has the width of a nought, so a nought's width is
   what a missing digit takes up.

   Empty and out of the way of everything else. An inline-block with no content
   has no line boxes in it, so its baseline is its own bottom edge and it sits
   on the text's baseline contributing no height — the row is the height it was
   before the field existed. It holds no text, so it changes no cell's reading,
   no sort key, and nothing a search matches against. */
table.data .dec-pad { display: inline-block; }
table.data caption {
  caption-side: top;
  text-align: left;
  /* The side padding is the cell gutter, not a step of its own. It was
     `--s-5` against the cells' `--s-4`, so the sentence introducing the table
     began eight pixels right of the column it introduces — far enough to read
     as an indent nobody chose, and it moved independently of the cells at
     every width, because the gutter narrows on a dense table and below
     1180px and the caption did not. */
  padding: var(--s-4) var(--cell-x) 0;
  font-size: var(--step--1);
  color: var(--ink-3);
}
/* Cell gutters are held in a variable rather than set on the cells, because
   two things vary them independently: how wide the window is, and whether the
   table has declared itself dense. Setting them directly took one rule per
   combination and made the narrow-window rules fight the dense one on
   specificity; naming the step lets each answer its own question. */
table.data { --cell-x: var(--s-4); }
table.data th, table.data td { padding: 0.55rem var(--cell-x); text-align: left; vertical-align: baseline; }
/* The first cell of every body row is a `th scope="row"`, so that a screen
   reader announces the name of the thing before each of its measurements. That
   is a change of element, not a change of design: the cell still has to look
   like the ordinary cell it was. The browser's own stylesheet makes a `th`
   bold, so without this line nineteen tables would grow a bold first column
   overnight — and bold already means something in these tables: it is what the
   stacked phone layout below uses to mark the record's heading, and a weight
   that appears everywhere marks nothing.
   Only the weight is restated. The centring a browser also gives a `th` is
   already overridden by the rule above.

   This rule was in place and nine of the page's twenty-five tables were bold
   in the first column anyway, because nine authoring sites wrapped the value
   in `**` before it ever reached a stylesheet. All-or-nothing per table, and
   no principle separated the nine from the sixteen: disease names and PDB
   identifiers — short labels, the case bold would be for — were among the
   plain ones. A weight that half the tables carry and half do not tells a
   reader nothing except that more than one person wrote the page.

   The `**` is gone from all nine. What it was for is already said twice over:
   the cell is the leftmost in its row and it is a `th`, which is what the
   stacked phone layout below reads to find the record's heading — and that
   layout is the one place on the page where this cell does go bold, because
   there it has lost the column that was saying so. */
table.data tbody th { font-weight: 400; }
/* Not sticky, and the reason is worth writing down because the rule that was
   here looked right and had never once worked.

   It read `position: sticky; top: 56px; z-index: 2`, written so a long table's
   column headings would hold under the 56-pixel masthead. But a sticky box
   offsets against its nearest scroll container, and `.table-wrap` above sets
   `overflow-x: auto` — which makes the computed `overflow-y` `auto` as well, so
   the panel, not the page, was the scrollport. The panel has no height limit
   and never scrolls, so the headings never stuck to anything. What the offset
   did instead was displace the heading cell 17.6 pixels down inside its own
   panel, where its opaque `--card` background painted over the top of the first
   record: the ascenders of row one were shaved flat, and the two-pixel rule
   under the headings — painted by the table, because the borders are collapsed
   — was covered by the cell that had moved off it. Seventeen of this page's
   twenty-five tables were drawn that way.

   There is no version of this that works here. Getting page-level stick back
   means the panel must not be a scroll container, and the panel scrolls because
   a wide table has to be reachable at a narrow width. `top: 0` would stop the
   damage but leave a declaration that does nothing, which is worse than no
   declaration. So the headings scroll with their table, as they do on paper —
   `print.css` had been switching this off for print since it was written, which
   was the first sign it was a screen affectation rather than a reading aid. */
table.data thead th {
  background: var(--card);
  font-family: var(--font-data);
  font-size: var(--step--2);
  font-weight: 650;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-strong);
  border-bottom: 2px solid var(--ink-strong);
  /* Headings wrap. Holding them on one line makes a long heading push the
     table wider than the panel it sits in, and the overflow is a horizontal
     scroll nobody sees: the last column simply reads as a cut-off word. */
  white-space: normal;
  hyphens: none;
}
/* A numeric column's heading sits over its figures. The body cells already do
   this; the heading is the other half of the same rule and was missing, so the
   two halves of every numeric column were aligned to opposite edges of it.
   `td.n` further down also sets `nowrap`, and that half deliberately does not
   come with it: a heading is words and has to be allowed to wrap, which is what
   the rule above says. Right-aligned, a wrapped heading stacks its lines
   against the same edge the numbers stand on, so the last line ends where the
   figures end and the sort marker hanging off it lands there too. */
table.data thead th.n { text-align: right; }
/* A table carrying more columns than a comfortable gutter leaves room for.
   Four columns of figures beside two of text overflow the panel at every
   width on the default step, and the overflow is invisible: the panel scrolls
   and the last column reads as a cut-off word. Declared by the table rather
   than counted here, because how much room a set of columns needs depends on
   what is in them, not on how many there are. A dense table always sits one
   step tighter than a plain one beside it, down to the floor below. */
table.data[data-dense] { --cell-x: var(--s-3); }

/* The text column is at its narrowest between about 860 and 1180 pixels, where
   the notes column has gone but the contents rail still takes its width. A
   table with six or seven columns does not fit there on full padding, and the
   overflow is invisible: the panel scrolls and the last column reads as a
   cut-off word. Tightening the gutters costs less than losing a column. */
@media (max-width: 1180px) {
  table.data { --cell-x: var(--s-3); }
  table.data[data-dense] { --cell-x: var(--s-2); }
}
/* The floor. Below this the table has already broken into one record per row,
   so there is no width left to buy and a dense table gains nothing by being
   tighter than its neighbours. */
@media (max-width: 720px) {
  table.data, table.data[data-dense] { --cell-x: var(--s-2); }
}

/* On a phone a table of six or seven columns has two bad options: shrink the
   type until it cannot be read, or scroll sideways, which hides the columns on
   the right and gives no sign they are there. So below this width the table
   stops being a grid. Each row becomes one record, and each cell becomes a line
   with its column name beside it, carried in the cell by the renderer. Sorting
   and filtering still work, because this changes how the rows are drawn and not
   which rows exist or what order they are in. */
@media (max-width: 640px) {
  /* The header row goes rather than being hidden off-screen. Every cell now
     states its own column name, so a screen reader that still had the header
     would announce each name twice. */
  table.data thead { display: none; }
  /* The caption is on this list because it was left off it. `display: table-
     caption` needs a table to be the caption of, and the line above has just
     taken the table away; with no table box in the ancestry the browser makes an
     anonymous one around the caption alone, and an anonymous table shrink-wraps
     its contents. Measured at 600px, the sentence "Every disease the report's
     tier tables name, with the page it was read from." was drawn 69 pixels wide
     and 83 tall — one word to a line, in a column beside a table that spans the
     screen. Twenty-five tables, twenty-five of these. */
  table.data, table.data caption, table.data tbody, table.data tr, table.data td, table.data tbody th { display: block; width: auto; }
  /* `display: block` on the row, three lines up, is an author declaration and
     the browser's own `[hidden] { display: none }` is not, so this list quietly
     outranked it — origin beats specificity, and no amount of weight on the
     browser's rule would have changed that. The effect was that filtering did
     nothing to a table on a phone: measured at 390px with one facet pressed,
     all 29 rows the filter had just hidden were still on the screen, under a
     counter reading "9 of 38 diseases shown". The chart above them narrowed and
     the table below them did not, and the counter agreed with the chart.
     Restated here as an author rule so that it can win. */
  table.data tbody tr[hidden] { display: none; }
  table.data tbody tr { padding: var(--s-3) var(--s-4) var(--s-4); border-top: 1px solid var(--rule); }
  /* The label goes above the value rather than beside it, and the reason is
     that "beside it" was a two-column grid, and a grid makes a grid item out
     of every element in the cell. A cell here is not a value, it is a
     sentence, and the sentences carry markup: `<span class="nobreak">` around
     a citation so the author and the year cannot be split across a line, an
     empty `<span class="dec-pad">` reserving the decimal field, occasionally a
     link. Each of those became an item of its own, was blockified, and took
     the next free cell of the grid — which is the first column of the next
     row, where the column name goes.

     Measured at 600px, one cell of the mechanism table drew like this: the
     paragraph ran to "…and repressing cellular senescence (", then the grid
     started a new row and put "Carapeti et al." in the label column, ", 1998;"
     in the value column, "Kindle et al." in the label column beneath it, and
     so on for seven citations. A cell 83 pixels tall on a laptop was 449
     pixels tall on a phone, the sentence was in fifteen pieces, and seven
     author names were sitting exactly where the reader has learned to look for
     the name of the column. There is no way to opt an element out of being a
     grid item, so the grid had to go.

     A block cell has none of this: the cell's content is inline flow again,
     the citations stay inside their sentences, and the label is a block of its
     own above them. It also suits these labels better than the column did.
     They are phrases, not words — "What the evaluation argues", "Resolution,
     ångström" — and in a 136-pixel column that one wrapped to three lines
     beside the paragraph it introduces. On its own line it is one line, and
     the paragraph gets the full width of the screen instead of two thirds of
     it. */
  table.data td, table.data tbody th { padding: 0.3rem 0; display: block; }
  /* The hanging indent above is for a cell that opens with the tone diamond.
     Down here `::before` carries the column's name instead and the diamond is
     not drawn at all, so the indent would be hanging nothing — and it is set
     on a narrower selector than the rule above, so it would win and push the
     label out of line with every other row. Taken back explicitly. */
  table.data td.supports:not(.n),
  table.data td.opposes:not(.n),
  table.data td.caution:not(.n),
  table.data td.flag:not(.n) { padding-left: 0; text-indent: 0; }
  table.data td::before, table.data tbody th::before {
    content: attr(data-label);
    display: block;
    font-family: var(--font-data);
    font-size: var(--step--2);
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--ink-3);
  }
  /* The first column names the row, so it reads as the record's heading. It is
     matched by element rather than by position now that it is the row's `th`:
     `td:first-child` stopped matching anything the moment the first cell became
     a header cell, and a rule that silently matches nothing is the quietest way
     for a layout to lose its headings. */
  table.data tbody th { font-weight: 600; color: var(--ink-strong); font-size: var(--step-0); margin-bottom: var(--s-2); }
  /* Right-alignment lined a number up with the number below it. In a stack
     there is nothing above or below to line up with, so it reads as a gap.

     `tbody` is in the selector to win an argument with the identical selector
     further down this file, which sets the alignment back to right. Both are
     two classes and two elements, a media query adds nothing to specificity,
     and the other one is written later, so for as long as this rule read
     `table.data td.n` it lost and did nothing at all. What that looked like on a
     phone: "Parkinson's disease" started at the left of its column and "+0.414"
     ended at the right of the screen, four hundred pixels apart, in a stack of
     eight lines that were supposed to read as one record. The extra `tbody` is
     honest as well as sufficient — there is no `thead` down here to match. */
  table.data tbody td.n { text-align: left; white-space: normal; }
  /* The tone marker rides on the first cell in the grid layout, where that
     cell is the leftmost column and the stripe lands on the row's edge. Once
     the row is a stacked block the first cell is a heading partway down it,
     so the stripe moves to the block itself and keeps meaning "this record". */
  table.data tbody tr:has(> td.n.supports) > th,
  table.data tbody tr:has(> td.n.opposes)  > th,
  table.data tbody tr:has(> td.n.caution)  > th,
  table.data tbody tr:has(> td.n.flag)     > th { box-shadow: none; }
  table.data tbody tr:has(> td.n.supports) { border-left: 3px solid var(--supports); }
  table.data tbody tr:has(> td.n.opposes)  { border-left: 3px solid var(--opposes); }
  table.data tbody tr:has(> td.n.caution)  { border-left: 3px solid var(--caution); }
  table.data tbody tr:has(> td.n.flag)     { border-left: 3px solid var(--flag); }
  /* The bar's left margin is `auto` on a laptop, which is what holds it under
     the right-hand edge of the number it annotates in a right-aligned column.
     Nothing is right-aligned down here — the rule above has just taken that
     back — so `auto` would push a 76-pixel bar to the far side of a 534-pixel
     screen, half a screen away from the figure it belongs to and directly
     beneath the next value down. Set to zero it starts where the number
     starts. The note is on the same line for the same reason. */
  table.data td .spark, table.data tbody th .spark { margin-left: 0; }
  table.data td .cell-note, table.data tbody th .cell-note { margin-left: 0; }
}

table.data tbody tr { border-top: 1px solid var(--rule); }
table.data tbody tr:hover { background: var(--paper-sunk); }
/* A number is read as one thing, so it never breaks across lines. The width
   hint on the column is only a hint: the browser widens this column to fit the
   longest value and takes the room back from the prose columns, which wrap. */
table.data td.n { text-align: right; font-family: var(--font-data); white-space: nowrap; }

/* --- Direction is not a verdict -----------------------------------------
   up/down, pos/neg and rises/falls say which way a measurement went. They
   were grouped with supports/opposes and drew the same two colours, so a
   fold change of +0.364 printed green and −0.407 printed violet: the page
   told the reader that a gene going up in cancer is good news and a gene
   going down is bad, which is a claim nobody made and which the surrounding
   prose contradicts in both directions.

   It is also redundant. The sign is already in the glyph — the number starts
   with a minus, and the spark bar beside it sits on the far side of its zero
   axis. Colour was adding nothing except an opinion. */
table.data td.up, table.data td.down,
table.data td.pos, table.data td.neg,
table.data td.rises, table.data td.falls { color: var(--ink); }

/* --- Tone on a cell ------------------------------------------------------
   Tint the text only. A cell that needs a stronger signal than colour gets a
   chip instead, which carries its own background.

   These are for cells whose content is a *word* — "argues for", "argues
   against", "Low agreement" — and the word is now set in ink, with the tone
   moved into a small diamond in front of it. Setting the phrase itself in
   green or claret puts six lines of coloured running text down one column of
   a table, which reads as a status board rather than as a document, and it
   makes the colour compete with the links on the same screen. The diamond is
   the company's own mark, it holds its hue at 7px where a letterform does
   not, and the word still states the direction on its own. */
table.data td.supports:not(.n),
table.data td.opposes:not(.n),
table.data td.caution:not(.n),
table.data td.flag:not(.n) {
  color: var(--ink);
  /* The mark is an inline box, so a phrase that wraps returns to the cell's
     left edge and the second line starts underneath the diamond rather than
     underneath the first word. In the Position column that printed "argues
     for," over "with conditions", with the continuation sitting where the mark
     had been — which reads as a second, unmarked entry. Indenting the block by
     the mark and pulling the first line back by the same amount hangs the mark
     in the margin and puts every following line under the word it continues.

     The width is the mark and the gap it already sets: 7px and 0.5em, written
     twice because one is a padding and the other its negative, and they have
     to stay equal. The `em` resolves against this cell, which is also what the
     mark's own `margin-right` resolves against, so the two cannot drift. */
  --tone-hang: calc(7px + 0.5em);
  padding-left: calc(var(--cell-x) + var(--tone-hang));
  text-indent: calc(-1 * var(--tone-hang));
}
table.data td.supports:not(.n)::before,
table.data td.opposes:not(.n)::before,
table.data td.caution:not(.n)::before,
table.data td.flag:not(.n)::before {
  content: "";
  display: inline-block;
  width: 7px; height: 7px;
  margin-right: 0.5em;
  vertical-align: 0.05em;
  background: var(--tone-mark, var(--connector));
  transform: rotate(45deg);
}
table.data td.supports:not(.n) { --tone-mark: var(--supports); }
table.data td.opposes:not(.n)  { --tone-mark: var(--opposes); }
table.data td.caution:not(.n)  { --tone-mark: var(--caution); }
table.data td.flag:not(.n)     { --tone-mark: var(--flag); }

/* --- Tone on a number ----------------------------------------------------
   The same four classes land on numeric cells in one table, where the eight
   coefficients behind the review's "5 of 8 streams support" count are tinted
   by the direction the review reads into each one. That fails twice over.

   The number takes a colour it did not earn: −0.341 is green because the
   review likes what it implies, +192.76 is amber because the review is
   hedging about it, and −0.098 is violet because it cuts the other way — so
   three numbers of the same kind, in one column, under one heading, print in
   three colours with no key on the screen to say why. A reader who has just
   come from a table where green meant "high confidence" and a figure where
   green meant "KAT6A" has no chance.

   So the number goes back to ink and the tone moves to the edge of its row,
   where it marks the record rather than the measurement. The marker is the
   same device the notes use — a coloured left rule — and it lands on the
   first cell, which is the one that names the row.

   :has() rather than the row's data-facets, because the facets are the filter
   vocabulary and several other tables use the same words on rows that carry
   no toned number and need no stripe. Matching on the toned numeric cell
   itself means the marker appears exactly where the defect was. Browsers
   without :has() simply get no stripe, which is the safe way to lose it. */
table.data tbody tr:has(> td.n.supports) > th { box-shadow: inset 3px 0 0 var(--supports); }
table.data tbody tr:has(> td.n.opposes)  > th { box-shadow: inset 3px 0 0 var(--opposes); }
table.data tbody tr:has(> td.n.caution)  > th { box-shadow: inset 3px 0 0 var(--caution); }
table.data tbody tr:has(> td.n.flag)     > th { box-shadow: inset 3px 0 0 var(--flag); }
table.data td.n.supports,
table.data td.n.opposes,
table.data td.n.caution,
table.data td.n.flag { color: var(--ink); }

table.data .gene { font-family: var(--font-data); font-weight: 500; }

th.sortable { cursor: pointer; user-select: none; }
th.sortable:hover { color: var(--accent-ink); }
/* The marker hangs off the label rather than the cell, so a column that also
   carries a note keeps the two together instead of stranding the arrow below.

   It is drawn rather than typed. It used to be `\2195`, the up-down arrow, and
   that codepoint is not in the Latin subset this page ships: every sortable
   heading was borrowing a glyph from whatever font the reader's machine happens
   to supply, at a weight and a size chosen by nobody, on a page that carries
   its own typeface precisely so that it does not have to guess. What it looked
   like here was a hairline scratch. Three tapering rules are the ordinary mark
   for "this column can be put in order", they are all axis-aligned so they stay
   crisp at seven pixels, and drawing them in `currentColor` means the hover
   state above reaches them without a second copy of the mark.

   Which side it sits on follows the column's alignment, and it hangs in the
   gutter rather than taking room from the words. A numeric heading is set flush
   right over figures that are also flush right, and a mark set after it, in the
   flow, pushed the words ten pixels off the column they name — undoing, on
   every numeric column of every sortable table, exactly the alignment the
   heading was given `.n` for. So it leads on those columns and trails on the
   rest: always on the side away from the edge its column aligns to, which is
   also what keeps it out of the table's outer margin and puts it in the space
   between two columns, where there is room for it. Out of the flow, the heading
   does not move by a pixel either way.

   Out of the flow for a second reason as well. An atomic inline is a place a
   line is allowed to break, and "Tier" and "Page", four letters each in a
   column with room to spare, both fell to two lines with the mark stranded
   alone on the second. Something that contributes no width cannot do that.

   Reserving the space on the label and pinning the mark into it was the first
   way out and it failed on the headings that wrap. An inline-block too long for
   one line is as wide as the space it was offered rather than as wide as its
   longest line, so on "How it was solved" and "Resolution, ångström" the mark
   went to the far edge of the cell and stood in open white space, a full column
   away from the words it belonged to. A plain inline measures differently: the
   box an absolutely positioned child hangs off is the union of the lines the
   inline actually drew, so its edge is the end of the longest line, and the
   mark stays against the words whether the heading took one line or three.

   It sits on the first line because that is the line the header row shares.
   Cells align on a baseline and a plain inline takes the baseline of its first
   line, so "Structure" is level with the "How it was" of the heading beside it
   and not with its "solved", and every mark along the row is at one height.

   And it is painted only under the pointer. Five of these standing permanently
   along a five-column header row is five pieces of furniture in a report that
   is read far more often than it is operated: the reader who never sorts
   anything looks at them in every screenshot, and they say nothing about the
   table's contents. The heading already turns accent green under the pointer,
   which says "this responds"; the mark arriving with it says what it responds
   with. Keyboard focus shows it for the same reason, and the column that is
   actually sorted keeps its arrow whether anyone is pointing at it or not,
   because that one is not an offer but a fact about the order of the rows. */
th.sortable .th-label { position: relative; }
th.sortable .th-label::before {
  content: "";
  position: absolute;
  top: 4px;
  width: 7px; height: 7px;
  opacity: 0;
  transition: opacity 0.12s ease;
  background-image:
    linear-gradient(currentColor, currentColor),
    linear-gradient(currentColor, currentColor),
    linear-gradient(currentColor, currentColor);
  background-size: 7px 1px, 4px 1px, 2px 1px;
  background-repeat: no-repeat;
}
/* How far it hangs is capped at the cell's own gutter, which is what keeps the
   mark inside the cell it belongs to at every width. The gutter is a step of
   the spacing scale and it narrows twice on the way down — sixteen pixels at
   the design width, twelve below 1180, eight below 720 — while a hang written
   as a flat number does not narrow with it. Twelve pixels of hang against
   eight pixels of padding is four pixels of the mark standing outside its own
   cell, which is a decoration in the next column's space and, on the last
   column, past the edge of the table. The build measures the page at 680
   pixels and reads exactly that: four pixels.

   At the width the report is designed for the cap does nothing — sixteen is
   wider than either hang — so this changes nothing about how the header rows
   below have been checked and everything about how they hold when the window
   is small.

   The taper points away from the heading: longest rule nearest the words. */
th.sortable:not(.n) .th-label::before {
  right: calc(-1 * min(12px, var(--cell-x)));
  background-position: left top, left center, left bottom;
}
/* Two and a half pixels further out than its mirror, and the two then look
   equal. A trailing mark clears the last letter's right side bearing and the
   letter-space the heading's tracking adds after it; a leading one starts hard
   against the first letter's ink with neither in the way. Matching the numbers
   left "RESOLUTION," with its arrow all but touching the R while "STRUCTURE"
   had a comfortable gap after the E. */
th.sortable.n .th-label::before {
  left: calc(-1 * min(14.5px, var(--cell-x)));
  background-position: right top, right center, right bottom;
}
th.sortable[aria-sort="none"]:hover .th-label::before,
th.sortable[aria-sort="none"]:focus-visible .th-label::before { opacity: 0.55; }
@media (prefers-reduced-motion: reduce) {
  th.sortable .th-label::before { transition: none; }
}
/* The sorted-column arrow sits directly above a column of numbers. In the
   supports green it read as a mark on the column rather than as a control the
   reader had just pressed. Full-strength ink says "this one" without saying
   anything about what is in it. These two arrows are in the subset, so the
   sorted state is typed and the resting state is drawn: a neutral mark for a
   column that could be put in order, a definite one for the column that is. */
th[aria-sort="ascending"] .th-label::before,
th[aria-sort="descending"] .th-label::before {
  background-image: none;
  opacity: 1;
  color: var(--ink-strong);
  /* The arrow keeps the same seven pixels of gutter the resting mark had, so
     nothing shifts when a heading is pressed. It is a character rather than a
     drawing, so it is given a line box of the heading's own height, from the
     top of the heading's first line, and lands on that line's baseline at very
     nearly the heading's size — the way a letter of the heading would. */
  top: 0;
  height: 15px;
  line-height: 15px;
  font-size: 0.94em;
  text-align: center;
}
th[aria-sort="ascending"] .th-label::before { content: "\2191"; }
th[aria-sort="descending"] .th-label::before { content: "\2193"; }

/* --- Controls ----------------------------------------------------------- */
.controls {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3);
  padding: var(--s-4) 0;
  margin-bottom: var(--s-3);
  border-bottom: 1px solid var(--rule);
}
/* Both of these wrap. `.controls` already did, but a group is one flex item
   to it, so a group holding six tier buttons overflowed as a unit and pushed
   the document sideways: 161px of it at 320px wide, and at 390px the "Other"
   button sat at x=422, entirely off the right of the screen with no way to
   reach it. Wrapping inside the group and inside the segmented control keeps
   every button on the page at every width. */
.controls__group { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); min-width: 0; }
.controls__label {
  font-family: var(--font-data);
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  /* A gutter, not a word. Set to its own width the label pushed each row of
     tabs to wherever that particular word ended, and when two groups in one
     bar wrap onto separate lines the two rows of tabs no longer share a left
     edge: measured on the indications filter, TIER started its tabs at x=379
     and AREA at x=385, six pixels apart and one under the other. Six pixels is
     too small to look deliberate and too large to look straight — it reads as
     a row that was nudged.

     2.75rem is 44px, which clears the widest label that ever stacks here:
     "Tier" sets 30px, "Area" and "Gene" 36px, "Stage" 44px. A longer label —
     "With age" sets 68px — simply takes the room it needs, and the rows it
     shares a bar with are beside it rather than beneath it, so nothing is out
     of line. This only has to be wide enough to be a column; it does not have
     to be wide enough for every label the kit will ever be given. */
  min-width: 2.75rem;
}
/* The filters. They were rounded pills — a white capsule each, and the
   selected one filled with the brand wash and outlined in brand green.

   The shape is the problem before the colour is. A filled, rounded, tinted
   capsule is the badge shape: on this page it is what a status marker looks
   like, and the page deliberately does not use it even for status, because a
   tinted capsule reads as a label the document is applying to something rather
   than as a control the reader is holding. Five of them in a row above a chart,
   one of them green, and the row reads as findings — "All tiers" looks like an
   assertion about the chart rather than the button that is currently down.

   The colour compounded it: brand green filling the selected control is the
   same green this page uses for evidence that supports something, so pressing
   a filter looked faintly like agreeing with it.

   What replaces it is the device the page already owns. The contents rail
   marks the section you are in with `color: var(--ink-strong)` and a 2px brand
   rule along its edge, and nothing else — no fill, no capsule. These are the
   same statement about the same kind of thing, so they are drawn the same way,
   turned through ninety degrees: every item sits on a hairline track, and the
   selected one's hairline thickens into the brand rule under it. That is a tab
   row, which is what this control has always been.

   Three details that are not decoration:

   The track is drawn per button rather than on the container, because the row
   wraps. An inset shadow on `.seg` draws one line at the bottom of the whole
   box, so a row that wrapped to two lines got a track under the second and
   nothing under the first. Per button, with `gap: 0`, the hairlines meet and
   read as one line, and each wrapped row keeps its own.

   The weight does not change on press. The rail does not change weight either,
   and here there is a second reason: `font-weight: 600` on the pressed item
   makes that item wider, so every button to its right shifts as the reader
   clicks along the row. The colour step from `--ink-3` to `--ink-strong` and a
   2px rule are the signal, and the rule is a shape, so colour is not carrying
   it alone.

   `gap: 0` also means the buttons touch, which is what puts the padding to
   work as the hit area rather than as air between capsules. */
.seg { display: inline-flex; flex-wrap: wrap; background: transparent; border-radius: 0; padding: 0; gap: 0; }
.seg button {
  font: inherit;
  font-size: var(--step--1);
  padding: 0.3rem 0.7rem 0.42rem;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--ink-3);
  cursor: pointer;
  white-space: nowrap;
  box-shadow: inset 0 -1px 0 var(--rule);
  transition: color var(--dur-1) var(--ease-out), box-shadow var(--dur-1) var(--ease-out);
}
.seg button:hover { color: var(--ink-strong); box-shadow: inset 0 -2px 0 var(--connector); }
.seg button[aria-pressed="true"] {
  color: var(--ink-strong);
  box-shadow: inset 0 -2px 0 var(--accent);
}
/* These used to tint the pressed button with its tier colour, to tie the
   filter to the dots. Two problems. Tier 1 was the supports green, so
   pressing a filter looked like agreeing with it; and once the tier scale
   became an ordered ramp, its lower steps are marks-only values that fail
   contrast as text. The pressed state is now the same ink for every button —
   a control's state, not a claim about a tier — so the classes stay as hooks
   and restate the shared value rather than reintroducing a per-tier colour
   through the back door. */
.seg button[data-tone="1"][aria-pressed="true"],
.seg button[data-tone="2"][aria-pressed="true"],
.seg button[data-tone="3"][aria-pressed="true"] { color: var(--ink-strong); }

.search {
  font: inherit;
  font-size: var(--step--1);
  padding: 0.34rem 0.65rem;
  border: 1px solid var(--rule);
  border-radius: 5px;
  background: var(--card);
  color: var(--ink);
  min-width: 190px;
}
.search::placeholder { color: var(--ink-4); }
.search:focus { border-color: var(--focus); outline: none; box-shadow: 0 0 0 3px var(--link-wash); }

.count { margin-left: auto; font-family: var(--font-data); font-size: var(--step--2); color: var(--ink-3); }

/* The two sentences a filter bar shows when a selection empties what it
   controls. Unstyled they inherit `p`, which is body copy, and body copy
   inside a flex row of chips is the one thing that looks like a mistake:
   the sentence takes whatever gap is left at the end of the chip row, sits
   on the same baseline as the buttons, and reads as a caption someone
   forgot to move. Three things fix that.

   `flex-basis: 100%` breaks them onto their own row under the chips. A
   sentence that has to wrap mid-phrase because two chips took the width is
   the state this element exists to prevent, and the full row also puts it
   directly above the blank table it is explaining rather than off to one
   side of the controls.

   The band and the left rule carry the emphasis, not a colour. This is the
   only thing on screen accounting for an empty result, so it has to survive
   greyscale, a mono printout and a reader who cannot separate the tone
   hues — and it is interface, not judgement, so under the rule this
   stylesheet now keeps it may not spend a tone colour saying "nothing here"
   in the same green or amber the tables use for what the evidence says.
   Full-strength ink at a sunk ground reads as a status line at a glance
   with no hue doing any work at all.

   No `display` is declared, deliberately. Both elements ship in the markup
   with `hidden` and the script toggles the attribute; an author `display`
   here would beat the browser's `[hidden] { display: none }` and leave both
   sentences on screen permanently, which is the failure that turns a
   working filter into a page insisting nothing matches while thirty-eight
   rows sit under it. Blockification by the flex parent gives block layout
   anyway. */
.controls__none,
.controls__empty {
  flex: 0 0 100%;
  /* `p` carries a reading measure from the base styles, and on a flex item
     that measure is not a cap on a full-width box — it is the width the
     browser lays the row out with. Left in, the sentence is 100% wide in the
     declaration and two-thirds wide on the screen, sharing its row with the
     counter and starting at a different left edge from the second sentence
     directly under it. Chrome is not prose and takes the width of the bar it
     belongs to. */
  max-width: none;
  margin: 0;
  padding: var(--s-2) var(--s-3);
  border-left: 3px solid var(--ink-4);
  background: var(--paper-sunk);
  font-size: var(--step--1);
  line-height: 1.45;
  color: var(--ink-strong);
}

/* --- Figures ------------------------------------------------------------ */
.figure { margin: var(--s-6) 0; }
.figure__frame {
  background: var(--card);
  border: var(--card-border);
  border-radius: var(--card-radius);
  padding: var(--s-5);
  box-shadow: var(--shadow-card);
}
.figure__cap {
  display: block;
  margin-top: var(--s-3);
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--ink-3);
  max-width: 74ch;
}
.figure__cap b { color: var(--ink-2); font-weight: 600; }
.figure__legend {
  display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-5);
  margin-top: var(--s-4);
  font-size: var(--step--2);
  color: var(--ink-3);
}
/* The hazard the two notes above warn about, and the one place in the kit that
   walked into it. A chart legend is always on screen, so declaring `display` on
   the shared class costs a chart nothing. The structure figure borrows the same
   class for its colour key, and that key ships with `hidden` because it is a key
   to a scene that only exists once a viewer has been drawn. `display: flex` on
   the class outranks the browser's `[hidden] { display: none }`, so the key was
   drawn whether or not the scene was: in a browser with no graphics context —
   which is the case the whole figure is arranged around — the reader met four
   colour swatches naming a modified amino acid, a histone tail and the zinc ions
   directly above three paragraphs of text and no picture at all. A key to
   nothing is worse than no key, because it tells a reader something is missing
   from their screen without telling them what or why, and the sentence written
   to explain exactly that sits hidden two lines above it.
   Repairing it on the attribute rather than the class keeps the fix where the
   fault is: `hidden` goes back to meaning hidden, the chart legends are
   untouched, and no `!important` is spent that would then have to be outranked
   in print.css. */
.figure__legend[hidden] { display: none; }
/* The same trap again, one element rule further down, and it took photographing
   the drawn scene to see it. A chart legend is a `div` and a structure legend is
   a `p`, because the structure one is a sentence's worth of key that ships in
   the markup; and `base.css` gives every `p` a prose measure, 58ch, so that
   running text does not run to the full width of a card. That measure landed on
   a row of four colour labels. The key is 474px of labels and three 24px gaps,
   and it was being asked to fit in 439, so "Rest of the protein" wrapped onto a
   line of its own under the other three — an orphan that reads as a fourth
   category set apart from the first three rather than as the last of four.
   A measure is a limit on line length for the sake of the eye tracking back to
   the next line. A legend has no next line to track back to. */
.figure__legend { max-width: none; }
.figure__legend span { display: inline-flex; align-items: center; gap: 0.4em; }
.swatch { width: 10px; height: 10px; border-radius: 2px; flex: none; }

/* A redrawn chart says which original it stands in for. It sits between the
   caption and the source line, and is marked so a reader can tell at a glance
   that this figure is ours rather than a copy of the report's. */
.figure__standin {
  margin-top: var(--s-3);
  padding-left: var(--s-3);
  border-left: 2px solid var(--rule);
  font-size: var(--step--2);
  color: var(--ink-3);
  max-width: 74ch;
}

/* --- Tooltip ------------------------------------------------------------ */
.tip {
  position: fixed;
  z-index: 60;
  pointer-events: none;
  background: var(--ink-strong);
  color: #fff;
  font-size: var(--step--2);
  line-height: 1.45;
  padding: var(--s-2) var(--s-3);
  border-radius: 4px;
  max-width: 250px;
  opacity: 0;
  transform: translateY(3px);
  transition: opacity var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.tip.is-on { opacity: 1; transform: translateY(0); }
.tip b { color: #fff; display: block; margin-bottom: 2px; }
/* The key line inside a tooltip names the value under the cursor, which on
   every chart on this page is a number that may point either way. */
.tip .k { color: var(--chem-blue-tint); font-family: var(--font-data); }

/* --- Reveal on scroll ---------------------------------------------------
   Visible is the default and the fade is the exception, which is the safe way
   round. The script adds `js-anim` at the moment it is ready to start adding
   `is-in`, so the window in which a block could be hidden with nothing coming
   to show it is one frame wide. Written the other way — hidden by default,
   revealed by script — every second the script spends loading is a second the
   reader spends looking at a blank page, and a reader with scripting off
   spends all of them. */
.reveal { opacity: 1; transform: none; }
.js-anim .reveal { opacity: 0; transform: translateY(10px); }
.js-anim .reveal.is-in {
  opacity: 1; transform: none;
  transition: opacity var(--dur-3) var(--ease-out), transform var(--dur-3) var(--ease-out);
}
@media (prefers-reduced-motion: reduce) {
  .js-anim .reveal { opacity: 1; transform: none; }
}

/* --- Next / prev -------------------------------------------------------- */
.pager { display: flex; justify-content: space-between; gap: var(--s-4); margin-top: var(--s-8); padding-top: var(--s-5); border-top: 1px solid var(--rule); }
.pager a { text-decoration: none; font-size: var(--step--1); color: var(--ink-3); }
.pager a:hover { color: var(--link); }
.pager b { display: block; font-size: var(--step-0); font-weight: 600; color: var(--ink-strong); font-family: var(--font-display); }

/* Legend entries double as series toggles where a chart supports it. */
.swatch-svg { width: 10px; height: 10px; flex: none; }
.legend-btn {
  display: inline-flex; align-items: center; gap: 0.4em;
  font: inherit; font-size: var(--step--2);
  color: var(--ink-3);
  background: none; border: 0; padding: 2px 4px; margin: -2px -4px;
  border-radius: 3px; cursor: pointer;
  /* Small enough to read as a legend, big enough to hit. The negative margin
     keeps the extra height from pushing the legend away from its chart. */
  min-height: 24px;
}
.legend-btn:hover { color: var(--ink); background: var(--paper-sunk); }
.legend-btn[aria-pressed="false"] { color: var(--ink-4); text-decoration: line-through; }
.legend-btn[aria-pressed="false"] .swatch-svg { opacity: 0.3; }
/* Why the last lit series will not switch off. The refusal itself is right —
   an empty plot area and a plot area that failed to draw look identical, and
   the reader has no way to tell which they are looking at — but until now the
   page enforced it in silence: the press landed, the button stayed lit, and
   nothing anywhere said why. Measured on the one chart that has toggles, the
   fourth press changed nothing on the screen at all.
   It ships hidden in the markup and the script unhides it on the press that is
   refused, so the sentence appears at the moment it answers a question the
   reader has just asked, and is not standing there beforehand explaining a rule
   nobody has run into. No `display` is declared, for the reason the two filter
   sentences above give at length: an author `display` would outrank the
   browser's `[hidden]` and leave it on screen permanently. */
.legend-note {
  flex: 0 0 100%;
  max-width: none;
  margin: var(--s-2) 0 0;
  font-size: var(--step--2);
  line-height: 1.45;
  color: var(--ink-3);
}

/* --- Axis head, laid out as a heading that is also the panel control ----- */
.axis__title { font: inherit; margin: 0; letter-spacing: 0; }
.axis__cell { display: block; min-width: 0; }
.axis__q { display: block; }
.axis__head .chip-row { margin-bottom: 0; margin-top: var(--s-2); }
.axis__meta {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--s-2) var(--s-4);
  margin: var(--s-4) 0 0;
  font-size: var(--step--1);
  color: var(--ink-2);
}
.axis__meta dt {
  font-family: var(--font-data);
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding-top: 0.15em;
  white-space: nowrap;
}
.axis__meta dd { margin: 0; }
.stance__self { color: var(--ink-3); font-style: italic; }
/* A note under a stance has one of two authors, and the words alone do not say
   which: both are written in the third person about the report. The site's own
   objections are ruled off so a reader scanning the cards can see, without
   reading, where the report stops speaking and this page starts. */
.stance__self--site {
  border-left: 2px solid var(--rule);
  padding-left: var(--s-3);
  font-style: normal;
}
/* One identifier, on one line. It is a citation key — a reader types it into a
   search box or quotes it in a mail — and it was wrapping as "corr–" above a
   bare "3", which is two strings, neither of them the key. It is also the last
   thing in the row and the least important, so it must not be allowed to take
   width from the claim: it does not grow and it does not shrink. */
.correction__id {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: 400;
  color: var(--ink-3);
  margin-left: var(--s-4);
  flex: none;
  white-space: nowrap;
}
/* The mildest entry is the one with no tone, and with no mark beside it its
   label started a diamond's width to the left of every other label in the
   column. A hollow mark keeps the column aligned and still reads as the
   absence of a judgement, which is what it is. */
.correction .chip--outline::before {
  content: "";
  width: 7px; height: 7px;
  flex: none;
  box-shadow: inset 0 0 0 1.5px var(--connector);
  transform: rotate(45deg);
  margin-right: 0.15em;
}
.th-note { display: block; font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--ink-3); }
/* A fact attached to one cell. It prints and it reads aloud, which a tooltip
   does neither of. */
.cell-note {
  display: block;
  font-size: var(--step--2);
  line-height: var(--lh-snug);
  color: var(--ink-3);
  margin-top: 0.25em;
  text-wrap: pretty;
}
.prov--note { font-style: italic; }

/* --- Steps --------------------------------------------------------------
   Each step names the observation that would kill it, so the list is a set
   of tests rather than a plan. */
.steps { list-style: none; padding: 0; margin: var(--s-5) 0; max-width: none; display: grid; gap: var(--s-3); }
.step {
  background: var(--card);
  border: var(--card-border);
  border-radius: var(--card-radius);
  padding: var(--s-4) var(--s-5);
  margin: 0;
  counter-increment: step;
  box-shadow: var(--shadow-card);
}
/* The number gets a column and the title gets a column, for the reason the
   contents rail gets the same treatment: the number was an inline `::before`
   with a margin after it, so a title that wrapped returned to the number's edge
   instead of its own. Four of the checks in "what would change the answer" run
   past one line, and each of them printed its second line starting under the
   numeral — "Run a phenome-wide association study on loss-of-function and
   coding variants in UK / Biobank or FinnGen." — which reads as a card whose
   text has slipped rather than a title of two lines.
   The first column is a fixed width rather than `auto` because each label is
   its own grid: sized to content, a list that reaches step 10 would indent that
   title further than the nine above it and the same ragged edge would come back
   at the bottom of the list instead of the middle. */
.step__label {
  display: grid;
  grid-template-columns: 1.8rem minmax(0, 1fr);
  column-gap: var(--s-2);
  align-items: baseline;
  font-weight: 600;
  color: var(--ink-strong);
  line-height: var(--lh-snug);
  margin-bottom: var(--s-2);
}
/* A step number is an ordinal, not a rating. In green on a green wash the
   four recommended actions each opened with a small mark in the colour the
   page uses for "the evidence supports this" — including the step whose own
   cost line reads "high". */
.step__label::before {
  content: counter(step);
  font-family: var(--font-data);
  font-size: var(--step--2);
  font-weight: 400;
  color: var(--ink-2);
  background: var(--paper-sunk);
  border-radius: 3px;
  padding: 0.15em 0.45em;
  /* No margin: the gap is the grid's now, and `start` keeps the box the width
     of its digit instead of stretching it across the whole column. */
  justify-self: start;
}
.steps { counter-reset: step; }
.step__action { margin: 0 0 var(--s-2); font-size: var(--step--1); color: var(--ink-2); }
.step__kill, .step__cost {
  display: block;
  font-size: var(--step--2);
  color: var(--ink-3);
  line-height: 1.5;
}
.step__kill b { color: var(--caution-ink); font-weight: 600; }

/* --- Reference list and glossary ----------------------------------------
   The measure comes from the token, not from a second opinion about it. It was
   `78ch`, which at this list's own size is 689px against the 524px of the
   sentence directly above it — so the section's one paragraph ended at x=907
   and the twenty-odd list items under it ran on to x=1035, and the block that
   should read as the tidiest on the page had the most obviously ragged left
   boundary of any of its neighbours. A reference list is running text, which is
   the case `--measure-prose` exists for; tables and figures are what may take
   the full column.
   `ch` inside a custom property resolves against the element using it, so the
   token gives this list 58 characters at 14px exactly as it gives the paragraph
   58 at 14px, and the two edges land together without either being told a pixel
   figure. */
.reflist { list-style: decimal; padding-left: 1.6rem; font-size: var(--step--1); color: var(--ink-2); max-width: var(--measure-prose); }
.ref { margin-bottom: var(--s-2); line-height: 1.5; }
.ref__who { color: var(--ink-strong); font-weight: 500; }
.ref__where { font-style: italic; color: var(--ink-3); }

.glossary { margin: var(--s-5) 0; display: grid; gap: 0; }
.glossary__row {
  display: grid;
  grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
  gap: var(--s-4);
  padding: var(--s-3) 0;
  border-top: 1px solid var(--rule);
}
.glossary dt { font-weight: 600; color: var(--ink-strong); }
.glossary dd { margin: 0; font-size: var(--step--1); color: var(--ink-2); }
.glossary__seen { display: block; font-size: var(--step--2); color: var(--ink-3); margin-top: var(--s-1); }
@media (max-width: 640px) { .glossary__row { grid-template-columns: minmax(0, 1fr); gap: var(--s-1); } }

/* --- Scripting off -------------------------------------------------------
   `no-js` is set on the html element in the markup and removed by `report.js`
   itself, at the top of the file, before the parser reaches the body. So it
   does not mean "the script has not run yet"; it means the script is not going
   to run, for any of the reasons a script does not run — scripting switched
   off, an extension or a corporate proxy blocking the file, a truncated
   download — and the rules below are what a reader in that state sees.

   It used to be removed by a separate one-line snippet inlined in the head,
   and that is the defect this arrangement fixes. The snippet answered a
   different question from the one the class asks: it reported that the browser
   was willing to run script, not that the file carrying the behaviour had
   arrived. With `report.js` blocked and everything else served, `no-js` came
   off, these rules stood down, and the page put up twenty-one filter chips,
   two search boxes, four series toggles and seven panel headers — thirty-four
   pressable things, none of which could do anything — while seven panels stayed
   collapsed over about four thousand words with nothing left on the page able
   to open them.

   Two things follow from the class meaning what it says. Everything the page
   has to say is on the screen, because a collapsed panel with no script to open
   it is content the reader cannot reach. And every control that needs the
   script is gone, because a button that cannot do anything is worse than no
   button: the reader presses it, nothing happens, and they are left wondering
   what they got wrong. */
.no-js .axis__body[hidden] { display: block; }
.no-js .axis__head { cursor: default; }
.no-js .axis__head:hover { background: none; }

.no-js [data-expand-all],
.no-js .controls,
.no-js .search,
.no-js .count { display: none; }

/* The legend still has to label the colours, so it stays and drops the parts
   that suggest it can be pressed. */
.no-js .legend-btn { cursor: default; }

/* --- Controls that have not been handed their behaviour yet ---------------
   Every control on this page ships with `disabled` in the built markup and is
   released by `report.js` at boot. The rules above are the first lock and this
   is the second, because the rules above are a stylesheet: a page whose CSS
   arrives and whose script does not is an ordinary thing to serve by accident,
   and `.no-js .controls { display: none }` is no help at all in the mirror
   case, where the stylesheet is the file that goes missing. `disabled` is
   carried by the markup, needs nothing else to be true, and is the one word
   both a browser and a screen reader already understand for "this is a control
   and it cannot be operated".

   What follows is only about how that state looks, and the answer is: like the
   thing it is, not like a broken button. The heading of a panel is a heading
   whether or not it can be collapsed, and a legend is a key to the colours in a
   chart whether or not the entries can be switched off, so neither may be
   dimmed — dimming is for the affordance, and the affordance is the only part
   that has gone. What goes is the pointer cursor and the hover response, the
   two signals that invite the press. Browsers grey the text of a disabled
   button by default, which is why the colours are restated rather than left
   alone. */
.axis__head:disabled { cursor: default; color: inherit; }
.axis__head:disabled:hover { background: none; }
.legend-btn:disabled { cursor: default; color: var(--ink-3); }
.legend-btn:disabled:hover { color: var(--ink-3); background: none; }
.seg button:disabled { cursor: default; }
.seg button:disabled:hover { color: var(--ink-3); box-shadow: inset 0 -1px 0 var(--rule); }
.seg button:disabled[aria-pressed="true"],
.seg button:disabled[aria-pressed="true"]:hover { color: var(--ink-strong); box-shadow: inset 0 -2px 0 var(--accent); }
.search:disabled { cursor: default; background: var(--paper-sunk); color: var(--ink-3); }
.no-js .legend-btn:hover { color: var(--ink-3); background: none; }
