/* © 2026 TR3.FYI */
/* THE TYPE SCALE — one ratio, seven steps, and the only legal sizes on this site.
 *
 * Before this existed the stylesheet carried 143 font-size declarations drawing on 32 --fs-*
 * tokens (twelve of them never referenced, eight aliases of other tokens, three defining the same
 * 12px) plus fifteen hardcoded values. Up to thirty-three distinct sizes, and eight font weights
 * between 400 and 850.
 *
 * The damage concentrated where the chrome lives: 10, 11.01, 11.5, 12 and 13.01 — five sizes
 * inside three pixels. Nobody perceives 12 against 12.5 as a decision, and 650 against 700 is
 * below the threshold where weight carries meaning. The header looked unconsidered because every
 * element had chosen a plausible number on its own.
 *
 * These seven steps are a 1.25 ratio around a 16px base. The names are kept from the old token
 * set so nothing downstream breaks; only their values move onto the scale, and the aliases now
 * resolve to a step rather than to each other. tokens.css is fleet-wide and shared with four
 * dormant sites, so the scale is declared here — the Atlas owns its own typography and the
 * others keep theirs.
 */
:root{--step-1:.6875rem;/* 11px — chart ticks, axis labels, the smallest legible mark */
  --step-2:.8125rem;/* 13px — all interface text: nav, controls, table cells, captions */
  --step-3:1rem;/* 16px — body copy and the wordmark */
  --step-4:1.25rem;/* 20px — lead paragraphs, stat values, small headings */
  --step-5:1.5625rem;/* 25px — section headings */
  --step-6:1.9375rem;/* 31px — workspace titles */
  --step-7:2.4375rem;/* 39px — the paper's hero, the only display size */

  /* Three weights. Four hundred reads, six hundred distinguishes, seven hundred commands —
     650/750/800/850 were four ways of saying the same thing. */
  --weight-text:400;--weight-medium:600;--weight-strong:700;/* Every gap in the chrome is a multiple of this. The header ran 16/0/0/23/594. */
  --unit:8px;/* The old names, re-pointed at the scale. Kept so no rule has to change to gain consistency. */
  --fs-micro:var(--step-1);--fs-label:var(--step-2);--fs-meta:var(--step-2);--fs-caption:var(--step-2);--fs-caveat:var(--step-2);--fs-eyebrow:var(--step-2);--fs-note:var(--step-2);--fs-body:var(--step-3);--fs-prose:var(--step-3);--fs-input:var(--step-3);--fs-read:var(--step-3);--fs-lead:var(--step-4);--fs-h4:var(--step-4);--fs-title-sm:var(--step-4);--fs-h3:var(--step-4);--fs-title-md:var(--step-5);--fs-title-lg:var(--step-5);--fs-title-cq:var(--step-5);--fs-h2:var(--step-5);--fs-stat:var(--step-6);--fs-h1-doc:var(--step-6);--fs-h1:var(--step-7);}:root{color-scheme:light dark;--ground:#e8e3d8;--paper:#f8f5ee;--paper-raised:#fffdf8;--ink:#20201d;--muted:#65635c;--rule:#d3cec1;--rule-strong:#a8a396;/* The only rule weight that clears 3:1 on the page ground. --rule and --rule-strong are
     decorative (1.44 and 2.31); anything a reader must SEE to read a figure uses this. */
  --rule-contrast:#908d83;--score:#356fa3;--comments:#a94860;--rank:#17847b;--duration:#8b5b10;--selection:#7254ae;--hn:#b84900;/* links that leave to news.ycombinator.com */
  --success:#267259;--selection-on-ink:#b79be8;--ds-front:#356fa3;--ds-ask:#8b5b10;--ds-show:#7254ae;--ds-jobs:#a94860;--success-on-ink:#8bd5b8;--duration-on-ink:#e0ab4f;--ink-surface-muted:#c9c5bb;--ink-surface-subtle:#aaa69d;--shadow:0 18px 48px rgb(48 42 30 / 9%);/* What occlusion is made of. Ink flips light in dark mode; shadows and scrims must not —
     an ink-based scrim over a dark page is a white haze. Shade stays dark in both themes. */
  --shade:#20201d;--sans:Inter,ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;--serif:Iowan Old Style,Charter,"Palatino Linotype",Georgia,serif;/* One canvas for every route. Paper's sheet was 1120 while the workspaces ran 1360, so the
     primary surface changed width as a reader moved — the site read as different artifacts.
     The sheet wins: reading is the product, and the tables live comfortably inside it. */
  --wide:1120px;/* Two measures, replacing nine scattered caps (590–960px). Prose reads at --measure;
     headings and header blocks at --measure-wide. */
  --measure:760px;--measure-wide:880px;/* Two type sizes the shared scale lacks a home for, named by their role here: labels and
     ticks at 11px (21 uses were raw), captions at 12px (11 uses were raw; --fs-eyebrow is the
     same size but names a different voice). Rem-based like the shared scale, so reader font
     preferences apply to them too. 13px prose already has --fs-note. */
  --fs-label:.688rem;--fs-caption:.75rem;/* The 44px control tier the 2026-08-06 header slim introduced; --target-min stays the 48px
     floor everywhere else. */
  --target-compact:44px;/* The glass surface sticky bars float on. One recipe, four surfaces. */
  --veil:color-mix(in srgb,var(--paper) 96%,transparent);--fs-signal-hero:var(--fs-h1);--fs-signal-hero-mobile:clamp(30px,10vw,44px);--fs-signal-workspace:var(--fs-h1-doc);--fs-signal-h2:var(--fs-h2);--fs-signal-h3:var(--fs-title-sm);--fs-signal-panel:var(--fs-title-lg);--fs-signal-title:var(--fs-title-lg);--fs-signal-title-lg:var(--fs-stat);--fs-signal-controls:var(--fs-title-lg);--fs-signal-workspace-mobile:clamp(26px,8vw,34px);--focus-ring-color:var(--selection);--focus-ring-width:3px;--focus-ring-offset:3px;--signal-header-height:56px;--signal-safe-top:env(safe-area-inset-top,0px);--signal-safe-right:env(safe-area-inset-right,0px);--signal-safe-bottom:env(safe-area-inset-bottom,0px);--signal-safe-left:env(safe-area-inset-left,0px);--signal-header-offset:calc(var(--signal-header-height) + var(--signal-safe-top));--signal-page-scroll-offset:calc(var(--signal-header-offset) + 12px);--signal-lab-scroll-offset:64px;--signal-briefing-sticky-offset:calc(var(--signal-header-offset) + 28px);--signal-briefing-scroll-offset:4px;--signal-playback-scroll-offset:64px;--signal-inset:var(--sp-6);--signal-surface-inset:var(--surface-pad);}@media (prefers-color-scheme:dark){:root{--ds-front:#408bc5;--ds-ask:#ad7a0c;--ds-show:#9073c3;--ds-jobs:#af4a68;--ground:#171817;--paper:#20211f;--paper-raised:#272825;--ink:#efede6;--muted:#b5b2aa;--rule:#3c3e39;--rule-strong:#65675f;--rule-contrast:#6c6c66;--score:#70a9d8;--comments:#e08ba0;--rank:#55bdb2;--duration:#e0ab4f;--selection:#ae91e4;--hn:#ff6600;/* Hacker News' own orange; it clears 5.5:1 on this ground */
    --success:#72c5a4;--selection-on-ink:#7254ae;--success-on-ink:#267259;--duration-on-ink:#8a5705;--ink-surface-muted:#56544f;--ink-surface-subtle:#64615c;--shadow:0 18px 48px rgb(0 0 0 / 22%);--shade:#000;}}*{box-sizing:border-box;}/* No smooth scrolling. It is ambient motion, which is what this interface just spent a release
   removing, and it makes every programmatic scroll asynchronous: focus() and scrollIntoView()
   return before the viewport settles, so anything that measures immediately afterwards — the
   release gate's click-point computation among them — reads a position the page is still
   leaving. scroll-padding-top stays: it keeps the sticky header from covering fragment targets,
   which is layout, not animation. */
html{scroll-behavior:auto;scroll-padding-top:var(--signal-page-scroll-offset);}body{margin:0;min-width:320px;background:var(--ground);color:var(--ink);font-family:var(--sans);font-size:var(--step-3);line-height:1.5;text-rendering:optimizeLegibility;}/* This link leaves for Hacker News or Y Combinator. Every origin; the repeated [href] is
   specificity, lifting these over component rules that colour their own links at 0,1,1. */
a[href^="https://news.ycombinator.com"][href],a[href^="https://www.ycombinator.com"][href],a[href^="https://ycombinator.com"][href]{color:var(--hn);}a{color:inherit;text-decoration-thickness:.08em;text-underline-offset:.2em;}@media (hover:hover){a:hover{text-decoration-thickness:.14em;}}/* The definition was carried only by the native `title`, which waits about a second, cannot be
   reached by keyboard, and does nothing at all on a touch screen — so on a phone the dotted
   underline promised an explanation that could not be obtained. The shared data-tip engine does
   not help here: it lives in the fleet app.js, which the Atlas never loads, and it only binds
   inside svg.chart. This is CSS-only, so there is no script for the CSP to allow and no engine to
   go missing; :focus-visible covers keyboard, and a focusable element receives focus on tap. */
.gloss{position:relative;text-decoration:underline dotted;text-decoration-thickness:1px;text-underline-offset:.2em;cursor:help;font-style:inherit;}/* Start-aligned, not centred. A centred panel clips whenever the term sits within half a panel
   width of a viewport edge, measured at 8px past the left on "front page" and 31px past the
   right on the header's "Snapshot". Anchoring to the term's own start edge makes left clipping
   impossible; the one place a term sits near the right edge is the header pill, handled below. */
.gloss::after{content:attr(data-tip);position:absolute;z-index:40;left:0;bottom:calc(100% + 8px);width:max-content;max-width:min(280px,70vw);padding:8px 10px;color:var(--paper);background:var(--ink);border:1px solid var(--ink);font:var(--weight-text) var(--step-2)/1.4 var(--sans);letter-spacing:0;text-transform:none;text-decoration:none;white-space:normal;display:none;opacity:0;visibility:hidden;transition:opacity .12s ease;pointer-events:none;}.capture-status .gloss::after{left:auto;right:0;}/* :focus, not :focus-visible. Browsers suppress focus-visible for pointer input, so a tap
   would have focused the term and shown nothing — the touch case this exists for. */
/* display:none while hidden, not merely visibility:hidden. An absolutely positioned box that
   is only invisible still occupies layout and still counts toward the document's scroll
   width: a 537px-wide tooltip anchored to a term near the right margin made every page
   carrying a glossary term scroll sideways at tablet and desktop widths. It went unseen at
   375px because the phone rule switches it to position:fixed, which contributes nothing.
   The cost is the 120ms fade, which prefers-reduced-motion already removes for some readers. */
.gloss:focus::after{display:block;opacity:1;visibility:visible;}@media (hover:hover){.gloss:hover::after{display:block;opacity:1;visibility:visible;}}/* Below 640px every term is within a panel width of both edges, so no inline anchoring is safe.
   Pin to the viewport instead — it cannot clip, and a definition is worth the sheet. It clears
   the route bar (50px plus the safe area) rather than covering the way back out of the page. */
@media (max-width:640px){/* Provenance stacked label-over-value costs three rows on a phone and pushed the paper's
   first finding to 774px of an 812px viewport — past the fold for the half of readers who
   never scroll. One row each keeps every receipt visible and returns ~70px to the content. */
.capture-line > div{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 8px;}.capture-line dt::after{content:":";}.gloss::after,.capture-status .gloss::after{position:fixed;inset:auto max(12px,var(--signal-safe-right)) calc(62px + var(--signal-safe-bottom)) max(12px,var(--signal-safe-left));width:auto;max-width:none;}}@media (prefers-reduced-motion:reduce){.gloss::after{transition:none !important;}}button,input{font:inherit;}:focus-visible{outline:var(--focus-ring-width) solid var(--focus-ring-color);outline-offset:var(--focus-ring-offset);}.skip-link{position:fixed;z-index:100;left:max(12px,var(--signal-safe-left));top:calc(8px + var(--signal-safe-top));transform:translateY(-160%);background:var(--ink);color:var(--paper);padding:12px 16px;}.skip-link:focus{transform:translateY(0);}/* NOT grouped with .lab-lens-label's media-scoped clone below, nor may :focus-visible ever
   join a selector list containing :has(): the label is only hidden at some widths, and an
   unsupported selector in a plain list invalidates the whole list — grouping would break
   the visible label at one width and every focus ring in older engines. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;}.app-header{min-height:var(--signal-header-offset);padding:calc(6px + var(--signal-safe-top)) max(24px,var(--signal-safe-right),calc((100vw - var(--wide)) / 2)) 6px max(24px,var(--signal-safe-left),calc((100vw - var(--wide)) / 2));display:grid;grid-template-columns:auto auto minmax(0,1fr);align-items:center;gap:32px;/* --ground, not --paper: this is what sits under Safari's tinted top bar, and
     theme-color is --ground. Veiling a lighter shade put a visible step at the chrome
     edge of every page. The blur still separates the header once content scrolls under
     it — the lift is earned by the border and the blur rather than by a lighter fill. */
  background:color-mix(in srgb,var(--ground) 94%,transparent);border-bottom:1px solid var(--rule);position:sticky;top:0;z-index:20;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);}.brand{display:flex;align-items:center;gap:var(--unit);min-height:var(--target-compact);white-space:nowrap;text-decoration:none;min-width:0;}/* The qualifier wraps rather than running under the section control. At 320-332px the header's
   auto track is sized by the longest route label, leaving the brand's 1fr track too narrow for
   the lockup, and nowrap sent "HACKER NEWS" across the divider rule. Wrapping, not truncation:
   a contract test forbids ellipsis site-wide because a cut-off word hides information, and it
   is right — this costs a second line at three route labels below 333px and nothing anywhere
   else, since a line only breaks when it must. */
.brand > span{min-width:0;}.brand small{white-space:normal;}.brand-mark{display:block;flex:none;width:30px;height:30px;}.brand strong,.brand small{display:block;}/* One lockup, not two stacked lines: the serif name and the uppercase qualifier read as a single
   mark only when the leading between them is tighter than the leading within either. */
.brand strong{font:var(--weight-strong) var(--step-3)/1 var(--serif);}.brand small{margin-top:1px;color:var(--muted);font-size:var(--fs-label);line-height:1;letter-spacing:.12em;text-transform:uppercase;}/* The nav is derived from the route catalog, so its width is whatever the catalog happens to be
   — adding /start/ pushed it to a 803px min-content that the `1fr` column could not shrink below,
   and the overflow landed on the document instead. `min-width: 0` plus an inline scroll container
   caps the damage at the nav: a catalog that outgrows the header scrolls its own row rather than
   scrolling the page sideways. */
.primary-nav{justify-self:start;min-width:0;display:flex;align-items:center;}/* The header names three routes; this opens the index holding all of them. The count is the point
   — it tells a reader there is more here than the three words beside it, which a bare "Menu" does
   not. It reads as a rule-bounded control rather than a fourth link so the three stay primary. */
/* No left margin: the row's own gap sets the distance to the lockup. That gap alone does NOT
   centre the divider, though — it was tried, and measuring the built header still found the
   rule 16px from the lockup and 8px from the label, because the two sides are set by
   different things: the gap outside, this button's padding inside. */ .nav-index-toggle{min-height:var(--target-compact);/* The divider is this button's border-left, so the space to its LEFT comes from the
     header's own gap and the space to its RIGHT from this padding. They were 16px and 8px,
     which read as a rule pushed up against the word after it. Matching the padding to the
     gap centres the rule between the lockup and the label. */
  padding:0 var(--unit) 0 calc(var(--unit) * 2);display:inline-flex;align-items:center;gap:var(--unit);color:var(--muted);background:transparent;border:0;border-left:1px solid var(--rule);font:inherit;white-space:nowrap;cursor:pointer;}@media (hover:hover){.nav-index-toggle:hover{color:var(--ink);}}.nav-index-toggle span{font-size:var(--step-1);letter-spacing:.1em;text-transform:uppercase;}.nav-index-toggle strong{color:var(--ink);font-size:var(--step-2);font-weight:var(--weight-medium);font-variant-numeric:tabular-nums;}.nav-index-toggle[aria-expanded="true"]{color:var(--ink);}/* This slot only ever holds the active route, so it carries the current-destination styling
   itself rather than waiting for the aria-current sweep, which would otherwise paint it as a
   muted peer for a frame. Its label comes from the catalog, so it must not wrap. */
.header-status{justify-self:end;min-width:0;display:grid;grid-template-columns:minmax(0,1fr);align-items:stretch;}.capture-status{--status-color:var(--rank);width:100%;min-width:0;min-height:34px;display:flex;align-items:center;justify-content:flex-end;color:var(--muted);font-size:var(--fs-label);line-height:1.25;white-space:nowrap;}.capture-status[data-capture-state="live"]{--status-color:var(--success);}.capture-status[data-capture-state="reference"]{--status-color:var(--muted);}.capture-status[data-capture-state="reference"] :is(.capture-status-unit,time){display:none;}/* Two fixed rows — label above, timestamp below — never a flex line that decides for itself how
   to break. The old markup put four inline children in one flex row and switched to `display:
   grid` under narrow widths, which stacked each child on its own line: "Snapshot / verified / ·
   / 31 Jul 26 · 07:25 UTC" filled three lines of a 34px box. Row one carries the unit so "UTC"
   is stated once, in the label, rather than trailing the value where it forced the wrap. */
.capture-status-copy{display:grid;gap:1px;}.capture-status-head{display:flex;align-items:baseline;gap:4px;}/* UTC belongs with the value it qualifies, not with the label above it. It sat on row one
   because trailing the value once wrapped the box to three lines — that was four inline
   children in one flex row, which the two-row copy replaced. Row two is a row of its own
   and stays one line; the 34px floor below is what proves it. */
.capture-status-time{display:flex;align-items:baseline;gap:4px;min-width:0;}.capture-status strong{color:color-mix(in srgb,var(--status-color) 68%,var(--ink));font-size:var(--fs-micro);font-weight:var(--weight-strong);letter-spacing:.09em;text-transform:uppercase;}.capture-status-label{color:color-mix(in srgb,var(--status-color) 68%,var(--ink));}.capture-status time{color:var(--ink);font-variant-numeric:tabular-nums;}.capture-status-unit{color:var(--muted);}.capture-status-unit::before{content:"·";margin-right:4px;color:var(--rule-strong);}.site-footer{margin-top:32px;padding:16px max(var(--page-pad),var(--signal-safe-right)) calc(24px + var(--signal-safe-bottom)) max(var(--page-pad),var(--signal-safe-left));color:var(--muted);border-top:1px solid var(--rule);font-size:var(--fs-label);line-height:1.5;}.footer-line{width:min(100%,var(--wide));margin:0 auto;display:flex;flex-wrap:wrap;align-items:center;gap:4px 16px;}.footer-line nav{display:inline-flex;gap:16px;}/* Which document governs this one, stated before the policy rather than after it. A reader who
   lands here from search has no other way to learn a parent exists. */
.parent-policy{margin:16px 0 0;padding:12px 16px;background:var(--paper-raised);border-left:2px solid var(--rule-strong);color:var(--muted);font-size:var(--fs-note);}.parent-policy a{color:var(--ink);}/* The publisher mark is the Atlas's one link back out to tr3.fyi, and it is now the NAME inside
   the copyright rather than a separate entry beside it. The treatment is unchanged and the
   reasoning survives the move intact: underlined only on interaction, because a permanent
   underline on a copyright line reads as boilerplate someone forgot to style, and this is still
   the only outbound link in the legal row. Selector follows the anchor into the copyright span.
   .legal-footer is deliberately NOT named here: the Atlas emits no such row — sync_hn_legal_footers
   skips a footer that already states the copyright and policies — and adding a selector for a
   class this sheet does not own fails validate_design_system, correctly. */
.footer-line > span > a[href^="https://tr3.fyi"]{color:inherit;text-decoration:none;}@media (hover:hover){.footer-line > span > a[href^="https://tr3.fyi"]:hover{text-decoration:underline;text-underline-offset:3px;}}.footer-line > span > a[href^="https://tr3.fyi"]:focus-visible{text-decoration:underline;text-underline-offset:3px;outline:2px solid var(--selection);outline-offset:3px;/* --r-2xs is 3px, the scale's smallest. The literal here was 2px; on the corner of a
     focus ring that difference is a single pixel, which is not worth a new scale member. */
  border-radius:var(--r-2xs);}.footer-line a,.footer-line button{min-height:var(--target-compact);display:inline-flex;align-items:center;color:var(--ink);background:none;border:0;padding:0 2px;font:inherit;text-decoration:underline;text-underline-offset:3px;cursor:pointer;}.site-footer > *{width:min(100%,var(--wide));margin:0 auto;}.site-footer nav{display:flex;flex-wrap:wrap;gap:4px 16px;}.site-footer a{color:var(--ink);text-underline-offset:3px;}/* The update prompt used to sit in an 84px second column beside the capture pill. That made a
   transient service-worker state permanently widen a fixed-width header slot: measured at a
   1180px viewport the header's three columns summed to 1227px, so the document itself gained
   47px of horizontal scroll and the button hung off the right edge. Everything between the
   1160px desktop breakpoint and roughly 1250px was affected, iPad Pro landscape included.
   One slot, two states instead: the pill IS the update control while an update waits. Nothing
   is lost to assistive technology because the button's aria-label already restates the capture
   ("Apply site update… Packaged verified snapshot, captured …"). */
.header-status:has(.pwa-update) .capture-status{display:none;}/* A pill that fits the row it lives in. It was width:100%, stacked (display:grid, the label
   above the action) and min-height:var(--target-min) — 48px against a 56px header — so it
   could not share the row and took one of its own: 57px to 113px, measured. The breakpoint
   comment further down already names that exact failure for the OLD status and calls "the
   row holds at every width" the fix; the pill was simply exempted from it.
   Inline-flex puts UPDATE and its action on one line. The touch target is kept by padding
   with a matching negative margin, so it is paid for in hit area rather than in layout
   height — the same trick the launchpad footer links use. */
.pwa-update{width:auto;min-width:0;min-height:0;margin-block:calc(var(--sp-6) * -1);padding:var(--sp-6) var(--sp-4);display:inline-flex;align-items:baseline;gap:var(--sp-4);white-space:nowrap;}.pwa-update:focus-visible{color:var(--paper);background:var(--selection);}@media (hover:hover){.pwa-update:hover{color:var(--paper);background:var(--selection);}}/* Statistical symbols keep their case inside uppercased labels: Ρ is not ρ. */
dt .stat-symbol,.stat-symbol{font-style:normal;text-transform:none;}.pwa-update strong{font-size:var(--fs-micro);font-weight:var(--weight-strong);letter-spacing:.08em;text-transform:uppercase;}.pwa-update span{font-size:var(--fs-micro);font-weight:var(--weight-strong);}.pwa-update:disabled{cursor:default;opacity:.78;}main{min-height:calc(100vh - 104px);}.view{width:min(100% - 48px,var(--wide));margin:0 auto;padding:32px 0 64px;}.content-grid{width:min(100%,1040px);margin-inline:auto;}h1,h2,h3,p{overflow-wrap:anywhere;}h1,h2{font-family:var(--serif);font-weight:var(--weight-medium);letter-spacing:-.025em;}h1{margin:0;max-width:var(--measure-wide);font-size:var(--fs-signal-hero);line-height:1;}h2{margin:0;font-size:var(--fs-signal-h2);line-height:1.1;}h3{margin:0;font-size:var(--fs-signal-h3);line-height:1.25;}.dek{max-width:var(--measure);margin:24px 0 0;color:var(--muted);font:var(--weight-text) clamp(18px,2.1vw,24px)/1.4 var(--serif);}.lead-copy{font:var(--weight-text) clamp(18px,2vw,23px)/1.5 var(--serif);}.paper-hero{padding:20px 0 24px;}/* The Atlas's first stated principle is to orient before asking anyone to explore, and the paper
   opened in expert register — "a finite reading of one verified front-page capture" — without ever
   saying what Hacker News is. This band answers that in one sentence. Deliberately a rule and muted
   text rather than a card: a returning reader's eye should pass over it on the way to the evidence,
   so it costs them a line, not a dismissal. */
/* margin-block, never the `margin` shorthand: this element also carries .content-grid, whose
   `margin-inline: auto` does the centring, and a shorthand here resets that to 0 and shunts the whole band 96px left of the hero it sits under. */ /* Ruled above only. The bottom rule sat 4px above the paper sheet's own edge, so two lines
   did one separator's work. The sheet already begins where the note ends. */
.orientation-note{margin-block:0 4px;padding:12px 0 16px;border-top:1px solid var(--rule);color:var(--muted);font-size:var(--step-2);line-height:1.5;}.orientation-note p{max-width:72ch;margin:0;}.orientation-note p + p{margin-top:6px;}.orientation-note strong{color:var(--ink);font-weight:var(--weight-medium);}/* One sentence that differs between visits, for the reader who arrives, glances, and leaves.
   Sits under the receipts so provenance still reads first. */
/* One contextual step at the foot of a workspace. Six routes previously ended with nothing,
   so the only way onward was reopening the section sheet. Deliberately a single link, not a
   restored navigation bar: the strip removed the bar because it was noise, and this answers
   the different question of "what follows this". */
.route-onward{margin:32px 0 0;padding-top:16px;border-top:1px solid var(--rule);}.route-onward a{min-height:var(--target-min);display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:2px 12px;padding:8px 0;color:var(--ink);text-decoration:none;}.route-onward span:first-child{grid-row:span 2;color:var(--muted);font-size:var(--fs-micro);font-weight:var(--weight-strong);letter-spacing:.12em;text-transform:uppercase;}.route-onward strong{font-size:var(--fs-title-sm);font-weight:var(--weight-strong);}.route-onward small{grid-column:2;color:var(--muted);font-size:var(--fs-meta);}.route-onward a:focus-visible strong{color:var(--selection);text-decoration:underline;text-underline-offset:.18em;}@media (hover:hover){.route-onward a:hover strong{color:var(--selection);text-decoration:underline;text-underline-offset:.18em;}}.route-onward span[aria-hidden]{grid-row:span 2;color:var(--selection);font-size:var(--fs-title-sm);}.paper-delta{margin:16px 0 0;color:var(--muted);font-size:var(--fs-meta);line-height:1.5;}.capture-line{display:flex;flex-wrap:wrap;gap:10px 32px;margin:16px 0 0;}/* The snapshot id is a receipt, not a headline: exact and selectable, but quiet. It read at
   the same weight as the human-facing capture time, which made a machine string the second
   loudest fact in the hero. */
#paper-snapshot{color:var(--muted);font-size:var(--fs-meta);font-weight:var(--weight-text);letter-spacing:.01em;}.capture-line div{flex:1 1 180px;}/* A discussion page carries two of these rows — what HN reported, then what the model made of
   the conversation — and they must stay separate, because keeping measured figures apart from a
   model's account is the distinction the whole site is built on. Sharing a component does not
   make them share a rhythm: .capture-line grows its items to fill, so a row of three and a row
   of four land on different widths and no label in the second lines up with anything in the
   first. A fixed four-column grid gives both rows the same columns; a three-item row simply
   leaves the fourth empty. */
.measure-row{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px 32px;margin:16px 0 0;}.measure-row div{flex:none;}@media (max-width:640px){.measure-row{grid-template-columns:repeat(2,minmax(0,1fr));}}.capture-line dt{color:var(--muted);font-size:var(--fs-micro);letter-spacing:.14em;text-transform:uppercase;}.capture-line dd{min-height:20px;margin:2px 0 0;font-size:var(--fs-note);font-weight:var(--weight-medium);font-variant-numeric:tabular-nums;}/* No shadow. This element is the full height of the document — 4,525px on the paper — so
   `0 18px 48px` was a page-length slab floating over the ground: skeuomorphic depth, and the only
   decorative weight left anywhere in the view. The paper-against-ground distinction is structural
   and stays; the hairline is what separates them, and one hairline is enough. */
.paper-sheet{width:min(100%,var(--wide));margin:0 auto;background:var(--paper);}.paper-section{display:grid;grid-template-columns:minmax(0,1fr);gap:24px;padding:36px clamp(24px,5vw,64px);border-bottom:1px solid var(--rule);}/* The number and its name are two adjacent spans with nothing between them, so every section
   heading in a paper sheet read as "01PURPOSE". Pre-existing on About and Methods, not new to
   Start here. Margin rather than a flex gap: .section-label sits in a narrow grid column, and making it a flex row wraps the name onto its own line and stretches the box. */ .paper-section .lead-copy{max-width:var(--measure);}/* Changelog, Privacy and Terms inject a flat run of <h2>/<p> straight into .paper-section,
   which is a two-column grid (72px label + content). With no .section-label to occupy the
   first cell, every element auto-placed alternately: heading into the 72px column, paragraph
   into the content column, all the way down the document. Prose was breaking mid-word
   — "Hacke/r", "condition/s", "underlyin/g" — for the entire length of all three pages.
   One wrapper spanning both tracks; the grid is for label-and-content pairs, and these
   three sections have no label. */
.paper-section > .policy-body{grid-column:1 / -1;max-width:var(--measure);}/* The hairline stat grid: three surfaces share one frame. Narrow-width overrides for each
   arrive later in the cascade and are unaffected by the shared declaration. */
.finding-grid,.history-segments,.research-grid{margin-top:24px;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1px;background:var(--rule);border:1px solid var(--rule);}.finding{min-width:0;padding:24px;background:var(--paper-raised);}.finding-value{display:block;margin-bottom:16px;color:var(--selection);font:var(--weight-medium) clamp(30px,4vw,46px)/1 var(--serif);font-variant-numeric:tabular-nums;}.finding p{margin:10px 0 0;color:var(--muted);font-size:var(--step-2);}.figure-column{min-width:0;}.figure-heading{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:end;gap:8px 24px;}.figure-heading p{max-width:var(--measure);margin:12px 0 0;color:var(--muted);}.button{min-height:var(--target-min);padding:0 16px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--ink);font-size:var(--fs-note);font-weight:var(--weight-strong);text-decoration:none;}.button-secondary{background:transparent;}@media (hover:hover){.button:hover{background:var(--ink);color:var(--paper);}}.chart-shell{min-width:0;margin-top:32px;padding:var(--signal-surface-inset);position:relative;overflow:visible;background:color-mix(in srgb,var(--ground) 40%,transparent);border:1px solid var(--rule);}.chart-shell.large{min-height:440px;}.scatter{display:block;width:100%;height:auto;overflow:visible;}.chart-grid{stroke:var(--rule);stroke-width:1;}.chart-axis{fill:var(--muted);font:11px var(--sans);}.chart-axis-label{fill:var(--ink);font:var(--weight-medium) 11px var(--sans);letter-spacing:.08em;text-transform:uppercase;}/* One hue per mark; the ring is a paper halo, not a second measurement. */
.chart-point{fill:var(--score);stroke:var(--paper);stroke-width:1.5;pointer-events:none;transition:r 120ms ease,fill 120ms ease;}.chart-hit{fill:transparent;stroke:transparent;cursor:pointer;}.chart-datum:focus-within .chart-point{stroke-width:3;}@media (hover:hover){.chart-datum:hover .chart-point{stroke-width:3;}}.chart-datum.dataset-front .chart-point{fill:var(--ds-front);}.chart-datum.dataset-ask .chart-point{fill:var(--ds-ask);}.chart-datum.dataset-show .chart-point{fill:var(--ds-show);}.chart-datum.dataset-job .chart-point{fill:var(--ds-jobs);}.chart-datum.is-emphasis .chart-point{fill:var(--paper-raised);stroke:var(--selection);stroke-width:4;}/* One selection, one signal. The selected mark used to carry four at once: a 4px ring, a
   drop-shadow glow, the r16 hit circle drawn as a second ring, and — because a browser outlines
   an SVG element's bounding BOX — a square focus outline around a round target. A bullseye in a
   box. The mark is filled with the accent and drawn larger by the renderer; that is the signal.
   The hit circle keeps a ring on focus, which is the round indicator the square was failing to
   be, so the default outline is suppressed only where a better-shaped one replaces it. */
.chart-datum.is-selected .chart-point{fill:var(--selection);stroke:var(--paper);stroke-width:2;}.chart-datum:focus-within .chart-hit{stroke:var(--selection);stroke-width:2;}.chart-hit:focus-visible{outline:none;}.chart-label{fill:var(--ink);font:var(--weight-medium) 10px var(--sans);pointer-events:none;}.chart-crosshair{stroke:color-mix(in srgb,var(--selection) 45%,transparent);stroke-width:1;stroke-dasharray:2 4;pointer-events:none;}.chart-tooltip{position:absolute;z-index:5;width:min(290px,calc(100% - 24px));padding:12px;color:var(--paper);background:var(--ink);border:1px solid var(--rule-strong);box-shadow:var(--shadow);pointer-events:none;font-size:var(--fs-caption);transform:translate(-50%,calc(-100% - 18px));}.chart-tooltip[hidden]{display:none;}.chart-tooltip strong,.chart-tooltip span,.chart-tooltip .chart-tooltip-title{display:block;}.chart-tooltip strong,.chart-tooltip .chart-tooltip-title{font:var(--weight-medium) var(--step-3)/1.25 var(--serif);}/* Interactive only while it carries the selected story's link. The default pointer-events:none
   above is load-bearing: an always-clickable tooltip sits over the marks and swallows the clicks
   meant for them. */
.chart-tooltip.is-interactive{pointer-events:auto;}/* The second component that inverts its ground — .intelligence-card below carries the full
   explanation of why each theme needs the OTHER theme's --hn. It only matters here since the
   tooltip gained a link; before that, nothing in it used the link colour. */
/* Colour comes from the a[href^="https://news.ycombinator.com"][href] rule above, which outranks
   anything written here — so this sets the token, not the property. Declaring `color` as well
   would read as load-bearing while losing on specificity. */
.chart-tooltip{--hn:#ff6600;}/* The shared focus ring is --accent, which is chosen against the page ground, not this one. */
.chart-tooltip-title:focus-visible{outline:2px solid var(--hn);outline-offset:2px;}.chart-tooltip span{margin-top:4px;color:var(--ink-surface-muted);}.chart-tooltip .chart-tooltip-position{margin-top:8px;color:var(--ink-surface-subtle);font-size:var(--fs-micro);font-weight:var(--weight-strong);letter-spacing:.1em;text-transform:uppercase;}.figure-meta{display:flex;flex-wrap:wrap;gap:8px 20px;margin:12px 0 0;color:var(--muted);font-size:var(--fs-label);}/* A date is not a caveat: doc-stamp once used .limitation and wore its caution rule. */
.limitation,.doc-stamp{margin:16px 0 0;color:var(--muted);font-size:var(--fs-note);}.limitation{padding-left:12px;border-left:3px solid var(--duration);}/* The route loader's failure notice. bootstrap.js prepends it with role="status" when an
   analytical workspace cannot load — and it had no rule in any of the fifteen stylesheets the
   Atlas ships, so the one message a reader sees when something has gone wrong arrived as
   unstyled body prose. It sits on the failure path, which is why nobody met it.
   Given the caution rule that .limitation uses, because it says the same kind of thing. */
.notice{margin:16px 0 0;padding-left:12px;border-left:3px solid var(--duration);color:var(--muted);font-size:var(--fs-note);}/* The one component that inverts its ground, which is why --ink-surface-subtle, -muted and
   --success-on-ink exist. Two colours reached it without an on-ink value: the HN link colour,
   applied by an attribute selector that outranks the .fact-list `color: inherit` it used to
   defer to (3.10:1), and the numerals (2.80:1). Each theme's opposite value is the on-ink one. */
.intelligence-card{--hn:#ff6600;--duration:#e0ab4f;padding:clamp(24px,4vw,44px);background:var(--ink);color:var(--paper);}/* After the rule it overrides, not earlier: a media query adds no specificity, so a block above
   the base rule loses to it. Ground #efede6 needs the light values, orange a notch darker than
   #b84900, which measures 4.49:1 there. */
@media (prefers-color-scheme:dark){.intelligence-card{--hn:#b34600;--duration:#8b5b10;}.chart-tooltip{--hn:#b34600;}}.intelligence-heading{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px 24px;align-items:start;}.gemma-label{color:var(--ink-surface-subtle);font-size:var(--fs-label);font-weight:var(--weight-strong);letter-spacing:.13em;text-transform:uppercase;}.intelligence-card h2{margin-top:8px;}.grounded-badge{flex:none;padding:8px var(--signal-surface-inset);color:var(--success-on-ink);background:transparent;border:1px solid currentColor;font-size:var(--fs-micro);font-weight:var(--weight-strong);letter-spacing:.12em;text-transform:uppercase;}.observation-list{margin:24px 0;padding:0;list-style:none;counter-reset:observation;}.observation-list li{counter-increment:observation;display:grid;grid-template-columns:34px 1fr;gap:4px 12px;padding:16px 0;border-top:1px solid color-mix(in srgb,var(--paper) 18%,transparent);}.observation-list li::before{content:counter(observation,decimal-leading-zero);color:var(--duration);font:var(--weight-medium) 12px var(--serif);}.observation-list li strong,.observation-list li span{grid-column:2;}.observation-list li strong{font-family:var(--serif);font-size:var(--step-4);}.observation-list li span{color:var(--ink-surface-muted);font-size:var(--step-2);}.visible-evidence{margin-top:24px;padding-top:20px;border-top:1px solid color-mix(in srgb,var(--paper) 18%,transparent);}.visible-evidence h3{font:var(--weight-medium) 18px var(--serif);}/* A quoted comment is the only text here the project did not write, so it is set apart rather
   than folded into the page voice: the rule on the left marks the boundary, and the source line
   names who wrote it and how many people answered — the count that selected it. */
/* The evidence sections sit between the table and the method note and need the same rule    above them the method note has, or the last attribution line reads as the next    heading's kicker. */
.observation-archive > section{margin-top:24px;}.observation-archive > section > h3{font:var(--weight-medium) 19px var(--serif);}.fact-list{padding-left:20px;color:var(--ink-surface-muted);font-size:var(--fs-note);}.fact-list a{color:inherit;}.paper-end{min-height:110px;padding:24px clamp(24px,6vw,84px);display:flex;justify-content:space-between;align-items:center;gap:20px;font-size:var(--fs-note);}.paper-end span{color:var(--muted);}.workspace-header{display:flex;flex-wrap:wrap;justify-content:space-between;gap:10px 24px;align-items:end;margin-bottom:16px;}.workspace-header > div{min-width:0;}.workspace-header h1,.workspace-header h2{font-size:var(--fs-signal-workspace);}.workspace-header .dek{max-width:var(--measure-wide);margin-top:12px;font-size:clamp(17px,1.65vw,21px);line-height:1.4;}.workspace-grid{display:grid;grid-template-columns:minmax(0,3fr) minmax(250px,1fr);gap:16px;}.panel{min-width:0;padding:clamp(18px,2.4vw,28px);background:var(--paper);border:1px solid var(--rule);}/* Same contract as .control-deck-heading: heading and status sit together while both fit,
   and the status drops below when they do not. A nowrap row squeezed panel headings to
   eleven characters per line at 320px. */
.panel-header{display:flex;flex-wrap:wrap;align-items:start;justify-content:space-between;gap:8px 20px;}.panel-header > div{flex:1 1 16ch;min-width:0;}.panel-header h2{font-size:var(--fs-signal-panel);}.figure-controls{display:flex;align-items:end;gap:10px;}/* The badge sits beside a control, so it matches the control's height: at 32px against
   the select's 48 it read as having slipped down, which is what it was. Scoped to this
   row — .data-state is a standalone status line on landscape, lab and briefings, and
   there a control-sized box would be padding around nothing. */
.figure-controls .data-state{min-height:var(--target-min);}.figure-controls label,.control-grid label,.workbench-controls label{display:grid;gap:6px;color:var(--muted);font-size:var(--fs-micro);font-weight:var(--weight-strong);letter-spacing:.1em;text-transform:uppercase;}select,input[type="search"]{min-height:var(--target-min);width:100%;padding:0 36px 0 var(--signal-surface-inset);color:var(--ink);background:var(--paper-raised);border:1px solid var(--rule-strong);border-radius:0;}input[type="search"]{padding-right:12px;}.chart-description{margin-top:16px;padding:16px;background:color-mix(in srgb,var(--ground) 44%,transparent);border:1px solid var(--rule);}.chart-description h3{font-family:var(--serif);font-size:var(--fs-title-lg);}.chart-description p{color:var(--muted);}.stat-line{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1px;margin:16px 0 0;background:var(--rule);border:1px solid var(--rule);}.stat-line div{padding:12px var(--signal-surface-inset);background:var(--paper);}.stat-line dt{color:var(--muted);font-size:var(--fs-micro);font-weight:var(--weight-strong);letter-spacing:.09em;text-transform:uppercase;}.stat-line dd{margin:4px 0 0;font:var(--weight-medium) 17px var(--serif);}.data-state{min-height:32px;padding:6px var(--signal-surface-inset);display:inline-flex;align-items:center;color:var(--success);border:1px solid currentColor;font-size:var(--fs-micro);font-weight:var(--weight-strong);letter-spacing:.1em;text-transform:uppercase;}.selection-panel{background:var(--ink);color:var(--paper);}.selection-panel h2{margin:8px 0 12px;font-size:var(--fs-signal-title);}.selection-panel p{color:var(--ink-surface-muted);}.selection-rank{color:var(--duration-on-ink);font:var(--weight-medium) var(--step-7)/1 var(--serif);}.selection-stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1px;margin-top:24px;background:color-mix(in srgb,var(--paper) 18%,transparent);}.selection-stats div{min-width:0;padding:12px;background:var(--ink);}/* Weight and tracking matched to .story-reading-label, which sits four lines below it in the same
   panel. Both are 11px uppercase micro-labels on the same ground — SCORE / COMMENTS / SCORE Δ /
   COMMENT Δ, then DISCUSSION SUMMARY — and this rule stated no font-weight at all, so the first
   four came out 400 against the fifth at 700 with nothing distinguishing their roles. */
.selection-stats dt{color:var(--ink-surface-subtle);font-size:var(--fs-micro);font-weight:var(--weight-strong);text-transform:uppercase;letter-spacing:.08em;}.selection-stats dd{margin:4px 0 0;font:var(--weight-medium) 20px var(--serif);overflow-wrap:anywhere;}/* An absence is not a reading. At the tile's 20px the word is wider than the 99px column, and
   overflow-wrap: anywhere breaks it mid-word — measured 54px tall over two lines, 16px over one
   at caption size. The muted token is the surface-aware one; .is-unavailable's --muted is meant
   for paper, and these tiles sit on --ink. */
/* Ledger context graphics. Sized to the same 104x22 as the rest of the instrument's sparklines
   so the receipt reads as part of it rather than an import. --selection paints the figure under
   discussion, matching the hue the tables and marks already use for the current selection. */
.chart-spark,.chart-peer{display:block;overflow:visible;}/* A roving-tabindex composite, one per table — see armMicroCharts in app.js. :focus as well as
   :focus-visible, like .gloss above: these carry no pointer affordance, so there is no noisy-ring
   cost and the ring cannot depend on a heuristic. The global ring's 3px offset would be clipped
   by the table's scroller; the tint is what identifies which of ninety-eight has focus. */
.chart-spark:focus,.chart-peer:focus,.chart-spark:focus-visible,.chart-peer:focus-visible{outline:2px solid var(--focus-ring-color);outline-offset:0;border-radius:var(--r-2xs);background:color-mix(in srgb,var(--selection) 10%,transparent);}.chart-spark-line{stroke:var(--muted);stroke-width:1.25;stroke-linejoin:round;}.chart-spark-mark{fill:var(--selection);}.chart-peer-base{stroke:var(--rule-contrast);stroke-width:1;}.chart-peer-fill{fill:var(--selection);}.chart-peer-tick{stroke:var(--muted);stroke-width:1;}.selection-stats dd.is-unavailable{font:italic 400 var(--fs-caption)/1.4 var(--serif);color:var(--ink-surface-subtle);}.table-panel{margin-top:16px;}/* A column every row reports as unavailable is not evidence of a gap, it is a column that does
   not apply to the loaded cohort. Front-page captures carry no trajectory. */
#observatory-table[data-trajectory="none"] .col-trajectory{display:none;}.table-scroll{margin:16px -4px -4px;overflow-x:auto;overscroll-behavior-inline:contain;}.is-scrollable:focus-visible{outline:3px solid var(--selection);outline-offset:3px;}table{width:100%;border-collapse:collapse;font-size:var(--fs-note);line-height:1.4;}caption{padding:0 4px 12px;color:var(--muted);font-size:var(--fs-caption);line-height:1.4;text-align:left;}thead th{color:var(--ink);background:var(--veil);border-top:1px solid var(--rule-strong);border-bottom-color:var(--rule-strong);box-shadow:0 10px 16px -16px color-mix(in srgb,var(--shade) 80%,transparent);font-size:var(--fs-label);font-weight:var(--weight-strong);letter-spacing:.075em;text-transform:uppercase;}tbody th[scope="row"]{color:var(--ink);font-size:var(--fs-caption);font-weight:var(--weight-strong);letter-spacing:.01em;text-transform:none;}th,td{padding:16px var(--signal-surface-inset);text-align:right;border-bottom:1px solid var(--rule);font-variant-numeric:tabular-nums;}/* Cells are right-aligned because almost every one holds a figure, and figures line up on their
   last digit. A story title is not a figure: it is prose, it wraps, and right-aligning it gives
   a ragged left edge that no two rows share, so the eye has nothing to run down. The title cell
   opts back out wherever a story link lands in one. */
:is(th,td):has(> .story-link){text-align:left;}.story-link{text-align:left;}#figure-table th:nth-child(2),#figure-table td:nth-child(2),#observatory-table th:nth-child(3),#observatory-table td:nth-child(3),#archive-table th:nth-child(3),#archive-table td:nth-child(3),#workbench-table th:nth-child(1),#workbench-table td:nth-child(1){min-width:280px;text-align:left;}tbody tr:nth-child(even){background:color-mix(in srgb,var(--ink) 2.25%,transparent);}tbody tr[data-story-key]{cursor:pointer;}@media (hover:hover){tbody tr[data-story-key]:hover{background:color-mix(in srgb,var(--selection) 7%,transparent);}}tbody tr[data-story-key]:focus-visible{outline-offset:-3px;}tbody tr.is-selected{background:color-mix(in srgb,var(--selection) 13%,transparent);box-shadow:inset 4px 0 var(--selection);}tbody a{font-weight:var(--weight-medium);text-decoration:none;}.is-unavailable{color:var(--muted);font-style:italic;}/* --target-min is the height a control RENDERS at, not a floor its container then adds to. This
   read min-height:48px on the box and min-height:48px on the label inside it, plus 4px of padding
   each side and a 1px border: 58px, sitting next to a select that composed to exactly 48. Same
   token, ten pixels apart, and the row of controls read as broken because it was.
   The label still carries the touch target — 46px inside the borders, above the 44px floor —
   and the box no longer stacks its own padding on top of it. */
.segmented-control{min-height:var(--target-min);margin:0;padding:0;display:flex;align-items:stretch;border:1px solid var(--rule-strong);}.segmented-control label{min-height:calc(var(--target-min) - 2px);padding:0 12px;display:flex;align-items:center;cursor:pointer;font-size:var(--fs-caption);font-weight:var(--weight-strong);}.segmented-control input{position:absolute;opacity:0;}.segmented-control label:has(input:checked){background:var(--ink);color:var(--paper);}.segmented-control label:has(input:focus-visible){outline:var(--focus-ring-width) solid var(--focus-ring-color);outline-offset:var(--focus-ring-offset);}.control-deck{margin-bottom:12px;padding-block:16px;display:grid;grid-template-columns:auto minmax(0,1fr);column-gap:16px;}/* Wraps rather than squeezing: the heading and its action sit side by side while both fit,
   and the action drops to its own line when they do not. Without this the heading was
   compressed to ~139px at 320px — eleven characters per line at 20px type. Wrapping is
   fluid where a breakpoint would only move the cliff. */
.control-deck-heading{display:flex;flex-wrap:wrap;align-items:end;justify-content:space-between;gap:12px 20px;}.control-deck-heading > div{flex:1 1 14ch;min-width:0;}.control-deck-heading,.control-grid{grid-column:1 / -1;}.control-deck-heading h2{font-size:var(--fs-signal-controls);}.control-grid{margin-top:12px;display:grid;grid-template-columns:repeat(6,minmax(120px,1fr));gap:10px;}/* One height for every control on the site. The observatory's deck ran at --target-compact
   while the figure and workbench decks ran at --target-min: uniform within a screen, 44 against
   48 across the site, which is the kind of deliberate that drifts. The compact tier stays for
   the header and footer chrome it was introduced for — those are navigation text, not controls,
   and raising them would undo the header slim on purpose. */
.control-grid :is(select,input[type="search"]){min-height:var(--target-min);}/* The control absorbs whatever its label's text leaves over, and that is why one select in the
   deck stood 52.25px against everything else at 44. The grid stretches every label to the
   tallest, the label is itself a grid, and its rows stretch too — so a legend wrapping to two
   lines ("HORIZONTAL AXIS") and one wrapping differently ("VERTICAL AXIS") pushed different
   amounts of slack into the control below. Nothing declared a wrong height; the leftover space
   had to go somewhere. Packing to the end sends it above the legend instead, where it costs
   nothing, and leaves every control at the height it asks for. */
.control-grid label,.figure-controls label,.workbench-controls label{align-content:end;}/* The segmented control needs no override now: the base rule already puts it at --target-min,
   which is what every control around it uses. */
.control-deck .segmented-control{grid-column:1;width:fit-content;margin-top:12px;}.data-load-line{grid-column:2;margin-top:12px;display:flex;align-items:center;align-self:center;gap:8px;color:var(--muted);font-size:var(--fs-label);}.stat-ribbon{margin-bottom:12px;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:1px;background:var(--rule);border:1px solid var(--rule);}.stat-block{min-height:78px;padding:12px 16px;background:var(--paper);}.stat-block span,.stat-block strong{display:block;}.stat-block span{color:var(--muted);font-size:var(--fs-micro);font-weight:var(--weight-strong);letter-spacing:.1em;text-transform:uppercase;}/* .kicker is the same eyebrow and had no rule at all — not here, not in tokens.css, not in any of
   the five route sheets. Twelve emission sites across briefings.js and landscape.js fell through
   to body prose: 16px, weight 400, sentence case, full ink, directly above 25px serif headings.
   Where a card happened to carry its own `p` rule it came out 12px muted instead, so one class
   rendered three ways.
   The compound selector is not decoration. `.landscape-surface-card p` and
   `.landscape-coverage .result-card p` are (0,1,1) and outrank a bare `.kicker` (0,1,0) whatever
   the source order, so those two cards would have kept overriding the size. */
.kicker,.landscape-surface-card .kicker,.landscape-coverage .result-card .kicker{margin:0;color:var(--muted);font-size:var(--fs-micro);font-weight:var(--weight-strong);letter-spacing:.1em;text-transform:uppercase;}.stat-block strong{margin-top:4px;font:var(--weight-medium) var(--step-5)/1 var(--serif);}.stat-block small{display:block;margin-top:4px;color:var(--muted);font-size:var(--fs-micro);}.observatory-grid{display:grid;grid-template-columns:minmax(0,2fr) minmax(280px,1fr);gap:16px;}.wide{min-width:0;}.legend{color:var(--muted);font-size:var(--fs-label);}.legend i{display:inline-block;width:8px;height:8px;margin:0 4px 0 10px;border-radius:var(--r-circle);}.score-swatch{background:var(--ds-front);}.comment-swatch{background:var(--ds-show);}.rank-swatch{background:var(--ds-ask);}.duration-swatch{background:var(--ds-jobs);}.interaction-hint{margin:0 0 12px;padding:12px var(--signal-surface-inset);border-left:3px solid var(--selection);background:color-mix(in srgb,var(--selection) 6%,var(--paper-raised));color:var(--muted);font-size:var(--fs-micro);}.interaction-hint strong{color:var(--ink);}.movement-bars{margin-top:20px;display:grid;gap:12px;}/* minmax on the delta tracks, not a fixed length: a null delta renders the word
   "Unavailable", whose min-content is ~60px against 44px of track. A fixed max track
   sizing function clamps the item's automatic minimum, so the value overflowed its cell,
   collided with its neighbour, and painted past the panel's right rule. */
.movement-row{width:100%;min-height:var(--target-min);padding:4px 0;display:grid;grid-template-columns:38px minmax(90px,1fr) minmax(44px,auto) minmax(44px,auto);gap:10px;align-items:center;color:inherit;background:transparent;border:0;text-align:left;cursor:pointer;font-size:var(--fs-label);}.movement-track{height:8px;background:var(--rule);}.movement-fill{display:block;height:100%;min-width:2px;background:var(--score);}.movement-comments{color:var(--comments);font-weight:var(--weight-strong);text-align:right;}.movement-row.is-selected{color:var(--selection);}.movement-row.is-selected .movement-fill{background:var(--selection);}.rank-rail{margin-top:20px;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--sp-4);}.rank-node{min-width:0;min-height:70px;padding:var(--signal-surface-inset);color:inherit;text-align:left;border:0;border-top:4px solid var(--rank);background:color-mix(in srgb,var(--rank) 8%,transparent);cursor:pointer;}.rank-node strong,.rank-node span{display:block;}.rank-node strong{font:var(--weight-medium) 19px var(--serif);}.rank-node span{margin-top:4px;color:var(--muted);font-size:var(--fs-micro);white-space:normal;overflow:visible;}.rank-node.is-selected{color:var(--selection);border-color:var(--selection);background:color-mix(in srgb,var(--selection) 12%,transparent);}.observatory-grid .interpretation-panel{grid-column:1 / -1;}.observatory-grid .table-panel{margin:0;grid-column:1 / -1;}.chart-reading{margin:16px 0 0;color:var(--muted);font-size:var(--fs-note);}/* The sticky workspace sub-bar, shared by the lens rail, the playback section nav, and the
   methods index: identical framing declared once, each surface keeping only its own grid. */
.lab-lens-nav,.playback-section-nav{position:sticky;z-index:17;top:var(--signal-header-offset);background:var(--veil);border:1px solid var(--rule-strong);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);}.viz-contract{margin-top:16px;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:1px;background:var(--rule);border:1px solid var(--rule);}.viz-contract span{padding:12px 16px;background:var(--paper-raised);color:var(--muted);font-size:var(--fs-micro);}#rank-alignment-table th:first-child,#rank-alignment-table td:first-child,#momentum-table th:first-child,#momentum-table td:first-child,#frontier-table th:first-child,#frontier-table td:first-child,#intensity-table th:first-child,#intensity-table td:first-child,#lab-trajectory-table th:nth-child(2),#lab-trajectory-table td:nth-child(2),#fingerprint-table th:nth-child(2),#fingerprint-table td:nth-child(2),#coverage-table th:nth-child(2),#coverage-table td:nth-child(2){min-width:300px;text-align:left;}.fingerprint-cell{stroke:var(--paper);stroke-width:1;}.fingerprint-missing{fill:transparent;stroke:var(--rule-contrast);stroke-dasharray:2 2;}.coverage-span{stroke:color-mix(in srgb,var(--duration) 35%,var(--rule-contrast));stroke-width:8;}.coverage-observed{stroke:var(--duration);stroke-width:4;}.briefings-header{align-items:start;}.briefings-layout{display:grid;grid-template-columns:270px minmax(0,1fr);gap:16px;align-items:start;}/* The discussion reading, held apart from the measured facts above it. The rule and the muted
   label do real work: everything in the <dl> is a figure Hacker News reported, this is a model's
   account of what people said, and they must not read as one block.
   Four rules, not seven. The first draft styled themes as bordered chips and pushed compressed
   core 207 bytes past its budget — a component every route loads, for a panel one route shows.
   The budget was right to refuse it: chips are decoration this site does not use elsewhere, and
   the themes read as well on one muted line. */
.story-reading{margin-top:var(--sp-7);padding-top:var(--sp-6);border-top:1px solid var(--rule);}.story-reading p{margin:var(--sp-4) 0 0;max-width:var(--measure);}/* The reading sits in a 218px rail, where body type gave about 28 characters a line — well under
   the 45-75 that reads comfortably, so the eye returned to the left edge every few words. The
   panel cannot get wider here, so the type comes down instead and the leading opens up to keep
   the lines distinct at the smaller size. */
/* Every line of prose in this panel, not just the summary. Sizing .story-reading-body alone left
   "Themes:" and "Read from N captured comments" at the inherited body size, so one block of
   related sentences was set in two different sizes — the summary small and its own qualifiers
   large, which reads as a mistake because it is one.
   Named individually rather than as `.story-reading p`, which would outrank .story-reading-label
   on specificity and drag the uppercase eyebrow down to caption size with them. */
.story-reading-body,.story-reading-themes,.story-reading-basis,.story-reading-absent{font-size:var(--fs-caption);line-height:1.65;}.story-reading-label{margin:0 !important;font-size:var(--fs-micro);font-weight:var(--weight-strong);letter-spacing:.08em;text-transform:uppercase;}.story-reading-label .gloss{color:inherit;font:inherit;letter-spacing:inherit;}.story-reading-label,.story-reading-themes,.story-reading-basis,.story-reading-absent{color:var(--muted);}.workbench-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px;}.workbench-controls{margin-bottom:16px;display:grid;grid-template-columns:minmax(260px,1.7fr) repeat(3,minmax(150px,1fr));gap:16px;align-items:end;}.workbench-controls h2{font-size:var(--fs-h2);}.question-card,.result-card{min-height:390px;}.question-card h2,.result-card h2{margin-top:6px;}.definition-list{margin:24px 0 0;}.definition-list div{display:grid;grid-template-columns:minmax(110px,.45fr) 1fr;gap:16px;padding:12px var(--signal-surface-inset);border-top:1px solid var(--rule);}.definition-list dt,.landscape-topic-controls label{color:var(--muted);font-size:var(--fs-label);font-weight:var(--weight-strong);letter-spacing:.08em;text-transform:uppercase;}.definition-list dd{margin:0;}.rho{color:var(--selection);font:var(--weight-medium) clamp(72px,10vw,130px)/.9 var(--serif);letter-spacing:-.07em;}.result-card .lead-copy{margin-bottom:24px;}.result-scale{height:5px;background:linear-gradient(90deg,var(--comments),var(--rule) 50%,var(--score));position:relative;}.result-scale span{position:absolute;top:50%;width:15px;height:15px;border-radius:var(--r-circle);background:var(--selection);border:3px solid var(--paper);transform:translate(-50%,-50%);left:var(--rho-position,50%);}.plain-list{margin:20px 0 0;padding:0;list-style:none;}.plain-list li{padding:12px 0;border-top:1px solid var(--rule);}.workbench-chart-panel,.workbench-table-panel{grid-column:1 / -1;}.workbench-table-panel{margin-top:0;}#workbench-calculation dd{overflow-wrap:anywhere;}.observation-archive{margin-top:16px;}.observation-intro{max-width:var(--measure);color:var(--muted);}.history-segment{padding:20px;background:var(--paper-raised);}.history-segment strong,.history-segment span{display:block;}.history-segment strong{font:var(--weight-medium) 24px var(--serif);}.history-segment span{margin-top:4px;color:var(--muted);font-size:var(--fs-label);}.history-bar{height:6px;margin-top:16px;background:var(--rule);}.history-bar i{display:block;height:100%;background:var(--duration);}.history-caveat{margin-top:20px;padding-top:20px;border-top:1px solid var(--rule);}.history-caveat h3{font:var(--weight-medium) 19px var(--serif);}.history-caveat p{color:var(--muted);font-size:var(--fs-note);}.filter-row{margin-top:20px;display:flex;flex-wrap:wrap;gap:8px;}.filter-row button{min-height:var(--target-min);padding:0 16px;color:var(--ink);background:transparent;border:1px solid var(--rule-strong);cursor:pointer;}.filter-row button[aria-pressed="true"],.filter-row button[aria-current="location"]{color:var(--paper);background:var(--ink);}.honest-empty{min-height:190px;margin-top:16px;padding:32px;display:flex;align-items:center;justify-content:center;gap:20px;border:1px dashed var(--rule-strong);color:var(--muted);}.honest-empty > span{font:var(--weight-medium) 52px var(--serif);color:var(--rule-strong);}.honest-empty h2{color:var(--ink);font-size:var(--fs-h2);}.honest-empty p{margin:8px 0 0;}.methods-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px;}.methods-grid .panel{min-height:260px;}.method-number{color:var(--selection);font:var(--weight-medium) 27px var(--serif);}.methods-grid h2{margin-top:24px;font-size:var(--fs-signal-panel);}.methods-grid p{margin-bottom:0;color:var(--muted);}.source-register{margin-top:16px;}.source-register .definition-list{max-width:none;}.method-detail,.research-register{margin-top:16px;}.method-detail,.source-register,.research-register{scroll-margin-top:0;}.method-detail table{min-width:960px;}.method-detail th,.method-detail td{text-align:left;vertical-align:top;}.method-detail th:first-child,.method-detail td:first-child{min-width:160px;}.research-grid section{padding:20px;background:var(--paper-raised);}.research-grid h3{font:var(--weight-medium) var(--step-4)/1.25 var(--serif);}.research-grid p{color:var(--muted);font-size:var(--fs-note);}.research-grid a{min-height:var(--target-min);display:inline-flex;align-items:center;font-size:var(--fs-caption);font-weight:var(--weight-strong);}.landscape-topic-option span,.section-index-list small{margin-top:4px;color:var(--muted);font-size:var(--fs-micro);line-height:1.4;}.landscape-topic-option[aria-pressed="true"],.section-index-list a[aria-current="page"]{box-shadow:inset 4px 0 var(--selection);background:color-mix(in srgb,var(--selection) 10%,var(--paper));}/* The section index is the navigation model at every width now, so its container is always live;
   only the fixed bottom route bar inside it stays narrow-only. It used to be display:none above
   1159px because the header listed every route itself — the arrangement that stopped fitting. */
.section-index-navigation{display:block;}/* The section index serves every width now, so its own rules are unscoped; only the fixed
   bottom route bar and its toggle remain inside the narrow-width block below. These lived
   in @media (max-width: 1159px) back when the header listed every route itself. */
.section-index-backdrop{position:fixed;z-index:38;inset:0;width:100%;border:0;background:color-mix(in srgb,var(--shade) 62%,transparent);cursor:pointer;}.section-index-sheet{position:fixed;z-index:40;inset:auto 0 0;max-height:min(84dvh,calc(100dvh - var(--signal-safe-top) - 12px),760px);padding:20px max(16px,var(--signal-safe-right)) calc(24px + var(--signal-safe-bottom)) max(16px,var(--signal-safe-left));overflow-y:auto;overscroll-behavior:contain;color:var(--ink);background:var(--paper);border-top:1px solid var(--rule-strong);border-radius:var(--r) var(--r) 0 0;box-shadow:0 -24px 80px color-mix(in srgb,var(--shade) 35%,transparent);}.section-index-sheet[hidden],.section-index-backdrop[hidden]{display:none;}.section-index-header{position:sticky;z-index:2;top:-20px;padding:4px 0 12px;display:flex;align-items:center;justify-content:space-between;gap:16px;background:var(--paper);}.section-index-header h2{font-size:var(--fs-signal-title-lg);}.section-index-close{min-height:var(--target-min);padding:0 12px;border:1px solid var(--rule-strong);color:var(--ink);background:var(--paper-raised);cursor:pointer;}.section-index-search{display:block;color:var(--muted);font-size:var(--fs-micro);font-weight:var(--weight-strong);letter-spacing:.08em;text-transform:uppercase;}.section-index-search input{width:100%;min-height:var(--target-min);margin-top:6px;padding:0 12px;border:1px solid var(--rule-strong);border-radius:0;color:var(--ink);background:var(--paper-raised);font-size:var(--step-3);}.section-index-list{margin-top:12px;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1px;background:var(--rule);border:1px solid var(--rule);}.section-index-group{grid-column:1 / -1;margin:0;padding:12px var(--signal-surface-inset);color:var(--muted);background:var(--paper);font-size:var(--fs-micro);font-weight:var(--weight-strong);letter-spacing:.12em;text-transform:uppercase;}.section-index-group[hidden]{display:none;}.section-index-list a{min-height:78px;padding:12px var(--signal-surface-inset);display:grid;grid-template-columns:28px minmax(0,1fr);gap:8px;align-items:start;color:var(--ink);background:var(--paper-raised);text-decoration:none;}.section-index-list a[hidden]{display:none;}.section-index-list a[data-section-index-wide]{grid-column:1 / -1;}.section-index-number{color:var(--selection);font:var(--weight-medium) 16px var(--serif);}.section-index-list strong,.section-index-list small{display:block;}.section-index-list strong{font-size:var(--fs-note);line-height:1.25;}.section-index-results{margin:12px 0 0;color:var(--muted);font-size:var(--fs-label);}body.section-index-open{overflow:hidden;}.noscript{margin:0;padding:20px;background:#8c2537;color:white;text-align:center;}@media (min-width:1160px){#methods-index{position:sticky;z-index:17;top:var(--signal-header-offset);margin-bottom:16px;padding:8px;background:var(--veil);border:1px solid var(--rule-strong);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);}#methods-index + .methods-grid{margin-top:0;}.method-detail,.source-register,.research-register{scroll-margin-top:66px;}}@media (max-width:1159px){/* The same three columns as the wide form, tightened. The header used to become a different
   layout here — links gone, status wrapped to a full second row, height 61px to 113px. With one
   control instead of four the row holds at every width, so the only thing that changes is the
   gap. */
.app-header{grid-template-columns:auto auto minmax(0,1fr);gap:8px 10px;}/* Only when the update pill is present: the pill row spans the header and the fixed status
   widths further down the cascade must not shrink-wrap it, hence the width override and the
   raised specificity. The plain capture status keeps its slot in the top row. */
.app-header .header-status:has(.pwa-update){justify-self:end;}/* was: grid-column 1 / -1 — the full second row this breakpoint exists to prevent */
.finding-grid,.methods-grid,.research-grid,.landscape-community-grid,.landscape-surface-grid{grid-template-columns:repeat(2,minmax(0,1fr));}.control-grid{grid-template-columns:repeat(3,minmax(0,1fr));}.workbench-controls{grid-template-columns:repeat(3,minmax(0,1fr));}.workbench-controls > div{grid-column:1 / -1;}/* minmax(0, 1fr), never a bare 1fr, for every one-column mobile track below.
   A grid item defaults to min-width:auto — its min-content size — and that floor beats a 1fr
   track. The verification receipt holds a 502px table, so on a 375px phone .paper-section's
   column resolved to 494px, the item refused to shrink, and the document scrolled sideways by
   159px, carrying the header and every paragraph off the left edge. The .table-scroll inside it
   could not help: it was being sized by the item rather than sizing it. The desktop rule already
   said minmax(0, 1fr); only these mobile overrides dropped it.
   The release gate did not catch it because its "mobile" scenario runs at 525px, where the block
   still fits. Nothing was auditing a phone-width viewport. */
.workspace-grid,.observatory-grid{grid-template-columns:minmax(0,1fr);}.observatory-grid .table-panel{grid-column:auto;}.selection-panel{min-height:250px;}.rank-rail{grid-template-columns:repeat(4,minmax(0,1fr));}.view{width:auto;margin-left:max(24px,var(--signal-safe-left));margin-right:max(24px,var(--signal-safe-right));}/* The footer's own controls are the last interactive row in the document, so they need
   clearance beneath them — not the 112px the fixed route bar used to require, but enough
   that they never sit flush against the document end. At 64px total the tablet profile's
   dialog openers became marginal to hit: scrollIntoView('center') cannot centre an element
   at the document end, and the click point lands within a pixel or two of the edge. */
.site-footer{padding-bottom:calc(40px + var(--signal-safe-bottom));}.site-footer nav a{min-height:28px;display:inline-flex;align-items:center;}.view{padding-bottom:72px;}.briefings-layout{grid-template-columns:minmax(0,1fr);}}@media (max-width:640px){:root{--signal-header-height:56px;}.app-header{min-height:var(--signal-header-offset);padding:calc(6px + var(--signal-safe-top)) max(16px,var(--signal-safe-right)) 6px max(16px,var(--signal-safe-left));gap:10px;grid-template-columns:minmax(0,1fr) auto;}/* A phone cannot hold brand, Sections and status in one row — measured at 375px, the three want
   448px of a usable 343. The status used to take a second row, which cost 57px of vertical space
   on every screen, above every page, to repeat a time the page states anyway: each panel carries
   its own reading chip and Methods gives the capture in full.
   So the header keeps one row and the status stands down here. Brand and Sections both stay at
   full size — the alternative that kept the clock shrank the lockup AND stopped naming the route
   a reader is on, which is the one thing a header is for.
   The skew pill is the one thing that still needs a reader to act, and its rule is below: it
   joins this row rather than opening the second one. */
.header-status:not(:has(.pwa-update)){display:none;}/* A third auto track and the pill compacted to its verb, so the row holds at 57px with the
   pill in it instead of going to 88px. Two things give and neither is the brand name: the
   subject line under it, and the pill's descriptive half — whose sentence the aria-label still
   carries in full, so a screen reader loses nothing. Keeping the whole lockup instead crushed
   "Hacker News" from 95px to 25px.
   The floor is measured, not cautious. Brand, Sections and the pill want 334px together, so
   below that they cannot share a row: at 320px they do not clip, they OVERLAP — the pill draws
   on top of Sections, which is worse than the second row it was meant to avoid. Under 360px the
   status keeps its own row. Every current phone profile is above it.
   Hiding the subject line rather than the whole lockup is also what keeps the release gate
   honest: audit.ts fails a build for nav_brand_missing when the brand link has no visible text,
   and it would never have caught this one, because a freshly opened browser has no release skew
   and so never sees this pill at all. */
@media (min-width:360px){.app-header:has(.pwa-update){grid-template-columns:auto auto auto;}.app-header:has(.pwa-update) .brand small{display:none;}.app-header .header-status:has(.pwa-update){grid-column:3;grid-row:1;justify-self:end;align-self:center;}.pwa-update span{display:none;}}.capture-status{padding-inline:var(--signal-inset);font-size:var(--fs-micro);}.capture-status{white-space:normal;}.capture-status-copy{min-width:0;}.capture-status-label{display:none;}.capture-status time{font-size:var(--fs-micro);}.view{width:auto;margin-left:max(16px,var(--signal-safe-left));margin-right:max(16px,var(--signal-safe-right));padding-top:20px;}.site-footer{margin-top:32px;padding-top:16px;padding-right:max(16px,var(--signal-safe-right));padding-left:max(16px,var(--signal-safe-left));gap:12px;}.site-footer nav{gap:4px 12px;}h1{font-size:var(--fs-signal-hero-mobile);}.paper-hero{padding-top:8px;}.paper-section{grid-template-columns:minmax(0,1fr);padding:24px 20px;}.finding-grid,.methods-grid,.workbench-grid,.history-segments,.research-grid,.stat-line{grid-template-columns:minmax(0,1fr);}.workspace-header{gap:12px;margin-bottom:16px;}.workspace-header h1,.workspace-header h2{font-size:var(--fs-signal-workspace-mobile);line-height:1;}.workspace-header .dek{margin-top:10px;font-size:var(--step-3);line-height:1.4;}.control-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;}.workbench-controls{grid-template-columns:minmax(0,1fr);}.control-deck{padding:16px 12px;grid-template-columns:minmax(0,1fr);}.control-deck-heading{align-items:end;gap:10px 12px;}.control-deck-heading > div{min-width:0;}.control-deck-heading .button{min-height:var(--target-min);padding-inline:12px;}.control-grid label{min-width:0;}.control-grid :is(select,input[type="search"]){min-width:0;padding-inline:var(--signal-surface-inset) 32px;font-size:var(--fs-note);}.control-deck .segmented-control{width:100%;}.control-deck :is(.control-deck-heading,.control-grid,.segmented-control,.data-load-line){grid-column:1;}.control-deck .segmented-control label{padding-inline:var(--signal-surface-inset);text-align:center;}.stat-ribbon{grid-template-columns:repeat(2,minmax(0,1fr));}.figure-controls{width:100%;align-items:stretch;flex-direction:column;}.chart-shell{overflow-x:auto;overscroll-behavior-inline:contain;}.chart-shell .scatter{min-width:680px;}.viz-contract{grid-template-columns:minmax(0,1fr);}.sticky-context-cell{position:sticky;z-index:2;left:0;width:clamp(176px,56vw,220px);min-width:clamp(176px,56vw,220px);max-width:clamp(176px,56vw,220px);background:var(--paper-raised);border-right:1px solid var(--rule-strong);box-shadow:12px 0 18px -18px color-mix(in srgb,var(--shade) 72%,transparent);}thead .sticky-context-cell{z-index:3;background:var(--paper);}tbody tr:nth-child(even) > .sticky-context-cell{background:color-mix(in srgb,var(--ink) 2.25%,var(--paper-raised));}@media (hover:hover){tbody tr[data-story-key]:hover > .sticky-context-cell{background:color-mix(in srgb,var(--selection) 7%,var(--paper-raised));}}tbody tr.is-selected > .sticky-context-cell{background:color-mix(in srgb,var(--selection) 13%,var(--paper-raised));}.figure-heading,.workspace-header,.intelligence-heading,.paper-end{align-items:start;flex-direction:column;}.workspace-header{display:flex;}.workspace-header .segmented-control{align-self:stretch;}.segmented-control label{flex:1;justify-content:center;}.panel{padding:20px;}.rank-rail{grid-template-columns:repeat(3,minmax(0,1fr));}.movement-row{grid-template-columns:30px minmax(60px,1fr) minmax(36px,auto) minmax(36px,auto);gap:6px;}.honest-empty{align-items:start;}.definition-list div{grid-template-columns:minmax(0,1fr);gap:4px;}th,td{padding-inline:var(--signal-surface-inset);}}@media (max-width:360px){.control-grid label:nth-child(-n + 2){grid-column:1 / -1;}}@media (max-width:420px){.section-index-list{grid-template-columns:minmax(0,1fr);}.rank-rail{grid-template-columns:repeat(2,minmax(0,1fr));}}@media (min-width:1160px){.app-header{gap:calc(var(--unit) * 2);}.section-index-sheet{inset:calc(var(--signal-header-offset) + 14px) 0 auto;width:min(720px,calc(100vw - 48px));margin-inline:auto;max-height:min(76dvh,700px);padding:20px 24px 24px;border:1px solid var(--rule-strong);border-radius:var(--r);box-shadow:0 26px 80px color-mix(in srgb,var(--shade) 28%,transparent);}.capture-status{font-size:var(--fs-micro);}.capture-status-label{display:none;}}@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto;}/* `transition-duration` alone is the widely-copied form and it is wrong: transition-property
   initially resolves to `all`, so setting a duration on every element ENABLES transitions on
   elements that never declared one. Any class or attribute change during render then creates a
   CSSTransition, which is why the release gate counted animations running under this profile in
   proportion to how busy the route was — five on observatory, one on figure — and why removing
   them one selector at a time only ever chipped at the number. `transition: none` drops the
   property itself, so no transition object can be created at all, which is what a reader asking
   for reduced motion actually means. */
*,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition:none !important;}/* The blanket rule shortens transitions; it does not stop them being created. Chart marks are
   the only elements that transition on render, so on a chart route a burst of CSSTransition
   objects exists for a few microseconds after paint — long enough for the release gate's DOM
   sample to catch six of them and report animations running under the reduced-motion profile.
   Shortening is also the wrong answer for a reader who asked for less motion: the marks should
   not transition at all. Removed at the source, which fixes the reader and the race together. */
.chart-point,.playback-scatter-point{transition:none !important;}}.hn-footer{gap:12px;padding-top:20px;}/* One wrapping line of text links. The reference routes and the three actions used to be
   two different objects — a six-column grid of links beside a three-column grid of bordered
   buttons — which made Suggest / Contact / Share read as heavier than Methods or Privacy
   though they sit at the same level. Same treatment, one row, wrapping where it must. */
@media (max-width:640px){.hn-footer{gap:12px;padding-top:16px;}}@media (prefers-contrast:more){:root{--muted:#514f49;--rule:#8d887d;--rule-strong:#55524b;--shadow:none;}:root{--focus-ring-width:4px;}.chart-grid{stroke:var(--rule-strong);}}@media (prefers-color-scheme:dark) and (prefers-contrast:more){:root{--muted:#d7d3ca;--rule:#777a70;--rule-strong:#aaa79e;}}@media (forced-colors:active){:root{--focus-ring-color:Highlight;--shadow:none;}.app-header,.lab-lens-nav,.playback-section-nav,#methods-index{-webkit-backdrop-filter:none;backdrop-filter:none;}.movement-fill,.legend i,.result-scale span,.history-bar i,.briefing-bar-track i,.trajectory-series-marker,.trajectory-table-story::before,.playback-tick,.playback-gap-band,.playback-scatter-point,.chart-point,.lab-mark,.lab-arrow,.lab-arrowhead,.trajectory-line,.trajectory-focus-point,.trajectory-coverage-segment,.landscape-theme-fill,.landscape-topic-line,.landscape-topic-dot,.landscape-topic-spark-line,.landscape-topic-spark-mark,.landscape-coverage-stack span,.landscape-coverage-bars span,.landscape-coverage-compare b{forced-color-adjust:none;}.movement-fill,.result-scale span,.history-bar i,.briefing-bar-track i{background:Highlight;border:1px solid CanvasText;box-shadow:none;}.movement-track,.briefing-bar-track,.history-bar,.result-scale,.landscape-theme-track{background:Canvas;border:1px solid CanvasText;}.legend i{width:10px;height:10px;background:Canvas;border:2px solid CanvasText;box-shadow:none;}.legend .score-swatch{background:CanvasText;}.legend .rank-swatch{border-style:dashed;}.legend .comment-swatch{border-width:3px;}.legend .duration-swatch{border-style:dotted;}.chart-grid,.chart-axis,.lab-grid-line,.lab-axis,.playback-timeline-axis,.playback-axis,.playback-axis-tick,.trajectory-axis,.trajectory-grid-line{stroke:GrayText;}.chart-point,.lab-mark,.playback-scatter-point,.trajectory-focus-point{fill:CanvasText;stroke:Canvas;}.chart-datum.is-selected .chart-point,.lab-selectable-mark.is-selected,.playback-scatter-point:focus{fill:Highlight;stroke:HighlightText;}.lab-inspector:focus,.lab-selectable-mark.is-selected,.trajectory-interaction:focus-visible{stroke:Highlight;filter:none;}[aria-current="page"],[aria-current="location"],[aria-current="step"],[aria-current="true"],[aria-pressed="true"],.segmented-control label:has(input:checked),:is(button,tr).is-selected{outline:2px solid Highlight;outline-offset:-2px;border-color:Highlight;}}@media print{.app-header,.section-index-navigation,.lab-lens-nav,.playback-section-nav,.button{display:none !important;}body{background:white;color:black;}.view{width:100%;padding:0;}.view[hidden]{display:none;}.paper-sheet{border:0;box-shadow:none;}}
