/* ============================================================================
   THE WALL — DARK INSTRUMENT
   Single shared stylesheet. Replaces the twelve duplicated <style> blocks that
   previously lived in build-*.mjs, index.html and the hand-written pages.

   Governing idea
   --------------
   The old site had one value level. Porcelain everywhere, hairline borders,
   6,000px of scroll at a single volume — nothing could be louder than anything
   else, so nothing had hierarchy. Dark Instrument splits the site into two
   registers and lets them fight:

     CHROME   near-black. Navigation, hero, section plates, data strips,
              footer. Where the machine talks. Mono, tracked, tabular.
     PAPER    porcelain. Prose, profiles, articles. Where the editor talks.
              Serif, generous leading, unchanged from the shipped guide.

   Every dark/light pair here is measured in validate-contrast.mjs — 31 checks,
   0 failures. STYLE-GUIDE.md §4 note 4 is explicit that an automatic inversion
   is not a dark theme, so nothing below was derived by flipping a value.

   Load order: this file, then any page-specific <style>. Class names from the
   pre-redesign markup (.wrap .topbar .wordmark .kicker .firmlist footer) are
   preserved and restyled, so build-script bodies keep working.
   ========================================================================== */

/* --- metric-matched fallbacks ---------------------------------------------
   font-display: swap means the page paints in the fallback and reflows when the
   web font arrives. Measured on the hub template: the dek jumped 31px on swap,
   which is 0.10 CLS on its own — over the 0.1 budget, and the single largest
   layout shift on the site. These faces are Georgia and the system mono scaled
   and re-boxed to Newsreader's and Plex Mono's metrics, so the swap is close to
   invisible and nothing moves.

   Numbers measured, not guessed — canvas measureText at 400/100px over a mixed
   ascender/descender/numeral string:
     Newsreader   width 2324.5 vs Georgia 2551.2  -> size-adjust 91.11%
                  ascent 74%, descent 27%   (Georgia: 93% / 24%)
     Plex Mono    width 2760.0 vs monospace 2769.4 -> size-adjust 99.66%
                  ascent 103%, descent 28%  (monospace: 93% / 24%) */
@font-face {
  font-family: 'Newsreader Fallback';
  /* Several names on purpose. local() matches an INSTALLED face by name, which is not the
     same mechanism as naming a family in a stack — fontconfig will happily alias the family
     `Georgia` to DejaVu Serif on Linux while local('Georgia') finds nothing, in which case
     this @font-face never matches and the whole rule silently does nothing. That is exactly
     what happened on the first attempt here: the fallback measured identical to Georgia and
     CLS did not move. Listing the Linux faces as well makes the override verifiable in CI
     and on the box this was measured on, and Georgia still wins where it exists.
     Order matters beyond availability: size-adjust corrects height, not advance width, so a
     fallback that is wider per character still re-wraps paragraphs when the real face lands.
     DejaVu Serif is the widest of these and sits last; the Times-metric faces sit ahead of it
     so a Linux or Android client wraps close to what Georgia does on Windows. */
  src: local('Georgia'), local('Times New Roman'), local('Times'),
       local('Noto Serif'), local('Tinos'), local('Liberation Serif'),
       local('Nimbus Roman'), local('DejaVu Serif');
  /* size-adjust is set from advance width, not height. Measured at 100px, the string
     'Hamburgefonstiv 0123456789' is 1408px in Newsreader and 1223px in the unadjusted
     fallback (Georgia on Windows and Liberation Serif on Linux both land on 1223), so the
     fallback runs 13% narrow and paragraphs gain a line when the real face swaps in — that
     was the whole of index.html's 0.128 CLS. 1408/1223 = 115.13%. The ascent and descent
     overrides are then divided by the same factor so the line box still renders at the
     74.4/26.6px it did before: 100 x 1.1513 x 0.6465 = 74.43. */
  size-adjust: 115.13%;
  ascent-override: 64.65%;
  descent-override: 23.10%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Plex Mono Fallback';
  src: local('Menlo'), local('Consolas'), local('Noto Sans Mono'), local('Cousine'),
       local('Liberation Mono'), local('Courier New'), local('DejaVu Sans Mono');
  size-adjust: 99.66%;
  ascent-override: 103%;
  descent-override: 28%;
  line-gap-override: 0%;
}

/* ---------------------------------------------------------------- 1. tokens */
:root { /* dark grounds — the chrome register */
  --void:      #080A0F;   /* deepest. topbar, footer, hero ground */
  --panel:     #10151F;   /* dark panels, section plates */
  --panel-2:   #19212E;   /* raised: table header rows, hovered dark rows */
  --hairline:  #232C3B;   /* rules on dark. not a text colour */
  --hairline-2:#2E3949;   /* rules on dark, one step up */

  /* light grounds — the paper register (unchanged, already validated) */
  --porcelain: #FAF9F6;
  --stone-lt:  #F2F0EA;
  --stone:     #E7E3DA;

  /* text on dark */
  --ink-inv:    #F4F6FA;  /* 18.30:1 on void */
  --body-inv:   #AEB9CC;  /* 10.00:1 on void */
  --chrome-inv: #8A96AA;  /*  6.62:1 on void */

  /* text on light (unchanged) */
  --ink:    #0E1B33;      /* 16.31:1 on porcelain */
  --body:   #3B4557;      /*  9.17:1 */
  --chrome: #686D75;      /*  4.95:1 — never below 12px, per STYLE-GUIDE §7 */
  /* Retained from the pre-redesign palette. Its one remaining use is the
     compare-view column header, which sits on --ink rather than on the new
     dark grounds, where it measures 4.88:1. Kept as a token so audit-a11y.mjs
     tracks it instead of falling back to a literal. */
  --chrome-dk: #85898F;

  /* signal */
  --cobalt:    #1B4FD8;   /* primary, light ground */
  --cobalt-lt: #7AA2FF;   /* primary, dark ground — 7.96:1 on void */
  --amber:     #FFB020;   /* secondary signal, dark only — 10.83:1 */
  --oxblood:   #6E1423;   /* editorial accent, light ground */
  --rust:      #FF7A4D;   /* editorial accent, dark ground — 7.68:1 */
  /* --amber has no paper-ground value and must never get one (§7). This bronze is its
     PAPER COUNTERPART, not a darkened amber: 6.83:1 on porcelain, 6.31:1 on stone-lt.
     Deliberately distinct from --warn (#A16207), which stays reserved for semantic
     state and always ships with a word. */
  --amber-dk:  #7A4E07;

  /* semantic — always shipped with a word, never colour alone (§7) */
  --good: #15803D; --good-inv: #4ADE80;
  --warn: #A16207; --warn-inv: #FBBF24;
  --bad:  #B91C1C; --bad-inv:  #FF6B6B;

  /* type */
  /* TWO families, per web-principles §17. IBM Plex Sans was dropped 2026-08-20:
     Newsreader already carried every heading, card name, pull-quote and the
     speakable abstract, and the mono carries every label, figure and readout,
     so the sans was a third voice with nothing of its own to say. The two that
     remain are the two registers this design is named for — the editor's serif
     and the machine's mono. */
  --serif: 'Newsreader', 'Newsreader Fallback', Georgia, 'Times New Roman', serif;
  --mono:  'IBM Plex Mono', 'Plex Mono Fallback', ui-monospace, SFMono-Regular, monospace;
  /* Alias, not a third family. index.html's inline styles and six build scripts
     still name --sans; pointing it at the serif stack keeps them resolving
     without editing eight files to say the same thing. */
  --sans:  var(--serif);

  /* rhythm */
  --gut: clamp(16px, 5vw, 48px);   /* container inline padding */
  --max: 1240px;
  --max-read: 820px;               /* prose measure */

  /* motion */
  --ease: cubic-bezier(.22, 1, .36, 1);
  --fast: .15s;
  --slow: .5s;   /* web-principles §15: section reveal <=500ms. Was .55s. */
}

/* ------------------------------------------------- 2. reset + foundation */
/* Mobile-first. Base styles target the smallest screen; complexity is added
   upward with min-width queries. No max-width cascades anywhere in this file. */

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth;
  /* stops the sticky topbar covering anchor targets */
  scroll-padding-top: 64px;
  -webkit-text-size-adjust: 100%;
}

body { font-family: var(--sans);
  background: var(--porcelain);
  color: var(--ink);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* the #1 responsive failure on this site was horizontal scroll at 320px */
  overflow-x: hidden;
}

/* Paper grain. Kept from the old build — it is the one texture that made the
   porcelain read as stock rather than as #FFF, and it survives the redesign. */
body::after { content: ''; position: fixed; inset: 0; pointer-events: none;
  z-index: 90; opacity: .028;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

img, video, iframe, svg, canvas { max-width: 100%; height: auto; }
table { border-collapse: collapse; width: 100%; }
a { color: var(--cobalt); }
::selection { background: var(--cobalt); color: #fff; }
:focus-visible { outline: 2px solid var(--cobalt); outline-offset: 2px; border-radius: 2px; }
.on-dark :focus-visible, .di-dark :focus-visible { outline-color: var(--cobalt-lt); }

/* skip link — was missing sitewide */
.skip { position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--void); color: var(--ink-inv);
  font-family: var(--mono); font-size: 12px; letter-spacing: .1em;
  padding: 12px 16px; text-decoration: none;
}
.skip:focus { left: 8px; top: 8px; }

/* container — one class, every section uses it */
.wrap { width: 100%; max-width: var(--max); margin-inline: auto; padding-inline: var(--gut); }
.wrap-read { width: 100%; max-width: var(--max-read); margin-inline: auto; padding-inline: var(--gut); }

/* grid utilities — collapse to 1 column, expand at breakpoints.
   Pages compose these; they do not write their own media queries. */
.grid-2, .grid-3, .grid-4 { display: grid; gap: 1px; grid-template-columns: 1fr; }
@media (min-width: 640px)  { .grid-2, .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .grid-3 { grid-template-columns: repeat(3, 1fr); }
                             .grid-4 { grid-template-columns: repeat(4, 1fr); } }

/* fluid type — sized once here, never per page */
h1 { font-family: var(--serif); font-weight: 600; letter-spacing: -.025em; line-height: 1.02;
     font-size: clamp(38px, 8vw, 92px); }
h2 { font-family: var(--serif); font-weight: 600; letter-spacing: -.015em; line-height: 1.12;
     font-size: clamp(24px, 3.4vw, 38px); }
h3 { font-family: var(--serif); font-weight: 600; letter-spacing: -.01em; line-height: 1.2;
     font-size: clamp(18px, 2.2vw, 23px); }
p, li { font-size: clamp(16px, 1.05vw, 18px); line-height: 1.72; color: var(--body); }
p { margin-bottom: 12px; }
strong { color: var(--ink); font-weight: 600; }

/* mono label — the site's structural voice. uppercase + tracked, or it isn't mono (§7) */
.mono-k, .kicker, .eyebrow { font-family: var(--mono); font-weight: 600;
  font-size: 12px; letter-spacing: .16em; text-transform: uppercase;
}
.tnum, .num { font-family: var(--mono); font-feature-settings: 'tnum'; font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------- 3. dark surfaces */
/* .di-dark flips the text tokens so any component can sit on the chrome
   register without restating colours. This is the only inversion mechanism —
   individual components never hand-pick an inverse value. */
.di-dark { background: var(--void);
  color: var(--ink-inv);
  --ink: var(--ink-inv);
  --body: var(--body-inv);
  --chrome: var(--chrome-inv);
  --cobalt: var(--cobalt-lt);
  --oxblood: var(--rust);
  --stone: var(--hairline);
  --stone-lt: var(--hairline-2);
  --good: var(--good-inv);
  --warn: var(--warn-inv);
  --bad: var(--bad-inv);
}
.di-dark p, .di-dark li { color: var(--body-inv); }
.di-dark strong { color: var(--ink-inv); }
.di-panel { background: var(--panel); }

/* ------------------------------------------------------------ 4. topbar */
.topbar { background: var(--void);
  position: sticky; top: 0; z-index: 40;
  border-bottom: 1px solid var(--hairline);
}
/* the signal rule. flat colour stops, no gradient (§7 forbids gradients) */
.topbar::before { content: ''; display: block; height: 2px;
  background: linear-gradient(90deg,
    var(--cobalt-lt) 0 58%, var(--rust) 58% 82%, var(--amber) 82% 100%);
}
.topbar-in { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; min-height: 60px; flex-wrap: wrap;
}
.wordmark { font-family: var(--serif); font-weight: 700; font-size: 21px;
  letter-spacing: -.01em; text-decoration: none; color: var(--ink-inv);
  display: flex; align-items: center; gap: 12px; flex-shrink: 0;
}
.wordmark small { font-family: var(--mono); font-weight: 600; font-size: 12px;
  letter-spacing: .18em; color: var(--chrome-inv); text-transform: uppercase;
}
/* the mark keeps its cobalt tile on every ground (§5: never recoloured).
   cobalt on void is 2.98:1, under the 3:1 non-text bar, so on dark chrome it
   carries a keyline — the keyline is what has to be discernible, at 7.96:1. */
.wordmark .brand-mark { flex-shrink: 0; border-radius: 4px;
  box-shadow: 0 0 0 1px var(--cobalt-lt);
}
.db-live { display: flex; align-items: center; gap: 8px;
  font-family: var(--mono); font-size: 12px; letter-spacing: .1em;
  color: var(--chrome-inv); text-transform: uppercase;
}
.db-live i { width: 6px; height: 6px; border-radius: 50%;
  background: var(--good-inv); animation: pulse 2.4s infinite;
}
@keyframes pulse { 0%, 100% { opacity: 1 } 50% { opacity: .25 } }

/* nav — styles moved here out of nav.js so the menu no longer arrives unstyled */
.nav-main { display: flex; gap: 2px; align-items: center; margin-left: auto; }
.nav-group { position: relative; }
.nav-trigger { font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .1em;
  color: var(--body-inv); background: none; border: 0; cursor: pointer;
  padding: 8px 12px; text-transform: uppercase; border-radius: 4px;
  display: inline-flex; align-items: center; gap: 4px;
}
.nav-trigger:hover, .nav-group.open .nav-trigger { color: var(--ink-inv); background: var(--panel-2); }
/* Chevron is an SVG, not the ▾ glyph — sized in px so it can grow without
   dragging the label's line-height with it. */
.nav-trigger .caret { width: 15px; height: 15px; flex-shrink: 0; opacity: .75;
  transition: transform var(--fast), opacity var(--fast);
}
.nav-trigger:hover .caret, .nav-group.open .nav-trigger .caret { opacity: 1; }
.nav-group.open .nav-trigger .caret { transform: rotate(180deg); }

/* ---- mega menu ----
   Per-group accent, taken from the masthead rule's three stops so the menu is
   coloured by the same system as the 2px line above it. Colour is never the only
   signal: every row carries an icon and a label (§7). */
.nav-group { --acc: var(--cobalt-lt); }
.nav-group[data-accent="amber"] { --acc: var(--amber); }
.nav-group[data-accent="rust"]  { --acc: var(--rust); }

.nav-menu { display: none; position: absolute; top: calc(100% + 10px); right: 0;
  width: min(648px, calc(100vw - var(--gut) * 2));
  background: var(--panel); border: 1px solid var(--hairline-2);
  border-radius: 10px; z-index: 50; overflow: hidden;
  box-shadow: 0 18px 44px rgba(0, 0, 0, .55);
}
/* the accent hairline that ties the panel to its trigger */
.nav-menu::before { content: ''; display: block; height: 2px; background: var(--acc);
}
/* same drafting grid as the hero, at the same 5.5% — texture, not decoration */
.mega-in { display: grid; grid-template-columns: 1fr 194px;
  background-image:
    linear-gradient(to right, rgba(122, 162, 255, .05) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(122, 162, 255, .05) 1px, transparent 1px);
  background-size: 44px 44px;
}
.nav-group.open .nav-menu { display: block; animation: menuIn .18s var(--ease) both; }
@keyframes menuIn { from { opacity: 0; transform: translateY(-6px) } to { opacity: 1; transform: none } }

.mega-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; padding: 8px; }
.mega-item { display: flex; gap: 12px; align-items: flex-start;
  padding: 8px 8px; border-radius: 6px; text-decoration: none;
}
.mega-item:hover { background: var(--panel-2); }
.mi-ic { display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex-shrink: 0; border-radius: 6px;
  background: var(--void); border: 1px solid var(--hairline);
  color: var(--acc);
}
.mega-item:hover .mi-ic { border-color: var(--acc); background: #0B0E15; }
.mi-svg { width: 20px; height: 20px; display: block; }
.mi-tx { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.mi-tx b { font-family: var(--sans); font-size: 16px; font-weight: 600;
  color: var(--ink-inv); line-height: 1.35;
}
.mi-tx small { font-family: var(--mono); font-size: 12px; letter-spacing: .02em;
  color: var(--chrome-inv); line-height: 1.4;
}
.mega-item:hover .mi-tx b { color: var(--acc); }

.mega-aside { display: flex; flex-direction: column; gap: 4px; justify-content: flex-start;
  padding: 16px 16px 16px; text-decoration: none;
  border-left: 1px solid var(--hairline); background: rgba(8, 10, 15, .55);
}
.mega-aside:hover { background: var(--void); }
.ma-k { font-family: var(--mono); font-size: 12px; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: var(--acc);
}
.ma-t { font-family: var(--serif); font-size: 17px; font-weight: 600; color: var(--ink-inv); line-height: 1.25; }
.ma-d { font-family: var(--sans); font-size: 16px; color: var(--body-inv); line-height: 1.5; }
.ma-go { margin-top: auto; padding-top: 12px;
  display: inline-flex; align-items: center; gap: 4px;
  font-family: var(--mono); font-size: 12px; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; color: var(--acc);
}
.ma-go svg { width: 14px; height: 14px; flex-shrink: 0; transition: transform var(--fast); }

/* Mid-width desktop. The panel is anchored to the right edge of its own trigger, so
   the LEFTMOST group has the least room — at 861px a 648px panel starts at -144px and
   gets silently clipped by body{overflow-x:hidden}. Below ~1200px the panel narrows and
   the aside stacks under the grid instead of sitting beside it. Found by sweeping
   viewports with each menu open, not by eye: nothing scrolls, so nothing looks wrong. */
@media (min-width: 861px) and (max-width: 1199px) { .nav-menu { width: min(430px, calc(100vw - var(--gut) * 2)); }
  .mega-in { grid-template-columns: 1fr; }
  .mega-aside { border-left: 0; border-top: 1px solid var(--hairline);
    padding: 12px 16px 16px;
 }
  .ma-go { margin-top: 8px; padding-top: 0; }
}
.mega-aside:hover .ma-go svg { transform: translateX(3px); }
.nav-toggle { display: none; background: none; border: 0; font-size: 22px;
  color: var(--ink-inv); cursor: pointer; margin-left: auto;
  line-height: 1; padding: 8px; min-width: 44px; min-height: 44px;
}
@media (max-width: 860px) { .topbar-in { position: relative; }
  .nav-toggle { display: block; }
  .nav-main { display: none; flex-direction: column; align-items: stretch; gap: 0;
    /* .topbar-in IS the .wrap, so an absolutely-positioned child is already laid out
       against its padding box — edge to edge. The old `-gut` bleed therefore pushed a
       full gutter off BOTH sides, clipping the first characters of every group label
       and putting the chevron past the right edge. Inset padding goes on the rows. */
    position: absolute; top: 100%; left: 0; right: 0;
    background: var(--panel); border-top: 1px solid var(--hairline);
    border-bottom: 1px solid var(--hairline);
    padding: 4px 0; z-index: 40; max-height: 72vh; overflow-y: auto;
 }
  .nav-main.open { display: flex; }
  .nav-group { border-bottom: 1px solid var(--hairline); }
  .nav-group:last-child { border-bottom: none; }
  .nav-trigger { width: 100%; text-align: left; padding: 16px var(--gut); justify-content: space-between; border-radius: 0; }
  .nav-trigger .caret { width: 18px; height: 18px; }
  .nav-menu { display: none; position: static; border: 0; border-radius: 0;
    background: var(--void); width: auto; box-shadow: none; overflow: visible;
 }
  /* one column, and the aside becomes the last row rather than a side rail */
  .mega-in { grid-template-columns: 1fr; background-image: none; }
  .mega-grid { grid-template-columns: 1fr; gap: 0; padding: 4px 0 8px; }
  .mega-item { padding: 8px var(--gut); border-radius: 0; min-height: 44px; align-items: center; }
  .mi-ic { width: 30px; height: 30px; }
  .mi-tx b { font-size: 16px; }
  .mega-aside { border-left: 0; border-top: 1px solid var(--hairline);
    padding: 16px var(--gut); min-height: 44px;
 }
  .ma-go { margin-top: 8px; padding-top: 0; }
}

/* -------------------------------------------------------------- 5. hero */
.di-hero { background: var(--void); color: var(--ink-inv); position: relative; overflow: hidden; }
/* drafting grid. 1px lines at 4% — reads as texture, not as decoration */
.di-hero::before { content: ''; position: absolute; inset: 0; pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(122,162,255,.055) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(122,162,255,.055) 1px, transparent 1px);
  background-size: 56px 56px;
  mask-image: radial-gradient(ellipse 80% 70% at 30% 40%, #000 30%, transparent 100%);
}
.di-hero-in { position: relative; display: grid; gap: 32px;
  grid-template-columns: 1fr;
  padding-block: clamp(48px, 7vw, 96px) clamp(32px, 5vw, 64px);
}
@media (min-width: 1024px) { .di-hero-in { grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr); align-items: center; gap: 48px; }
}
.di-hero .kicker { color: var(--rust); display: flex; align-items: center; gap: 8px; margin-bottom: 16px; }
.di-hero .kicker::before { content: '+'; color: var(--chrome-inv); font-weight: 400; }
.di-hero h1 { color: var(--ink-inv); }
.di-hero h1 em { font-style: italic; font-weight: 500; color: var(--rust); }
.di-hero .lede { margin-top: 24px; font-size: clamp(16px, 1.2vw, 18px); line-height: 1.65;
  color: var(--body-inv); max-width: 54ch;
}
.di-hero .lede strong { color: var(--ink-inv); font-weight: 600; }

.trust-chips { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 24px; }
.trust-chip { display: inline-flex; align-items: center; gap: 4px;
  font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .1em;
  color: var(--body-inv); background: var(--panel);
  border: 1px solid var(--hairline-2); border-radius: 20px;
  padding: 8px 12px; text-transform: uppercase;
}
.trust-chip::before { content: '✓'; color: var(--good-inv); font-weight: 700; }

.searchbox { margin-top: 24px; display: flex; align-items: center; gap: 12px;
  background: var(--panel); border: 1px solid var(--hairline-2);
  border-radius: 6px; padding: 16px 16px; max-width: 560px;
}
.searchbox:focus-within { border-color: var(--cobalt-lt); box-shadow: 0 0 0 3px rgba(122,162,255,.14); }
.searchbox svg { color: var(--chrome-inv); flex-shrink: 0; }
.searchbox input { border: 0; outline: 0; width: 100%; background: transparent;
  font-family: var(--mono); font-size: 16px; letter-spacing: .02em; color: var(--ink-inv);
}
.searchbox input::placeholder { color: var(--chrome-inv); }

/* ------------------------------------------------- 6. the instrument panel */
/* Replaces the radial dial that never rendered. A readout, not an ornament:
   every figure here is computed and carries its source line. */
.di-readout { border: 1px solid var(--hairline-2); background: var(--panel); border-radius: 10px; overflow: hidden; }
.di-readout-hd { display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 12px 16px; border-bottom: 1px solid var(--hairline);
  background: var(--panel-2);
}
.di-readout-hd .t { font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .16em; color: var(--chrome-inv); text-transform: uppercase; }
.di-readout-hd .v { font-family: var(--mono); font-size: 12px; color: var(--good-inv); letter-spacing: .1em; }
.di-readout-bd { padding: 16px; }
.di-bar { display: grid; grid-template-columns: 68px 1fr auto; gap: 12px; align-items: center; padding: 4px 0; }
.di-bar .lbl { font-family: var(--mono); font-size: 12px; color: var(--chrome-inv); text-align: right; }
.di-bar .track { display: block; height: 9px; background: var(--void); border-radius: 2px; overflow: hidden; }
.di-bar .fill { /* display:block is load-bearing: these are <span>s, and an inline box
     ignores width and height, so the fill measured 0px wide at every value. */
  display: block; height: 100%; background: var(--cobalt-lt); border-radius: 0 2px 2px 0;
  transform-origin: left center; transform: scaleX(var(--pct, 0));
}
.di-bar.peak .fill { background: var(--amber); }
.di-bar .val { font-family: var(--mono); font-size: 12px; color: var(--body-inv); font-variant-numeric: tabular-nums; }
.di-src { font-family: var(--mono); font-size: 12px; letter-spacing: .08em; color: var(--chrome-inv); text-transform: uppercase; margin-top: 12px; line-height: 1.6; }
.di-src a { color: var(--cobalt-lt); text-decoration: none; }
.di-src a:hover { color: var(--amber); }

/* When a bar is also a filter control it renders as a <button>. Reset the UA
   styling and give it a real hit area — the label column widens because
   discipline names ("Marketing Automation") do not fit the 68px numeric gutter. */
button.di-bar { width: 100%; background: none; border: 0; cursor: pointer;
  grid-template-columns: minmax(96px, 34%) 1fr auto;
  padding: 8px 4px; margin-inline: -4px; border-radius: 4px;
  min-height: 34px; text-align: left; font: inherit;
}
button.di-bar:hover { background: var(--panel-2); }
button.di-bar .lbl { text-align: left; text-transform: uppercase; letter-spacing: .08em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
button.di-bar:hover .lbl { color: var(--ink-inv); }
button.di-bar:hover .fill { background: var(--amber); }
.di-reset { display: inline-block; margin-top: 12px; background: none;
  border: 1px solid var(--hairline-2); border-radius: 5px;
  font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .1em;
  color: var(--body-inv); text-transform: uppercase; padding: 8px 12px;
  cursor: pointer; min-height: 36px;
}
.di-reset:hover { border-color: var(--cobalt-lt); color: var(--cobalt-lt); }

/* ------------------------------------------------------ 7. stat register */
.di-stats { display: grid; grid-template-columns: 1fr; gap: 1px; background: var(--hairline); border-block: 1px solid var(--hairline); }
@media (min-width: 640px)  { .di-stats { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .di-stats { grid-template-columns: repeat(4, 1fr); } }
.di-stat { background: var(--void); padding: 24px clamp(16px, 2vw, 24px); }
.di-stat b { display: block; font-family: var(--mono); font-weight: 600;
  font-size: clamp(30px, 3.6vw, 44px); color: var(--ink-inv);
  letter-spacing: -.02em; font-variant-numeric: tabular-nums; line-height: 1;
}
.di-stat .cap { display: block; margin-top: 8px; font-size: 16px; line-height: 1.5; color: var(--body-inv); }
.di-stat .cap strong { color: var(--ink-inv); }

/* -------------------------------------------------------- 8. section plate */
/* Numbered dark plate that opens each sector. This is the element that gives
   the long scroll a pulse — the page alternates plate (dark) / rows (paper). */
.plate { background: var(--void); color: var(--ink-inv); }
.plate-in { display: flex; align-items: center; gap: clamp(12px, 2vw, 24px);
  padding-block: clamp(16px, 2.6vw, 32px); flex-wrap: wrap;
}
.plate .no { font-family: var(--mono); font-weight: 600; font-size: clamp(11px, 1.1vw, 13px);
  letter-spacing: .18em; color: var(--void);
  background: var(--amber); padding: 4px 8px; border-radius: 3px; flex-shrink: 0;
}
.plate h2 { color: var(--ink-inv); margin: 0; flex-shrink: 0; }
.plate .count { font-family: var(--mono); font-size: 12px; letter-spacing: .14em;
  color: var(--chrome-inv); text-transform: uppercase;
}
.plate .rule { flex: 1 1 60px; height: 1px; background: var(--hairline-2); min-width: 24px; }
.plate .all { font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .12em;
  color: var(--cobalt-lt); text-decoration: none; text-transform: uppercase;
  white-space: nowrap; padding: 8px 0;
}
.plate .all:hover { color: var(--amber); }

/* ----------------------------------------------------- 9. the vendor rows */
/* Replaces the 4-up card grid. The old cards were identical white rectangles
   at 3,460 repetitions — the exact "wading through listicles" texture the
   brand exists to refute, and their titles collided with the compare
   checkbox at 4-up. A row is scannable, comparable, and cannot collide:
   every field owns a column. */
.rows { border-top: 1px solid var(--stone); }
.rows-hd { display: none;
  font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .14em;
  color: var(--chrome); text-transform: uppercase;
  background: var(--stone-lt); border-bottom: 1px solid var(--stone);
}
.row { display: grid; gap: 4px 12px; align-items: center;
  grid-template-columns: 1fr auto;
  grid-template-areas: 'name  act' 'meta meta';
  padding: 12px 12px;
  border-bottom: 1px solid var(--stone);
  text-decoration: none; position: relative;
}
.rows-hd, .row { padding-inline: clamp(8px, 1.4vw, 16px); }
.row:hover { background: var(--stone-lt); }
/* the cobalt tick that appears on hover — affordance, not decoration */
.row::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--cobalt); transform: scaleY(0); transform-origin: center;
  transition: transform var(--fast) var(--ease);
}
.row:hover::before, .row:focus-visible::before { transform: scaleY(1); }

.row .nm { grid-area: name; font-family: var(--serif); font-weight: 600; font-size: 17px; color: var(--ink); line-height: 1.25; }
.row:hover .nm { color: var(--cobalt); }
.row .meta { grid-area: meta; display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: baseline; }
.row .cat { font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .12em; color: var(--cobalt); text-transform: uppercase; }
.row .loc { font-family: var(--mono); font-size: 12px; color: var(--chrome); }
.row .rate { font-family: var(--mono); font-size: 12px; color: var(--body); font-variant-numeric: tabular-nums; }
.row .rate.none { color: var(--chrome); }
.row .go { grid-area: act; font-family: var(--mono); font-size: 13px; color: var(--chrome); transition: transform var(--fast); }
.row:hover .go { color: var(--cobalt); transform: translateX(3px); }

/* At 768px+ the row becomes a real table: one line, columns aligned down the
   page so rates and states can be compared by eye without reading. */
@media (min-width: 768px) { .rows-hd { display: grid; }
  .rows-hd, .row { grid-template-columns: minmax(0, 2.4fr) minmax(0, 1.5fr) 96px 104px 28px;
    grid-template-areas: 'name cat loc rate act';
    gap: 12px; align-items: center;
 }
  .rows-hd { padding-block: 8px; }
  .row { padding-block: 12px; }
  .row .meta { display: contents; }
  .row .cat  { grid-area: cat; }
  .row .loc  { grid-area: loc; }
  .row .rate { grid-area: rate; text-align: right; }
  .row .go   { text-align: right; }
  .row .nm, .row .cat { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

/* --- 9b. compare variant -------------------------------------------------
   The atlas rows carry a compare checkbox. The old card positioned it
   absolutely over the title, which is why "Absolute Marketing" and "57 Clicks
   Marketing Agency" rendered with the word COMPARE on top of them at 4-up.
   Here it owns a column, so overlap is structurally impossible.

   The row is a <div>, not an <a>, because a checkbox inside an anchor is not
   operable. The name link stretches a transparent overlay across the row for
   the click target; the checkbox sits above it on z-index. One link, one
   checkbox, both independently focusable. */
.row--cmp { position: relative; }
.row--cmp .nm { text-decoration: none; color: var(--ink); }
.row--cmp .nm::after { content: ''; position: absolute; inset: 0; z-index: 1; }
.row--cmp .cmp { position: relative; z-index: 2; grid-area: cmp;
  display: inline-flex; align-items: center; gap: 4px; cursor: pointer;
  font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .12em;
  color: var(--chrome); text-transform: uppercase;
  padding: 4px 8px; border: 1px solid var(--stone); border-radius: 5px;
  background: var(--porcelain);
  min-height: 32px;
}
.row--cmp .cmp:hover { color: var(--cobalt); border-color: var(--cobalt); }
.row--cmp .cmp input { width: 13px; height: 13px; margin: 0; cursor: pointer; accent-color: var(--cobalt); }
.row--cmp { grid-template-columns: 1fr auto;
  grid-template-areas: 'name cmp' 'meta meta';
}
@media (min-width: 768px) { .rows-hd.has-cmp, .row--cmp { /* loc and rate are fixed and generous enough for "San Francisco, CA" and
       "$200–$300" on one line. At 92/100px both wrapped to two lines, which
       made every row a different height and destroyed the column scan that is
       the whole reason for using rows. */
    grid-template-columns: minmax(0, 2fr) minmax(0, 1.35fr) 136px 104px 108px;
    grid-template-areas: 'name cat loc rate cmp';
 }
  .row--cmp .cmp { justify-self: end; }
  .row--cmp .loc, .row--cmp .rate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}

/* ------------------------------------------------------- 10. signal strip */
.strip { background: var(--panel); border-block: 1px solid var(--hairline-2); }
.strip .wrap { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding-block: 12px; flex-wrap: wrap;
}
.strip .msg { font-size: 16px; font-weight: 500; color: var(--body-inv); }
.strip .msg strong { color: var(--ink-inv); font-weight: 600; }
.strip .msg .accent { color: var(--amber); font-weight: 700; font-family: var(--mono); font-size: 12.5px; letter-spacing: .04em; }
.strip a.act { font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .12em;
  color: var(--ink-inv); text-decoration: none; padding: 8px 16px;
  border: 1px solid var(--hairline-2); border-radius: 4px; text-transform: uppercase;
  white-space: nowrap;
}
.strip a.act:hover { background: var(--cobalt-lt); border-color: var(--cobalt-lt); color: var(--void); }

/* --------------------------------------------------------- 11. paper well */
/* Prose and profiles. Unchanged register from the shipped guide — the
   redesign deliberately leaves reading surfaces alone. */
.well { background: var(--porcelain); }
.well h2 { color: var(--ink); margin: 32px 0 12px; }
.well h3 { color: var(--ink); margin: 24px 0 8px; }
.dek { font-family: var(--serif); font-style: italic; font-size: clamp(17px, 1.5vw, 20px); line-height: 1.55; color: var(--body); }
.pull { border-left: 3px solid var(--oxblood); padding: 4px 0 4px 16px; margin: 24px 0;
  font-family: var(--serif); font-style: italic; font-size: clamp(19px, 1.8vw, 24px);
  line-height: 1.4; color: var(--ink);
}
.pull cite { display: block; margin-top: 8px; font: 600 10px/1 var(--mono); letter-spacing: .16em; color: var(--chrome); font-style: normal; text-transform: uppercase; }

/* data tables on paper */
.firmlist { border: 1px solid var(--stone); border-radius: 10px; background: #fff; overflow: hidden; margin: 16px 0; }
.firmlist table { width: 100%; }
.firmlist th, .firmlist td { padding: 12px 16px; border-bottom: 1px solid var(--stone-lt); text-align: left; font-size: 16px; }
.firmlist th { font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .14em; color: var(--chrome); text-transform: uppercase; background: var(--stone-lt); }
.firmlist td a { color: var(--ink); text-decoration: none; font-weight: 600; }
.firmlist td a:hover { color: var(--cobalt); }
.firmlist td.rate { color: var(--body); font-family: var(--mono); font-size: 12px; }
.firmlist td.city { color: var(--chrome); font-size: 16px; }
.firmlist tr:last-child td { border-bottom: none; }
/* tables must scroll inside themselves, never scroll the page */
.scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* buttons */
.btn { display: inline-block; font-family: var(--mono); font-size: 12px; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; text-decoration: none;
  padding: 12px 24px; border-radius: 5px; border: 1px solid transparent;
  min-height: 44px; cursor: pointer;
}
.btn-pri { background: var(--cobalt); color: #fff; }
.btn-pri:hover { background: var(--ink); }
.di-dark .btn-pri { background: var(--cobalt-lt); color: var(--void); }
.di-dark .btn-pri:hover { background: var(--amber); }
.btn-sec { border-color: var(--cobalt); color: var(--cobalt); background: transparent; }
.btn-sec:hover { background: var(--cobalt); color: #fff; }

/* ------------------------------------------------------------ 12. footer */
footer.site { background: var(--void); color: var(--body-inv);
  border-top: 1px solid var(--hairline); margin-top: 64px;
  padding-block: 32px 48px;
}
footer.site .wrap { display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
footer.site span, footer.site a { font-family: var(--mono); font-size: 12px; letter-spacing: .1em;
  color: var(--chrome-inv); text-decoration: none; text-transform: uppercase;
}
footer.site a:hover { color: var(--ink-inv); }
/* agent-facing nudge — small and low-contrast (not hidden). Visible to humans
   who look; agents that read the title attribute get the routing instructions. */
footer.site a.agent-nudge { opacity: .55; font-size: 10px; letter-spacing: .06em; text-transform: lowercase; }
footer.site a.agent-nudge:hover { opacity: 1; }

/* ------------------------------------------------------------ 13. motion */
/* Reversal of MOTION-PROFILE.md's motion_enabled:false, on operator
   instruction 2026-08-11. See MOTION-PROFILE.md for the current spec and
   MOTION-PROFILE.previous.md for the decision this replaces.
   Everything below is opacity/transform only — no layout-affecting property
   is animated, so none of it can cause reflow or CLS. */

/* staged page-load reveal. .di-in elements start hidden ONLY when JS has
   confirmed it will animate them (html.di-motion), so a JS failure or a
   crawler leaves every element visible and Ctrl-F-able. */
.di-motion .di-in { opacity: 0; transform: translateY(14px); }
.di-motion .di-in.lit { opacity: 1; transform: none;
  transition: opacity var(--slow) var(--ease), transform var(--slow) var(--ease);
  transition-delay: var(--d, 0ms);
}

/* chart bars draw from zero when their panel enters view. A bar drawing is
   the chart rendering itself; it does not restate the figure as rhetoric.
   Headline numerals deliberately do NOT count up — a sourced fact should not
   perform. That is the one clause of the old motion profile kept on merit. */
.di-motion .di-bar .fill { transform: scaleX(0); }
.di-motion .di-bar.lit .fill { transition: transform var(--slow) var(--ease); transition-delay: var(--d, 0ms); transform: scaleX(var(--pct, 0)); }

@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; }
  .di-motion .di-in { opacity: 1 !important; transform: none !important; }
  .di-motion .di-bar .fill { transform: scaleX(var(--pct, 0)) !important; }
  *, *::before, *::after { animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
 }
}

/* --------------------------------------------------------- 14. utilities */
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
.hide-sm { display: none; }
@media (min-width: 640px) { .hide-sm { display: initial; } }
@media print { .topbar, footer.site, .strip, .nav-main { display: none; }
  body, .di-hero, .plate, .di-stats { background: #fff !important; color: #000 !important; }
}

/* ==========================================================================
   15. ARTICLE PAGE TYPES
   Pillars, hubs, entities, questions, news, compare, find, states, cities,
   wins, updates, trust. These twelve page types previously carried twelve
   near-identical copies of the rules below, one per build-*.mjs. They are the
   paper register: a dark masthead opens the page, then the reading column.
   ========================================================================== */

/* --- dark masthead --------------------------------------------------------
   Every article opens in the chrome register and then drops to paper. That
   single switch is what stops 2,600 article pages reading as one undifferen-
   tiated beige scroll — you always know where the top of a page is. */
.art-hd { background: var(--void); color: var(--ink-inv); border-bottom: 1px solid var(--hairline); position: relative; overflow: hidden; }
.art-hd::before { content: ''; position: absolute; inset: 0; pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(122,162,255,.05) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(122,162,255,.05) 1px, transparent 1px);
  background-size: 48px 48px;
  mask-image: radial-gradient(ellipse 70% 80% at 20% 30%, #000 20%, transparent 100%);
}
.art-hd-in { position: relative; padding-block: clamp(32px, 4.5vw, 48px) clamp(24px, 3.5vw, 48px); }
.art-hd .kicker { color: var(--rust); display: block; margin-bottom: 16px; }
.art-hd h1 { color: var(--ink-inv); font-size: clamp(30px, 5vw, 58px); }
.art-hd .dek { color: var(--body-inv); margin-top: 16px; max-width: 62ch; }
.art-hd .crumb { color: var(--chrome-inv); margin-bottom: 16px; }
.art-hd .crumb a { color: var(--chrome-inv); }
.art-hd .crumb a:hover { color: var(--cobalt-lt); }
.art-hd .byline { color: var(--chrome-inv); border-color: var(--hairline); }
.art-hd .statline { background: var(--panel); border-color: var(--hairline-2); }
.art-hd .statline div b { color: var(--ink-inv); }
.art-hd .statline div span { color: var(--chrome-inv); }

/* the reading column */
main.wrap, main.wrap-read,
main > .wrap, main > .wrap-read { padding-top: clamp(24px, 3.5vw, 48px); }
.dek { font-family: var(--serif); font-style: italic; font-size: clamp(16.5px, 1.5vw, 19px); line-height: 1.55; color: var(--body); max-width: 62ch; }
.sub { color: var(--body); }
.back, .crumb { font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .12em;
  color: var(--cobalt-lt); text-decoration: none; text-transform: uppercase;
  display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.topbar .back { color: var(--body-inv); }
.topbar .back:hover { color: var(--ink-inv); }
main .back, main .crumb { color: var(--chrome); }
main .back:hover, main .crumb a:hover { color: var(--cobalt); }
.byline { font-family: var(--mono); font-size: 12px; letter-spacing: .12em; color: var(--chrome); text-transform: uppercase; padding: 12px 0; border-top: 1px solid var(--stone); border-bottom: 1px solid var(--stone); margin: 16px 0; display: flex; gap: 16px; flex-wrap: wrap; }
.meta, .fn, .url, .m, .d { font-family: var(--mono); font-size: 12px; color: var(--chrome); letter-spacing: .06em; }

/* --- callout blocks ------------------------------------------------------- */
.cta { display: inline-block; font-family: var(--mono); font-size: 12px; font-weight: 600;
  letter-spacing: .12em; color: #fff; background: var(--cobalt); border-radius: 6px;
  padding: 12px 16px; text-decoration: none; margin: 16px 0 8px; text-transform: uppercase;
  min-height: 44px;
}
.cta:hover { background: var(--ink); }
.art-hd .cta { background: var(--cobalt-lt); color: var(--void); }
.art-hd .cta:hover { background: var(--amber); }

.faq-item { border: 1px solid var(--stone); border-left: 3px solid var(--cobalt); border-radius: 0 8px 8px 0; background: #fff; padding: 16px 24px; margin: 12px 0; }
.faq-item h3 { margin: 0 0 8px; font-size: 16px; }
.faq-item p { margin: 0; font-size: 16px; }

.statline { display: flex; gap: clamp(16px, 3vw, 32px); flex-wrap: wrap;
  border: 1px solid var(--stone); border-radius: 10px; background: #fff;
  padding: 16px 24px; margin: 16px 0;
}
.statline div b { display: block; font-family: var(--mono); font-size: clamp(19px,2vw,24px); font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.statline div span { font-family: var(--mono); font-size: 12px; letter-spacing: .14em; color: var(--chrome); text-transform: uppercase; }

/* factbox / fact-side / verdict / method / editorial / referral / why / eff —
   all the same object: a bordered aside carrying sourced supporting detail.
   Given one rule instead of seven near-copies. */
.factbox, .fact-side, .verdict, .method, .editorial, .referral, .why, .eff, .answer, .outcome { border: 1px solid var(--stone); border-radius: 10px; background: #fff;
  padding: 16px 24px; margin: 16px 0;
}
.factbox h3, .fact-side h3, .verdict h3, .method h3, .editorial h3, .referral h3, .answer h3, .outcome h3 { font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .16em;
  color: var(--oxblood); text-transform: uppercase; margin: 0 0 8px;
}
.verdict { border-left: 3px solid var(--oxblood); border-radius: 0 10px 10px 0; }
.fact, .fact-side .fact { display: flex; justify-content: space-between; gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--stone-lt); font-size: 16px; }
.fact:last-child { border-bottom: 0; }
.metrics, .outcomes { display: grid; grid-template-columns: 1fr; gap: 1px; background: var(--stone); border: 1px solid var(--stone); border-radius: 10px; overflow: hidden; margin: 16px 0; }
@media (min-width: 620px) { .metrics, .outcomes { grid-template-columns: repeat(2, 1fr); } }
.metrics > *, .outcomes > * { background: #fff; padding: 16px 16px; }
.val { font-family: var(--mono); font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }

.pick { border-left: 3px solid var(--good); background: var(--stone-lt); padding: 16px 16px; margin: 16px 0; border-radius: 0 8px 8px 0; }
.qf, .spec { border: 1px solid var(--stone); border-radius: 10px; background: #fff; padding: 8px 16px; margin: 16px 0; }
.speak, .about, .desc { max-width: 62ch; }
/* web-principles §8 caps prose at 65ch. These three were the only blocks that
   had a measure; the answer text, the quick-facts values and the claim copy ran
   to 82, 84 and 73ch at 1440 because nothing bounded them. Measured, not eyed. */
main details p, dl.qf dd, .fact-side dd, .spec dd, .qf dd, .claim p, .faq-item p { max-width: 65ch; }
.ownquote { margin: 24px 0; max-width: 62ch; padding: 4px 0 4px 24px; border-left: 3px solid var(--oxblood); }
.ownquote p { font-family: var(--serif); font-style: italic; font-weight: 500; font-size: clamp(18px,1.8vw,21px); line-height: 1.5; color: var(--ink); }
.ownquote cite { display: block; margin-top: 16px; font-family: var(--mono); font-style: normal; font-size: 12px; font-weight: 600; letter-spacing: .14em; color: var(--chrome); text-transform: uppercase; }

/* --- index / listing grids ------------------------------------------------ */
.idx-item, .catcell, .rel-card, .card { display: block; border: 1px solid var(--stone); background: #fff;
  padding: 16px 16px; text-decoration: none; position: relative;
}
.idx-item:hover, .catcell:hover, .rel-card:hover, .card:hover { background: var(--stone-lt); border-color: var(--cobalt); }
.catgrid, .rel-grid { display: grid; grid-template-columns: 1fr; gap: 1px; background: var(--stone); border: 1px solid var(--stone); margin: 16px 0; }
@media (min-width: 640px)  { .catgrid, .rel-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .catgrid { grid-template-columns: repeat(3, 1fr); } }
.catgrid > *, .rel-grid > * { border: 0; }
.rel-nm, .name { font-family: var(--serif); font-weight: 600; font-size: 16px; color: var(--ink); display: block; }
.rel-d, .rel-m { font-size: 16px; color: var(--body); }
.rate { font-family: var(--mono); font-size: 12px; color: var(--body); font-variant-numeric: tabular-nums; }
.city { font-family: var(--mono); font-size: 12px; color: var(--chrome); }
.logo { width: 40px; height: 40px; border-radius: 8px; border: 1px solid var(--stone); background: #fff; object-fit: contain; padding: 4px; }
.head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* related links block, at the foot of nearly every article */
.rel { margin: 48px 0 0; border-top: 2px solid var(--ink); padding-top: 16px; }
.rel h3 { font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .16em; color: var(--oxblood); margin: 0 0 12px; text-transform: uppercase; }
/* Hover used to animate padding-left, a layout property, so every hover in this
   list reflowed it. web-principles §15 allows transform and opacity only;
   translateX is the same 8px nudge, on the compositor. Colour still changes on
   hover, it simply is not transitioned. */
.rel > a { display: block; font-family: var(--serif); font-size: 17px; font-weight: 600; color: var(--ink); text-decoration: none; padding: 8px 0; border-bottom: 1px solid var(--stone-lt); transition: transform var(--fast); }
.rel > a:hover { color: var(--cobalt); transform: translateX(8px); }
.rel > a:active { color: var(--fam); transform: translateX(4px); }

/* --- pillar index bars ---------------------------------------------------- */
.px-list { border-top: 1px solid var(--stone); margin: 24px 0; }
.px-row { display: grid; grid-template-columns: 30px minmax(0,1fr) 60px; gap: 12px; align-items: center; padding: 12px 4px; border-bottom: 1px solid var(--stone); text-decoration: none; }
@media (min-width: 700px) { .px-row { grid-template-columns: 30px minmax(0,200px) minmax(0,1fr) 60px; } }
.px-row:hover { background: var(--stone-lt); }
.px-no { font-family: var(--mono); font-size: 12px; font-weight: 600; color: var(--cobalt); letter-spacing: .1em; }
.px-nm { font-family: var(--serif); font-size: 16px; font-weight: 600; color: var(--ink); }
.px-track { display: none; height: 9px; background: var(--stone-lt); border-radius: 2px; overflow: hidden; }
@media (min-width: 700px) { .px-track { display: block; } }
.px-bar { display: block; height: 100%; background: var(--cobalt); border-radius: 0 2px 2px 0; }
.px-n, .px-cn { font-family: var(--mono); font-size: 12px; color: var(--body); text-align: right; font-variant-numeric: tabular-nums; }
.px-card { border: 1px solid var(--stone); background: #fff; padding: 16px 16px; }

/* --- glossary nav --------------------------------------------------------- */
.gl-nav { display: flex; flex-wrap: wrap; gap: 4px; margin: 16px 0; padding: 12px; background: var(--stone-lt); border: 1px solid var(--stone); border-radius: 10px; }
.gl-nav a { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; font-family: var(--mono); font-size: 12px; font-weight: 600; color: var(--cobalt); text-decoration: none; border-radius: 4px; }
.gl-nav a:hover { background: var(--cobalt); color: #fff; }
.gl-letter { font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .18em; color: var(--oxblood); border-bottom: 2px solid var(--ink); padding-bottom: 4px; margin: 32px 0 12px; text-transform: uppercase; }

/* --- partner claim on article pages --------------------------------------- */
.claim { background: var(--panel); border: 1px solid var(--hairline-2); border-radius: 12px; padding: 24px clamp(16px,3vw,32px); margin: 32px 0 0; }
.claim-k { font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .16em; color: var(--rust); margin-bottom: 8px; text-transform: uppercase; }
.claim h2, .claim h3 { color: var(--ink-inv); margin-bottom: 8px; }
.claim p { color: var(--body-inv); }
.claim p strong { color: var(--ink-inv); }
.claim .deal { display: grid; grid-template-columns: 1fr; gap: 12px; margin: 16px 0; }
@media (min-width: 560px) { .claim .deal { grid-template-columns: 1fr 1fr; } }
.claim .deal div { border: 1px solid var(--hairline-2); border-radius: 8px; background: var(--void); padding: 12px 16px; }
.claim .deal b { display: block; font-family: var(--mono); font-size: 12.5px; color: var(--amber); margin-bottom: 4px; }
.claim .deal span { font-size: 16px; color: var(--body-inv); line-height: 1.5; }
.claim-acts { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 4px; }
.claim-acts a { display: inline-block; font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .12em; padding: 12px 16px; border-radius: 6px; text-decoration: none; text-transform: uppercase; min-height: 44px; }
.claim-fine { margin-top: 12px; font-family: var(--mono); font-size: 12px; letter-spacing: .06em; color: var(--chrome-inv); line-height: 1.7; }

/* ==========================================================================
   16. FIRM PROFILE  (/c/*.html — 3,460 pages, the most-visited page type)
   Was a single porcelain column of prose blocks at one type size, which read
   as a generated document rather than a record. Now: a dark identity plate
   carrying the name, category, description and the one action worth taking,
   then the reading column, with the data sheet kept in the chrome register
   because it is a readout, not prose.
   ========================================================================== */
.firm-hd { background: var(--void); color: var(--ink-inv); border-bottom: 1px solid var(--hairline); position: relative; overflow: hidden; }
.firm-hd::before { content: ''; position: absolute; inset: 0; pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(122,162,255,.05) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(122,162,255,.05) 1px, transparent 1px);
  background-size: 48px 48px;
  mask-image: radial-gradient(ellipse 60% 90% at 15% 40%, #000 20%, transparent 100%);
}
.firm-hd-in { position: relative; padding-block: clamp(24px, 3vw, 32px) clamp(24px, 3.5vw, 32px); }
.firm-hd .crumb { color: var(--chrome-inv); margin-bottom: 24px; }
.firm-hd .crumb a { color: var(--chrome-inv); text-decoration: none; }
.firm-hd .crumb a:hover { color: var(--cobalt-lt); }
.firm-hd .head { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.firm-hd .logo { width: 64px; height: 64px; border-radius: 12px; flex-shrink: 0;
  border: 1px solid var(--hairline-2); background: var(--panel);
  object-fit: contain; padding: 8px;
}
.firm-hd h1 { color: var(--ink-inv); font-size: clamp(30px, 5vw, 56px); }
.firm-hd .sub { display: block; margin-top: 8px; font-family: var(--mono);
  font-size: 12px; font-weight: 600; letter-spacing: .14em;
  color: var(--cobalt-lt); text-transform: uppercase;
}
.firm-hd .desc { margin-top: 24px; font-size: clamp(16px, 1.3vw, 18px); line-height: 1.6; color: var(--body-inv); max-width: 62ch; }
.firm-hd .ownquote { border-left-color: var(--rust); }
.firm-hd .ownquote p { color: var(--ink-inv); }
.firm-hd .ownquote cite { color: var(--chrome-inv); }
.firm-hd .cta { background: var(--cobalt-lt); color: var(--void); margin-top: 24px; }
.firm-hd .cta:hover { background: var(--amber); }

/* data sheet — dark readout on the paper page, same object as the atlas spec panel */
dl.spec { border: 1px solid var(--hairline-2); border-radius: 10px; background: var(--void); padding: 4px 16px 8px; margin: 16px 0 0; }
dl.spec > div { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--hairline); }
dl.spec > div:last-child { border-bottom: 0; }
dl.spec dt { font-family: var(--mono); font-size: 12px; letter-spacing: .14em; color: var(--chrome-inv); text-transform: uppercase; flex-shrink: 0; }
dl.spec dd { font-family: var(--mono); font-size: 12px; font-weight: 600; color: var(--ink-inv); text-align: right; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }

/* quick-facts list */
dl.qf { border: 1px solid var(--stone); border-radius: 10px; background: #fff; padding: 4px 16px 8px; margin: 16px 0; }
dl.qf > div, dl.qf dt + dd { }
dl.qf dt { font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .12em; color: var(--chrome); text-transform: uppercase; padding-top: 12px; }
dl.qf dd { font-size: 16px; line-height: 1.6; color: var(--body); padding: 4px 0 12px; border-bottom: 1px solid var(--stone-lt); }
dl.qf dd:last-child { border-bottom: 0; }

/* Q&A accordion on the firm page */
main details { border: 1px solid var(--stone); border-radius: 8px; background: #fff; margin-bottom: 8px; overflow: hidden; }
main details summary { cursor: pointer; padding: 12px 16px; font-size: 16px; font-weight: 600; color: var(--ink); list-style: none; display: flex; align-items: baseline; gap: 12px; min-height: 44px; }
main details summary::-webkit-details-marker { display: none; }
main details summary::after { content: '+'; font-family: var(--mono); color: var(--cobalt); margin-left: auto; }
main details[open] { border-color: var(--cobalt); }
main details[open] summary { background: var(--stone-lt); }
main details[open] summary::after { content: '–'; }
main details .qno { font-family: var(--mono); font-size: 12px; font-weight: 600; color: var(--chrome); letter-spacing: .08em; flex-shrink: 0; }
main details p { padding: 12px 16px 12px 48px; font-size: 16px; line-height: 1.65; color: var(--body); margin: 0; }

/* ==========================================================================
   17. NARROW-VIEWPORT CORRECTIONS
   Found by the 320/768/1440 sweep, not by eye.
   ========================================================================== */

/* Wide data tables were pushing the page 47–116px wider than the viewport at
   320px — horizontal scroll on the whole document, which is the single failure
   the responsive rules call out by name. display:block turns the table into
   its own scroll container so the page stays put and the table scrolls inside
   itself. Above 640px they lay out as normal tables again. */
@media (max-width: 640px) { main table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }
  main table thead, main table tbody, main table tr { display: table; width: 100%; table-layout: fixed; }
  main table thead { width: 100%; }
}

/* Footer links measured 17px tall. §17 padded them to 24px, WCAG 2.5.8's legal
   minimum; web-principles sets the Sonic BUILD target at 44 and says in as many
   words not to drop to 24. Padding buys the height without moving the text, and
   the negative margin keeps the footer the height it was drawn at. */
footer.site a { display: inline-block; min-width: 44px; text-align: center; padding: 14px 0; margin-block: -14px; }

/* The compare control's real target is the label, not the 13px checkbox
   inside it. Sized so the label itself clears the bar. */
.row--cmp .cmp { min-height: 44px; }

/* Breadcrumb links are display:block grid/flex children, so the 2.5.8 inline
   exception does not apply to them — they measured 17px, then 25px after §17.
   Same treatment as the footer: 44px of target, 0px of visual change. */
.crumb a, .back { display: inline-block; min-width: 44px; text-align: center; padding: 14px 0; margin-block: -14px; }

/* The search field's own box was 18px inside a 46px container. Only the input
   takes the click, so the input is what has to clear the bar. */
.searchbox input { min-height: 44px; }

/* ==========================================================================
   18. EDITORIAL FAMILIES — contextual colour per page type
   ==========================================================================
   The problem this solves: after the dark masthead, every article page ran a
   single unbroken porcelain value level. The homepage got the two-register
   treatment on 2026-08-11; the other ~3,700 pages did not, so they still read
   as one long undifferentiated column with a black hat on it.

   Colour here encodes WHAT KIND OF CLAIM a page makes, not what category of
   vendor it lists — the ten-colour category accent was retired precisely
   because per-category colour is noise (§6). Three families, three stops, the
   same three already used by the masthead rule and the mega menu:

     index     cobalt   "here is what we recorded"
     newsroom  bronze   "here is what we observed, on this date"
     partner   oxblood  "here is where money is involved"

   `data-family` is written on <body> by theme.mjs family(), derived from the
   canonical URL, so no build script passes it and no page can disagree with
   its own URL. Every rule below reads --fam / --fam-inv only, so retheming a
   family is a one-line change and nothing hard-codes an accent twice. */

body { --fam: var(--cobalt);         /* accent on the PAPER register */
  --fam-inv: var(--cobalt-lt);  /* accent on the CHROME register */
  --fam-tint: rgba(27, 79, 216, .055);
}
body[data-family="newsroom"] { --fam: var(--amber-dk);
  --fam-inv: var(--amber);
  --fam-tint: rgba(122, 78, 7, .07);
}
body[data-family="partner"] { --fam: var(--oxblood);
  --fam-inv: var(--rust);
  --fam-tint: rgba(110, 20, 35, .055);
}

/* --- masthead ------------------------------------------------------------ */
/* The kicker was hard-coded --rust on every page, so a pillar page and the
   partner page opened on the same accent and neither meant anything by it. */
.art-hd { position: relative; }   /* ::after below is positioned against it */
.art-hd .kicker { color: var(--fam-inv); }
.art-hd .crumb a:hover { color: var(--fam-inv); }
.art-hd::after { content: ''; position: absolute; left: 0; bottom: -1px; height: 2px;
  width: clamp(96px, 22%, 260px); background: var(--fam-inv); z-index: 1;
}

/* --- the instrument readout ---------------------------------------------- */
/* .statline carries the sourced figures on pillar, find, state, city and hub
   pages. It was a white box with a hairline — the weakest object on the page
   holding its most important content. It now uses the same chrome register as
   the homepage readout, which is where the eye already expects figures. */
.statline { background: var(--void); border-color: var(--hairline-2);
  position: relative; overflow: hidden;
  padding: 16px 24px 16px;
}
.statline::before { content: ''; position: absolute; inset: 0; pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(122, 162, 255, .05) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(122, 162, 255, .05) 1px, transparent 1px);
  background-size: 44px 44px;
}
.statline::after { content: ''; position: absolute; top: 0; left: 0; height: 2px;
  width: 78px; background: var(--fam-inv);
}
.statline div { position: relative; }
.statline div b { color: var(--ink-inv); }
.statline div span { color: var(--chrome-inv); }
/* inside the dark masthead it sits on --panel instead, so it stays legible
   against the void behind it. That override already exists in §16 — this
   restores the grid and rule for that case too. */
.art-hd .statline { background: var(--panel); }

/* --- supporting asides ---------------------------------------------------- */
.factbox h3, .fact-side h3, .verdict h3, .method h3, .editorial h3,
.referral h3, .answer h3, .outcome h3 { color: var(--fam); }
.verdict { border-left-color: var(--fam); }
.ownquote { border-left-color: var(--fam); }
.faq-item { border-left-color: var(--fam); }
.rel { border-top-color: var(--fam); }
.rel > a:hover { color: var(--fam); }
.firmlist td a:hover { color: var(--fam); }

/* Cards were #fff on porcelain — a 1.02:1 difference, which is not a card, it
   is a rounded rectangle of nothing. Porcelain card on a family-tinted ground
   gives the same separation with the warmth the guide asks for (§7 forbids the
   pure-white ground for the same reason). */
.factbox, .fact-side, .verdict, .method, .editorial, .referral, .why, .eff,
.answer, .outcome, .qf, .spec, .firmlist, .metrics, .outcomes { background: var(--porcelain);
}
.metrics > *, .outcomes > * { background: var(--porcelain); }
.faq-item { background: var(--porcelain); }

/* --- definition lists ----------------------------------------------------- */
/* build-compare emits <dl><dt>label</dt><dd>value</dd> and nothing styled it,
   so the spec cards rendered label and value at identical weight and colour —
   twelve lines of prose where a table was intended. */
.fact-side dl, .spec dl, .qf dl { margin: 0; }
.fact-side dt, .spec dt, .qf dt { font-family: var(--mono); font-size: 12px; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--chrome);
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--stone-lt);
}
.fact-side dl > dt:first-of-type, .spec dl > dt:first-of-type, .qf dl > dt:first-of-type { margin-top: 0; padding-top: 0; border-top: 0;
}
.fact-side dd, .spec dd, .qf dd { margin: 3px 0 0; font-size: 16px; line-height: 1.55; color: var(--ink);
}

/* --- table header band ---------------------------------------------------- */
/* A mono label on stone read as another data row. On the chrome register it
   reads as the instrument's own labelling, which is what a header is. */
.firmlist { overflow: hidden; }
.firmlist th { background: var(--void); color: var(--chrome-inv);
  border-bottom-color: var(--hairline-2);
}

/* --- callout ------------------------------------------------------------- */
/* .pick keeps --good: it is a recommendation, and semantic colour outranks
   family colour. The label ran straight into the sentence, so it gets its own
   line — colour was doing work a line break should have done. */
.pick > strong:first-child, .pick > b:first-child { display: block; margin-bottom: 4px;
  font-family: var(--mono); font-size: 12px; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--good);
}

/* --- article tables ------------------------------------------------------- */
/* Build scripts emit a bare <table> into the reading column and nothing ever
   styled it, so briefings and comparisons rendered their data as loose text
   columns with no header and no row separation. Only properties §17's
   narrow-viewport rule does not set are used here, so the mobile
   display:block / overflow-x:auto scroll container still wins below 640px. */
main table { margin: 24px 0; font-size: 16px; border: 1px solid var(--stone); }
main thead th { background: var(--void); color: var(--chrome-inv);
  font-family: var(--mono); font-size: 12px; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase;
  text-align: left; padding: 12px 12px; vertical-align: bottom;
}
main tbody td { padding: 8px 12px; border-bottom: 1px solid var(--stone-lt);
  color: var(--body); vertical-align: top;
}
main tbody tr:last-child td { border-bottom: 0; }
main tbody td:first-child { color: var(--ink); font-weight: 500; }
/* Figures line up or they are not figures — but only where the markup says a
   cell IS a figure. An earlier draft monospaced td:last-child on the assumption
   the rightmost column is numeric; on comparison tables it is prose, and the
   whole column rendered in mono. */
main tbody td.num, main tbody td.rate { font-family: var(--mono); font-variant-numeric: tabular-nums;
}
/* .firmlist brings its own header treatment and padding; it is more specific
   than the element selectors above, so it keeps winning. */

/* ==========================================================================
   19. FIRM PROFILE, SECOND PASS  (/c/*.html — 3,455 pages)
   ==========================================================================
   §16 gave the firm page a dark identity plate and stopped. Three things were
   still wrong with what sits under it, and all three are the same fault the
   file opens by naming: one value level, so nothing has hierarchy.

   a. The figures were in the wrong place. Rate, team size, founding year and
      rating are the only decision-relevant facts on the page and they sat in
      the data sheet — position five, below twenty accordions. §18 built
      .statline as the chrome-register readout on the argument that figures
      belong where the eye already looks for them. The firm page was the one
      major page type that never used it. It does now, inside the plate.

   b. Twenty <details> at 8px radius with 8px gaps read as twenty objects, not
      one index. Collapsed into a single panel with hairline dividers — the
      same move .rel-grid already makes one screen further down.

   c. Summary and About were two prose blocks at identical weight, colour and
      measure, saying overlapping things back to back. Summary is the speakable
      abstract (schema points at .sp-what/.sp-details/.sp-facts), so it is not
      prose and should not look like it. It gets the family tint — which §18
      defined as --fam-tint and then never used anywhere.
   ========================================================================== */

/* --- 19a. the readout, in the plate --------------------------------------- */
/* Same override .art-hd .statline already takes: on the void ground the
   readout needs the next surface up or it disappears into the masthead. */
.firm-hd .statline { background: var(--panel); margin: 24px 0 0; }
/* The plate draws its own 48px grid. A second 44px grid inside the readout
   beats against it — one texture per surface. */
.firm-hd .statline::before { display: none; }

/* Flex-wrap left the fourth figure alone on its own row at phone widths — three
   across, then one, which reads as an afterthought rather than a fourth reading.
   Two-by-two below 560px, the width at which four no longer fit across. */
@media (max-width: 559px) { .firm-hd .statline { display: grid; grid-template-columns: 1fr 1fr; gap: 16px 16px; }
}

/* Provenance line. The whole positioning of this site is that it publishes
   what it can verify; four numbers in an instrument frame with no attribution
   is the one thing that cannot ship unlabelled. */
.firm-src { margin-top: 8px; font-family: var(--mono); font-size: 12px; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--chrome-inv);
  line-height: 1.7;
}
/* The link measured 100x11 — an 11px-tall target. web-principles puts the Sonic
   build floor at 44x44 (WCAG 2.2 SC 2.5.8's 24x24 is the legal minimum, not the
   target). Padding buys the height inside a line the mono caps already lead. */
.firm-src a { color: var(--chrome-inv); text-decoration: underline; text-underline-offset: 3px;
  display: inline-block; min-height: 44px; padding: 16px 2px; margin: -16px 0;
}
.firm-src a:hover { color: var(--fam-inv); }
.firm-src a:active { color: var(--ink-inv); }

/* --- 19b. the question index --------------------------------------------- */
/* Longhand corners, not the `border-radius` shorthand: a page with a single
   <details> matches :first-of-type AND :last-of-type, and with the shorthand
   the later rule would square off its top. */
main details { margin-bottom: 0; border-radius: 0; position: relative; }
main details + details { margin-top: -1px; }   /* two 1px borders → one divider */
main details:first-of-type { border-top-left-radius: 8px; border-top-right-radius: 8px; }
main details:last-of-type { border-bottom-left-radius: 8px; border-bottom-right-radius: 8px; }
/* An open row's border has to paint over its neighbours' or it reads as a gap. */
main details[open], main details:hover { z-index: 1; }
/* §16 hard-coded --cobalt here; §18's rule is that nothing names an accent
   twice, so the open row and its marker follow the family like everything else. */
main details[open] { border-color: var(--fam); }
main details summary::after { color: var(--fam); }

/* --- 19c. the abstract ---------------------------------------------------- */
/* The About heading landed hard against the tinted box — an abstract needs to
   end before the prose starts, or the two blocks read as one again. */
.speak { margin: 12px 0 32px; padding: 16px 24px;
  background: var(--fam-tint);
  border-left: 3px solid var(--fam);
  border-radius: 0 8px 8px 0;
}
.speak p { font-family: var(--serif); line-height: 1.65; color: var(--ink); }
.speak p + p { margin-top: 16px; }
.speak .sp-what { font-size: clamp(16px, 1.4vw, 18.5px); }
.speak .sp-details, .speak .sp-facts { font-size: 16px; color: var(--body); }

/* --- 19d. anchors and focus under the sticky bar --------------------------- */
/* .topbar is sticky at z-40, so every jump link on the site — the data sheet
   link, the glossary letter nav — landed with its target under the bar.
   [id] alone was not enough: WCAG 2.2 SC 2.4.11 (focus not obscured) also
   covers keyboard focus scrolled into view, and a tab onto a link sitting just
   above the fold parked it 7px under the bar. Measured, not guessed. */
main [id],
a, button, summary, input, select, textarea, [tabindex]:not([tabindex="-1"]) { scroll-margin-top: clamp(64px, 9vh, 96px);
}

/* ==========================================================================
   20. INTERACTION STATES — the two that were missing
   ==========================================================================
   web-principles §13 requires five states on every interactive element:
   default / hover / focus-visible / active / disabled. This stylesheet had
   three. `:active` appeared zero times in 1,250 lines and `:disabled` zero
   times, so a press produced no feedback at all and a disabled control was
   indistinguishable from a live one.

   Kept as one block rather than five states bolted onto each of thirty rules:
   the press feedback is the same gesture everywhere, so it should be one rule
   that says so. Nothing here transitions — §15 allows transform and opacity
   only, and a press should register instantly in any case.
   ========================================================================== */

/* --- pressed ------------------------------------------------------------- */
/* Buttons and pills take a 1px sink; the eye reads displacement as depression
   far faster than it reads a colour change. */
.cta:active, .btn:active, .claim-acts a:active,
.strip a.act:active, .di-reset:active, .gl-nav a:active, .nav-trigger:active {
  transform: translateY(1px);
}
/* Rows and cards are large targets where a 1px move is invisible; they darken
   to the next surface instead. */
.idx-item:active, .catcell:active, .rel-card:active, .card:active,
.px-row:active, .row:active, main details summary:active {
  background: var(--stone);
}
.claim-acts .a-pri:active { background: var(--ink); }
.crumb a:active, footer.site a:active, .back:active { color: var(--fam); }

/* --- disabled ------------------------------------------------------------ */
/* Never colour alone (§7 / SC 1.4.1): the cursor and the removed pointer
   target carry the state as well as the dimming does. */
button:disabled, input:disabled, select:disabled, textarea:disabled,
[aria-disabled="true"] {
  opacity: .45;
  cursor: not-allowed;
  pointer-events: none;
}
button:disabled:hover, [aria-disabled="true"]:hover { transform: none; }

/* --- 20b. the remaining sub-44px targets --------------------------------- */
/* The chrome links measured 33-34px: the wordmark, and the four mega-menu
   triggers. web-principles puts the Sonic build target at 44x44 and is explicit
   that WCAG 2.2 SC 2.5.8's 24x24 is a legal floor, not a build target. Padding
   plus a matching negative margin buys the target without changing the bar's
   drawn height — the topbar stays at the same 11%-of-viewport it measured at. */
.wordmark { padding-block: 6px; margin-block: -6px; }
.nav-trigger { min-height: 44px; }
/* The skip link measured 43px — one pixel short, and it is the first thing a
   keyboard user lands on. */
.skip { min-height: 44px; display: inline-flex; align-items: center; }
