/* ============================================================================
   LLMPrice.io design system — the single stylesheet every page imports.
   Extracted from index.html so the twenty-page split starts from a shared layer
   rather than drifting per page. Authority for values: brand/tokens-v2.css.
   Documented in DESIGN.md. Do not add page-specific rules here.
   ============================================================================ */


/* Self-hosted, latin and latin-ext only. Three registers, one weight budget:
   Newsreader 400 reading, IBM Plex Mono 400/500 data, IBM Plex Sans 400/500
   interface. Newsreader is a variable font whose every extra weight costs about
   180KB, so it is loaded at 400 alone: prose emphasis uses colour, not weight,
   which is what the system says anyway. Payload 168KB, down from 407KB, with a
   whole new family added. */
/* latin-ext */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/-F63fjptAgt5VM-kVkqdyU8n1iEq129k.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/-F63fjptAgt5VM-kVkqdyU8n1i8q1w.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/-F6qfjptAgt5VM-kVkqdyU8n3twJwl5FgtIU.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/-F6qfjptAgt5VM-kVkqdyU8n3twJwlBFgg.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url('/fonts/zYXzKVElMYYaJe8bpLHnCwDKr932-G7dytD-Dmu1syxQKYbABA.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url('/fonts/zYXzKVElMYYaJe8bpLHnCwDKr932-G7dytD-Dmu1syxeKYY.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url('/fonts/zYXzKVElMYYaJe8bpLHnCwDKr932-G7dytD-Dmu1syxQKYbABA.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url('/fonts/zYXzKVElMYYaJe8bpLHnCwDKr932-G7dytD-Dmu1syxeKYY.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/cY9VfjOCX1hbuyalUrK49dLafXjalZCsZBsgBgbDJYQraA.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/cY9VfjOCX1hbuyalUrK49dLafXjalZCsZBsgBgbNJYQ.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}



  /* Was pinned to the light paper colour in both modes, so in dark mode the
     overscroll area and anything outside <body> flashed near-white on a phone
     (UI/UX audit F25, 2026-07-31). */
  html { background: var(--paper); }
  html, body { overflow-x: clip; }
  /* Body is Newsreader at 17/1.62 per the Rev 02 scale. Mono is never used for prose. */
  body { font-family: 'Newsreader','Iowan Old Style','Palatino Linotype',Palatino,Georgia,serif;
         font-size: 17px; line-height: 1.62; }

  /* ===== Identity Rev 02 tokens. Authority: brand/tokens-v2.css =====
     OKLCH is authoritative, hex is the sRGB fallback. Two accents only, both
     semantic: decline when a figure fell, increase when it rose. Never
     decorative, never a fill, no third accent, no gradient anywhere. */
  :root {
    --paper:     #EAE8E4;
    --surface:   #F7F6F3;
    --ink:       #181612;
    --ink-2:     #3A3833;
    --muted:     #66635D;
    --rule-hair: #DCDAD5;
    --rule-edge: #D9D7D2;
    --decline:   #00643D;   /* a figure FELL  */
    --increase:  #9E4045;   /* a figure ROSE  */
    /* The brand mark's orange, approved from the fold mockup 2026-07-30. This is
       NOT a third data accent: its one permitted use is the "Price" syllable of
       the wordmark. It never colours a figure, a delta, a chart mark or a link.
       The two semantic accents above remain the only colours that touch data.

       AJ: this is a BRAND VALUE and the one change in the 2026-07-31 UI/UX pass
       that touches brand. The approved #C05A2C measured 4.10:1 against the paper
       and 3.62:1 against the card surface, both under the 4.5:1 AA floor for 16px
       text, and it was the only contrast failure on the site (audit F20). This is
       the same hue darkened 13%, the smallest step that clears 4.5:1 on BOTH
       grounds (5.15:1 on paper, 4.55:1 on surface). Revert by restoring #C05A2C
       on this one line; nothing else depends on it. */
    --brand:     #A74E26;

    /* ===== Categorical series palette (charts only) =====
       AJ, 2026-07-31: the monochrome trajectory chart was unreadable, and he is
       right. This is a chart series palette, NOT a fourth accent: it appears only
       inside an SVG plot, never on a figure, a delta, a table cell or any text.
       The two semantic accents still own data colour everywhere else.

       Seven slots for seven providers, assigned in fixed alphabetical order so a
       provider keeps its hue when the filter changes the series count. Colour by
       provider, not by model, because 42 hues is not a palette and the method caps
       categorical series well below that.

       Validated, not eyeballed, with the dataviz validator against our own
       surfaces. Light on #EAE8E4: lightness band, chroma floor, CVD separation
       (worst adjacent 9.1 protan) and normal-vision floor (19.6) all pass;
       contrast warns below 3:1, which obliges visible labels, and the chart
       carries a labelled provider legend with the full 42-row table above it.
       Dark on #060504: all five checks pass outright, contrast included.
       Re-run before changing any value:
         node scripts/validate_palette.js "<hex,...>" --mode light --surface "#EAE8E4" */
    --series-1: #2a78d6;   /* Anthropic  */
    --series-2: #eb6834;   /* DeepSeek   */
    --series-3: #1baf7a;   /* Google     */
    --series-4: #eda100;   /* Mistral    */
    --series-5: #e87ba4;   /* OpenAI     */
    --series-6: #008300;   /* Perplexity */
    --series-7: #4a3aa7;   /* xAI        */
    --serif: 'Newsreader','Iowan Old Style','Palatino Linotype',Palatino,Georgia,serif;
    --data:  'IBM Plex Mono',Menlo,Consolas,'DejaVu Sans Mono',monospace;
  }
  /* The site is dark-first, so :root above is the LIGHT reading and html (no .light)
     runs dark. Dark is a peer of light, not an inversion: same hue, low lightness. */
  html:not(.light) {
    --paper:#060504; --surface:#0F0D0A; --ink:#E8E5E0; --ink-2:#C6C4C0; --muted:#9B9892;
    --rule-hair:#211F1B; --rule-edge:#2B2825;
    --decline:#60BA8D;   /* lifted to hold contrast at the same perceived restraint */
    --increase:#F48A61;
    --brand:#DE8455;     /* same hue as light, lifted for the dark ground */
    /* The same seven hues stepped for the dark ground, not an automatic flip. */
    --series-1:#3987e5; --series-2:#d95926; --series-3:#199e70; --series-4:#c98500;
    --series-5:#d55181; --series-6:#008300; --series-7:#9085e9;
  }

  /* The wordmark, shared by every header. Plex Sans at the interface weight cap,
     with the brand orange on the one syllable it is permitted to colour. */
  .lp-wordmark { font-family:var(--ui); font-weight:500; font-size:16px;
                 letter-spacing:.01em; color:var(--ink); text-decoration:none; white-space:nowrap; }
  .lp-wordmark b { color:var(--brand); font-weight:500; }

  /* Ink is #E8E5E0 on dark, never pure white: the warm hue is held at every
     lightness, which is what makes the two modes read as one system. */
  html:not(.light) .text-white { color: #E8E5E0; }


  /* Icons: currentColor only, so they inherit ink in both modes. Stroke weight
     steps with the box per the spec (1.25px at 34, 1px at 20). */
  .ico { display:inline-block; vertical-align:-0.18em; width:20px; height:20px;
         fill:none; stroke:currentColor; stroke-width:1.6; stroke-linecap:butt;
         stroke-linejoin:miter; flex:none; }
  .ico-sm { width:16px; height:16px; stroke-width:1.9; vertical-align:-0.16em; }
  /* .ico-lg, .ico-decline and .ico-increase deleted 2026-07-31 (UI/UX audit F28):
     no element on the site carried any of them. The colour pair in particular was
     a standing invitation to tint an icon, which the two-accent rule forbids
     anyway. .fig-hero and .lp-m-6 were also unused but are kept: both are
     documented members of a component family in DESIGN.md, not strays. */


  /* ===== Three registers, one job each =====
     Newsreader  display headings and body prose only
     Plex Mono   figures, rates, machine identifiers; tabular in columns
     Plex Sans   the entire interface layer: table headers, buttons, form labels,
                 nav, badges, eyebrows, tooltips, empty states

     Plex Sans is the sibling of the mono already in use, drawn on the same
     skeleton, so the label and numeric columns of a table now harmonise instead
     of arguing. Before this, every button and table header was borrowing a
     reading serif, and 48 font-bold plus 23 font-extrabold were being FAUX-BOLDED
     because Newsreader was never loaded above 500. */
  :root { --ui: 'IBM Plex Sans', ui-sans-serif, system-ui, sans-serif; }

  /* [role="tab"] and [role="tablist"] dropped: no element on the site carries
     either. The /models profile strip uses .tab-on / .tab-off on plain buttons,
     which is a filter, not a tablist, and giving it the role without the arrow-key
     behaviour a tablist implies would be worse than not claiming it at all
     (UI/UX audit F28, 2026-07-31). */
  button, input, select, textarea, label, th, nav, legend,
  .lab-fig, .prov, .delta, .eyebrow, .tip-btn, #tooltip, #sync-badge, .has-tip {
    font-family: var(--ui);
  }
  /* Interface text tops out at 500. Anything heavier was faux bold. */
  button, th, label, nav, .has-tip { font-weight: 500 !important; }
  /* Tailwind weight utilities would otherwise ask for 600-900, which no loaded face
     has, so the browser synthesises it. Cap the interface layer at 500. */
  .font-bold, .font-extrabold, .font-black, .font-semibold { font-weight: 500 !important; }
  /* Prose and display keep the serif and stay at 400: size carries emphasis. */
  h1, h2, h3, p, article, .t-display, .standfirst { font-family: var(--serif); font-weight: 400; }
  /* An `h1.font-bold, h2.font-bold, h3.font-bold { font-weight: 400 }` rule sat
     here and never applied: the !important clamp two lines above beats it, so it
     only ever misled a reader of this file (UI/UX audit F29, 2026-07-31). The
     rendered result is unchanged because font-synthesis-weight: none already
     forces the 400 face. */

  /* Data cells: machine identifiers in mono at regular weight, aligned with the
     price columns so the row scans as one grid. */
  /* Newsreader ships at 400 only, so strong/b would be faux-bolded. Emphasis in
     prose is carried by ink colour instead, which is the rule the system already
     applies to figures. !important is LOAD-BEARING (regression found by audit,
     fixed 2026-07-31): Tailwind Play injects its preflight at runtime AFTER this
     static stylesheet, and its `b, strong { font-weight: bolder }` wins the
     cascade at equal specificity. The two clamp rules above survived only
     because they carried !important; this one did not, and the standfirst
     rendered a synthesised 700 in a 400-only face. */
  strong, b { font-weight: 400 !important; color: var(--ink); }
  /* No synthesised weights, ever: a weight with no real face behind it must
     fail visibly (render at the nearest shipped weight), not degrade into
     something that looks almost right. Every face here ships 400/500 only. */
  html { font-synthesis-weight: none; }
  /* Tab selection: ink and a rule, both mode-aware. A fixed Tailwind shade was
     right in dark and nearly invisible on paper. */
  .tab-on  { color: var(--ink) !important; border-bottom-color: var(--ink) !important; }
  .tab-off { color: var(--muted) !important; border-bottom-color: transparent !important; }
  .tab-off:hover { color: var(--ink-2) !important; }
  .ui-mono { font-family: var(--data); font-weight: 400; letter-spacing: -0.01em; }
  .ui-num  { font-family: var(--data); font-variant-numeric: tabular-nums; font-weight: 400; }
  table td, table th { font-variant-numeric: tabular-nums; }
  th { letter-spacing: .08em; text-transform: uppercase; }

  /* ===== Mode-aware neutrals =====
     One warm ramp serves both modes, but the site uses slate-500/600 for muted
     copy, which reads correctly on paper and goes too dark on the #060504
     ground. Lift those two steps in dark so muted text stays legible instead of
     dissolving. Rev 02 dark muted is #9B9892. */
  html:not(.light) .text-slate-500 { color: #9B9892; }
  html:not(.light) .text-slate-600 { color: #8A867E; }
  html:not(.light) .text-slate-700 { color: #8A867E; }

  /* ===== Buttons, token-driven =====
     A fill has to invert with the mode or the label disappears. --ink and
     --paper both flip, so one rule is correct in both: dark fill with light
     label on paper, light fill with dark label on the dark ground. */
  .btn-primary { background: var(--ink) !important; color: var(--paper) !important; border: 1px solid var(--ink) !important; }
  .btn-primary:hover { background: var(--ink-2) !important; border-color: var(--ink-2) !important; }
  .btn-ghost { background: transparent !important; color: var(--ink) !important; border: 1px solid var(--ink) !important; }

  /* ===== Small-text floor =====
     The Rev 02 scale sets labels and meta at 9-11.5px, which is right on a
     spec sheet read at arm's length and too tight on a real screen. Every
     small step is lifted about 15%: nothing drops below 12px, and mono labels
     keep their tracking so the instrument register survives the increase.
     Ratios between steps are preserved, so the hierarchy is unchanged. */
  .text-xs   { font-size: 13.5px !important; line-height: 1.55 !important; }
  .text-sm   { font-size: 15.5px !important; line-height: 1.6 !important; }
  .text-base { font-size: 17px !important; }
  .text-\[9px\]    { font-size: 12px !important; }
  .text-\[10px\]   { font-size: 12.5px !important; }
  .text-\[11px\]   { font-size: 13px !important; }
  .lab-fig   { font-size: 12px; letter-spacing: .15em; }
  .prov      { font-size: 12px; }
  .delta     { font-size: 13.5px; }
  .delta .word { font-size: 12.5px; }
  /* Tables: cell 12.5 -> 14, head 10 -> 12, keeping the head/cell relationship. */
  table td, .data-cell { font-size: 14px; }
  table th { font-size: 12px; letter-spacing: .1em; }

  /* Display face. Newsreader at 400: never bold, size carries the emphasis. */
  .t-display { font-family: var(--serif); font-weight: 400; letter-spacing: -0.02em; }

  /* THE FIGURE — Rev 02 sets figures in the SERIF, not mono (mono only for the
     inline figure-xs role). Tabular always. Never bold, never boxed, never on a
     fill, never in a card: space sets a conclusion apart, not a border. */
  .fig { font-family: var(--serif); font-weight: 400; font-variant-numeric: tabular-nums lining-nums;
         color: var(--ink); letter-spacing: -0.035em; white-space: nowrap; }
  /* Half the figure's size, but never below the 11px floor: on the small inline
     figures inside the estimator breakdown, .5em landed at 8.5px, under the 12px
     floor DESIGN.md sets as a deliberate deviation (UI/UX audit F42, 2026-07-31). */
  .fig .cur, .fig .dec { font-size: max(.5em, 11px); color: var(--muted); letter-spacing: 0; }
  .fig .cur { margin-right: .08em; }
  .fig .unit { font-family: var(--serif); font-size: .375em; color: var(--muted); margin-left: .3em; }
  .fig-hero  { font-size: clamp(64px,11vw,116px); line-height: .86; display: block; }
  .fig-block { font-size: 62px; line-height: .86; letter-spacing: -0.03em; display: block; }

  /* The stat block. DESIGN.md documents it as a component ("2px rule above a
     display figure") but the stylesheet never defined it, so nothing on the site
     could use it (UI/UX audit F14 and F28, 2026-07-31). Elevation is a rule and a
     ground shift: no border box, no fill, no shadow. */
  .stat { border-top: 2px solid var(--ink); padding-top: 12px; }
  .stat .fig { display: block; line-height: 1.02; }

  /* Label above a figure carries the negative half-leading overshoot plus one 8px
     step, never the 6px a default label would get. */
  .lab-fig { font-family: var(--data); font-size: 10.5px; letter-spacing: .16em;
             text-transform: uppercase; color: var(--muted); margin-bottom: 18px; }

  /* Delta. Direction is a glyph and a word, so colour never carries meaning alone. */
  .delta { display:inline-flex; align-items:baseline; gap:.4em; font-family: var(--data);
           font-size: 12px; font-variant-numeric: tabular-nums; letter-spacing: .04em; }
  .delta.favour  { color: var(--decline); }
  .delta.against { color: var(--increase); }
  .delta .word { color: var(--muted); }

  .prov { font-family: var(--data); font-size: 10.5px; letter-spacing: .1em; line-height: 1.8; color: var(--muted); }

  /* ===== Motion: one arrival sequence, entry surface only, once per load =====
     Structure is already painted at 0ms; only the hero content arrives. No hover,
     scroll or route motion anywhere. No odometer count-up on figures: the number
     is the credibility anchor and must never show a wrong value, not even for
     900ms, because a screenshot taken mid-count is a wrong citation. */
  @keyframes lp-rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
  @keyframes lp-fade { from { opacity: 0; } to { opacity: 1; } }
  .lp-m { animation-fill-mode: both; animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
  .lp-m-1 { animation: lp-fade 400ms ease-out both; }
  .lp-m-2 { animation: lp-rise 620ms  80ms both; }
  .lp-m-3 { animation: lp-rise 620ms 200ms both; }
  .lp-m-4 { animation: lp-rise 620ms 320ms both; }
  .lp-m-5 { animation: lp-rise 620ms 440ms both; }
  .lp-m-6 { animation: lp-rise 620ms 560ms both; }
  @media (prefers-reduced-motion: reduce) {
    .lp-m, .lp-m-1, .lp-m-2, .lp-m-3, .lp-m-4, .lp-m-5, .lp-m-6 {
      animation: none !important; opacity: 1 !important; transform: none !important;
    }
  }

  /* ===== Price ticker: sitewide strip below the nav (master brief, Part B) =====
     Lives INSIDE the <header> element so every shell-borrowing generator and the
     frozen-issue chrome refresh inherit it without their own wiring. Steps in
     discrete moves, never a gliding marquee: text is stationary whenever a reader
     is reading it. Sizes respect the 12px small-text floor. Direction is a glyph
     plus the semantic accent, never colour alone. No gradients: the window edge
     is a hard clip, per the identity rules. */
  .lp-ticker { display:flex; align-items:stretch; height:34px; border-top:1px solid var(--rule-hair);
    font-family:var(--data); font-size:12.5px; overflow:hidden; }
  /* The anchor is a GRID stacking its four index variants in shared COLUMNS, so
     the box is as wide as the widest variant AND each column (name, value,
     delta, date) is as wide as its widest occupant: cycling can jolt neither
     the rolling track beside it nor the number beside the name. CSS does the
     sizing; nothing is measured, so there is no font-load race. */
  .lpt-anchor { display:grid; grid-template-columns:repeat(4, max-content); column-gap:7px;
    align-items:center; padding:0 12px; border-right:1px solid var(--rule-hair);
    white-space:nowrap; flex:none; text-decoration:none; color:var(--ink); }
  .lpt-anchor .lpt-av { grid-row:1; grid-column:1 / -1; display:grid;
    grid-template-columns:subgrid; align-items:center; visibility:hidden; }
  .lpt-anchor .lpt-av.on { visibility:visible; }
  /* Browsers without subgrid fall back to the width-stable stack: the outer box
     holds, the inner columns may drift, which was the previous behaviour. */
  @supports not (grid-template-columns: subgrid) {
    .lpt-anchor .lpt-av { display:flex; gap:7px; grid-column:auto; grid-area:1/1; }
  }
  .lpt-anchor .ad { color:var(--muted); font-size:12px; }
  .lpt-anchor:hover .v { text-decoration:underline; }
  .lpt-anchor .k { font-size:12px; letter-spacing:.1em; text-transform:uppercase; color:var(--muted); }
  .lpt-anchor .v { font-weight:500; font-variant-numeric:tabular-nums; }
  .lpt-win { flex:1; min-width:0; overflow:hidden; position:relative; }
  .lpt-track { display:flex; align-items:center; height:100%; padding-left:12px; }
  .lpt-mv { display:flex; align-items:baseline; gap:7px; white-space:nowrap; flex:none; padding-right:28px; }
  .lpt-mv .m { color:var(--ink); font-weight:500; }
  .lpt-mv .meter { color:var(--muted); font-size:12px; letter-spacing:.04em; text-transform:uppercase; }
  .lpt-mv .dt { color:var(--muted); font-size:12px; }
  .lpt-dn { color:var(--decline); font-variant-numeric:tabular-nums; }
  .lpt-up { color:var(--increase); font-variant-numeric:tabular-nums; }
  .lpt-quiet { color:var(--muted); }
  .lpt-end { display:flex; align-items:center; gap:9px; padding:0 12px; flex:none; border-left:1px solid var(--rule-hair); white-space:nowrap; }
  .lpt-end .cnt { font-size:12px; color:var(--muted); letter-spacing:.06em; text-transform:uppercase; }
  .lpt-end a { font-size:12px; color:var(--ink); text-decoration:none; }
  .lpt-end a:hover { text-decoration:underline; }
  #lpt-pause { background:none; border:1px solid var(--rule-edge); color:var(--muted); font-family:var(--data);
    font-size:12px; letter-spacing:.06em; padding:2px 7px; cursor:pointer; line-height:1.3; }
  #lpt-pause:hover { color:var(--ink); border-color:var(--muted); }
  .lpt-pv { position:absolute; inset:0; display:flex; align-items:center; padding:0 12px; opacity:0; transition:opacity .4s ease; }
  .lpt-pv.on { opacity:1; }
  @media (max-width:639px) {
    .lp-ticker { height:28px; }
    /* The variants stack in ONE grid cell so the anchor holds the width of the
       widest one and cycling cannot wobble the strip beside it (AJ,
       2026-07-30: the flex swap resized the anchor on every change). */
    .lpt-anchor { display:grid; grid-template-columns:max-content; padding:0 10px; }
    .lpt-anchor .lpt-av { display:flex; gap:6px; grid-area:1 / 1; visibility:hidden; align-items:center; }
    .lpt-anchor .lpt-av.on { visibility:visible; }
    /* The index NAME stays readable (AJ, 2026-07-30: it was hidden here and a
       bare level with no label identified nothing); the idx/index suffix and
       the move date go, the name alone identifies it at this width. */
    .lpt-anchor .k { font-size:10px; letter-spacing:.05em; }
    .lpt-anchor .kx, .lpt-anchor .ad { display:none; }
    .lpt-mv .meter, .lpt-mv .dt { display:none; }
    /* No controls on mobile (the strip itself links to /models), and the count
       label was already hidden, so the end cell is empty chrome: drop it. */
    .lpt-end { display:none; }
  }
  @media (prefers-reduced-motion: reduce) {
    .lpt-pv { transition:none; }
  }

  /* ===== Nav Tools dropdown (AJ, 2026-07-30) =====
     Every tool findable from one menu: the split scattered them across five
     URLs and the footer was the only map. Native <details>, so it works with
     no script; the outside-click close is progressive enhancement. */
  .nav-tools { position:relative; }
  .nav-tools summary { list-style:none; cursor:pointer; display:inline-flex; align-items:center; gap:5px; }
  .nav-tools summary::-webkit-details-marker { display:none; }
  .nav-tools summary::after { content:"\25BE"; font-size:9px; opacity:.75; }
  .nav-tools[open] summary { color:var(--ink); }
  .nav-tools .nav-menu { position:absolute; left:0; top:calc(100% + 10px); z-index:70; min-width:215px;
    background:var(--surface); border:1px solid var(--rule-edge); padding:5px 0;
    text-transform:none; letter-spacing:.02em; }
  .nav-tools .nav-menu a { display:block; padding:9px 14px; font-size:13px; white-space:nowrap; }

  .card-glow { border: 1px solid var(--rule-edge); }
  /* The advanced calculator sliders predated the token system: every colour was a
     literal, the dark-mode thumb was filled with the LIGHT ink so it read as a
     hollow ring, `outline:none` removed the focus ring with nothing put back, the
     Firefox track was never styled so it kept its rounded default, and the whole
     control was a 6px-tall touch target (UI/UX audit F11 and F13, 2026-07-31).
     The padding trick raises the hit area to 44px without moving the visible
     track: the background is clipped to the content box, the padding is not. */
  /* --rule-edge is #D9D7D2, which on the near-white light paper is about 1.1:1:
     the bars were effectively invisible in light mode (AJ, 2026-07-31). The unused
     part of the track is a real neutral now, and the used part carries the brand
     accent, so the control states its value rather than just sitting there.
     --fill is set per slider by paintSlider() in app.js; the fallback 0% means a
     slider still renders correctly if the script never runs. */
  input[type=range] {
    -webkit-appearance: none; appearance: none; width: 100%;
    height: 6px; box-sizing: content-box; padding: 19px 0;
    border-radius: 0; background-clip: content-box;
    background-image: linear-gradient(to right,
      var(--brand) 0 var(--fill, 0%), var(--muted) var(--fill, 0%) 100%);
  }
  input[type=range]::-webkit-slider-runnable-track { height: 6px; border-radius: 0; background: transparent; }
  input[type=range]::-moz-range-track { height: 6px; border-radius: 0; background: transparent; }
  input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none; width: 18px; height: 18px;
    margin-top: -6px; border-radius: 0; background: var(--ink);
    cursor: pointer; border: 2px solid var(--paper);
  }
  input[type=range]::-moz-range-thumb {
    width: 18px; height: 18px; border-radius: 0; background: var(--ink);
    cursor: pointer; border: 2px solid var(--paper);
  }
  details > summary { list-style: none; cursor: pointer; }
  details > summary::-webkit-details-marker { display: none; }
  details[open] .faq-chev { transform: rotate(180deg); }
  /* rgba(2,6,23) is Tailwind's blue-black slate-950, the one surface never moved
     to the warm ramp, and the blur was a soft-elevation effect in a system whose
     elevation is a ground shift and a rule. The light override below was already
     correct (UI/UX audit F24, 2026-07-31). */
  .modal-backdrop { background: rgba(6,5,4,0.88); }
  /* .fade-in was a SECOND arrival sequence in a system that permits exactly one,
     so the panels now arrive with the page's own vocabulary and nothing animates
     on open. The keyframes are kept because the class is referenced from markup
     that predates this file; it simply resolves to no motion. */
  .fade-in { animation: none; }
  .embed-mode .hide-embed { display: none !important; }
  #calculator.audit .calc-only { display: none !important; }

  /* ===== Light theme overrides (html.light) — warm paper, Rev 02 =====
     Warm grey, not cream: hue 85 at chroma 0.004, a twelfth of the chroma in the
     cream cluster, so the warmth registers as paper stock rather than as tint. */
  html.light { background: #EAE8E4; }
  html.light body { background-color: #EAE8E4 !important; }
  html.light .bg-slate-950 { background-color: #EAE8E4; }
  html.light .bg-slate-950\/80 { background-color: rgba(247,246,243,0.9); }
  html.light .bg-slate-900, html.light .bg-slate-900\/70, html.light .bg-slate-900\/60,
  html.light .bg-slate-900\/40, html.light .bg-slate-900\/95 { background-color: #F7F6F3; }
  html.light .bg-slate-950\/60 { background-color: #EAE8E4; }
  html.light .bg-slate-800 { background-color: #DCDAD5; }
  html.light .text-white { color: #181612; }
  html.light .text-slate-200 { color: #181612; }
  html.light .text-slate-300 { color: #3A3833; }
  html.light .text-slate-400 { color: #3A3833; }
  html.light .text-slate-500 { color: #66635D; }
  html.light .text-slate-600 { color: #66635D; }
  html.light .text-cyan-200, html.light .text-cyan-300, html.light .text-cyan-400 { color: #181612; }
  html.light .text-green-400, html.light .text-green-300 { color: #00643D; }  /* a figure fell */
  html.light .text-red-400, html.light .text-red-300 { color: #9E4045; }      /* a figure rose */
  html.light .text-violet-400, html.light .text-violet-300 { color: #66635D; }
  html.light .border-slate-700, html.light .border-slate-700\/70 { border-color: #D9D7D2; }
  html.light .border-slate-800, html.light .border-slate-800\/60, html.light .border-slate-800\/80 { border-color: #DCDAD5; }
  html.light .border-cyan-500\/30, html.light .border-cyan-500\/40 { border-color: #D9D7D2; }
  html.light .placeholder-slate-600::placeholder { color: #8A867E; }
  html.light .hover\:text-white:hover { color: #181612; }
  html.light .hover\:text-slate-200:hover, html.light .hover\:text-slate-300:hover { color: #181612; }
  html.light .hover\:text-cyan-300:hover { color: #181612; }
  html.light .hover\:bg-slate-800\/30:hover { background-color: #E3E1DC; }
  html.light .bg-gradient-to-r.from-slate-900 { background-image: none; background-color: #F7F6F3; }
    html.light .modal-backdrop { background: rgba(24,22,18,0.35); }
  /* `background: #DCDAD5` here was the actual cause of the invisible light-mode
     bars (AJ, 2026-07-31): the shorthand reset background-image, wiping the
     gradient the base rule paints, and #DCDAD5 on #F7F6F3 is about 1.1:1. The
     base rule is token-driven and correct in both modes, so both overrides go.
     The -moz thumb was never fixed here either, which is why Firefox kept a
     near-white ring on paper (UI/UX audit F11). */
  html.light .card-glow { border-color: #D9D7D2; }
  html.light .shadow-glow, html.light .shadow-glowv, html.light .shadow-glowg { box-shadow: none; }

  /* Field help tooltips */
  .tip-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 15px; height: 15px; border-radius: 0; margin-left: 6px;
    /* 500 is the cap; 700 was overridden in practice but contradicted the rule in
       source, which is how a wrong value survives a review (UI/UX audit F40). */
    border: 1px solid #4A4640; color: #9B9892; font-size: 10px; font-weight: 500;
    line-height: 1; cursor: help; vertical-align: middle; flex: none;
  }
  /* This rule used to end in `outline: none` and paint the focused button in
     #181612, the LIGHT mode ink, so on the #060504 dark ground a focused help
     button went black on black with no ring to replace it (UI/UX audit F7,
     2026-07-31). Tokens in both modes, and the outline stays. */
  .tip-btn:hover, .tip-btn:focus { border-color: var(--ink); color: var(--ink); }
  html.light .tip-btn { border-color: #C6C4C0; color: #66635D; }
  #tooltip {
    position: fixed; z-index: 70; max-width: 280px; padding: 10px 12px;
    background: #0F0D0A; border: 1px solid #2B2825; border-radius: 0;
    color: #E8E5E0; font-size: 12px; line-height: 1.55; font-weight: 400;
    /* The one surviving box-shadow in the stylesheet, in a system whose rule is
       "no shadows at all" (UI/UX audit F23, 2026-07-31). The 1px border above is
       the elevation cue the system specifies. */
    pointer-events: none;
  }
  html.light #tooltip { background: #181612; border-color: #2B2825; color: #F7F6F3; }

  /* Focus was undesigned across the whole site: not one :focus-visible rule
     existed in this stylesheet, so every control fell back to the browser default,
     and two rules actively deleted that default without replacing it (UI/UX audit
     F7, 2026-07-31). One ring, driven off --ink so it inverts with the mode. */
  :focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
  .btn-primary:focus-visible, .est-live:focus-visible { outline-offset: -4px; }

  /* ===== The accent, widened =====
     AJ, 2026-07-31: the pages read a little flat, so the brand orange earns a
     second and third use beyond the wordmark. The discipline that keeps it from
     becoming decoration:

     1. It marks STRUCTURE and NAVIGATION, never data. No figure, no delta, no
        table cell, no chart mark is ever orange. The two semantic accents
        (--decline, --increase) remain the only colours that touch a number, so a
        screenshot of a result still reads exactly as before.
     2. It is never the only signal. Section headings are already headings by
        size and position; the colour is a second cue, not the meaning.
     3. One use per view, roughly. Subheads are spaced down a page, so the eye
        meets the accent once per screen rather than everywhere at once.

     Contrast checked both modes: #A74E26 is 5.17:1 on light paper and 4.55:1 on
     the light card; #DE8455 is well clear on the dark ground.
     DESIGN.md is updated to match; do not widen this further without changing
     both files together.

     The selectors are mode-qualified because the generated subheads carry
     `text-white`, and `html:not(.light) .text-white` outranks a plain
     `main h2.t-display`. Same trick the rest of this file uses. */
  html:not(.light) main h2.t-display, html.light main h2.t-display,
  html:not(.light) main h3.t-display, html.light main h3.t-display,
  html:not(.light) #est-result h3,    html.light #est-result h3,
  html:not(.light) main .eyebrow,     html.light main .eyebrow,
  .t-display.accent,
  .est-jump a:hover { color: var(--brand); }
  /* The uppercase label above a figure stays muted: it sits directly beside a
     number and the rule above says the accent never goes near data. */
  .lab-fig, .prov { color: var(--muted); }

  /* Estimator chips. Not a filled pill: ink plus a 2px rule, the same way the
     tab strip marks the chosen thing (UI/UX audit F15, 2026-07-31). */
  .est-chip {
    padding: 6px 2px; margin-right: 4px; border: 0; border-bottom: 2px solid transparent;
    background: none; border-radius: 0; cursor: pointer;
    font-family: var(--ui); font-size: 12.5px; color: var(--muted);
  }
  .est-chip:hover { color: var(--ink-2); }
  .est-chip-on { color: var(--ink); border-bottom-color: var(--ink); }

  /* Chart legend. Forty-two entries, so they read as a list of names rather than
     as chrome: no borders, no swatches (the chart is monochrome), and the pressed
     one takes ink because that is the line currently drawn heavy. */
  .hleg {
    padding: 2px 0; border: 0; background: none; cursor: pointer;
    font-family: var(--data); font-size: 11.5px; color: var(--muted);
  }
  .hleg:hover { color: var(--ink-2); }
  .hleg[aria-pressed="true"] { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }

  /* The provider filter is also the chart's legend, so each entry pairs its series
     swatch with the provider's name: identity never rests on colour alone. */
  .hprov { display: inline-flex; align-items: center; gap: 7px; }
  .hswatch { width: 10px; height: 10px; flex: none; border-radius: 0; }

  /* The theme toggle's icon carries the accent (AJ, 2026-07-31). It is the only
     icon on the site that does, and it earns it: the control has no text label,
     so the mark is the whole affordance. */
  .theme-ico { color: var(--brand); }

  /* Chart tooltip. Positioned against .chart-plot, which is the only positioned
     ancestor, so it follows the pointer inside the scroller rather than the page. */
  .chart-plot { position: relative; }
  .chart-tip {
    position: absolute; z-index: 30; pointer-events: none;
    display: grid; gap: 1px; padding: 8px 10px; border-radius: 0;
    background: var(--surface); border: 1px solid var(--rule-edge);
    font-family: var(--ui); white-space: nowrap;
  }
  .chart-tip[hidden] { display: none; }
  .chart-tip-name { font-family: var(--data); font-size: 12px; color: var(--ink); }
  .chart-tip-meta { font-size: 11px; color: var(--muted); }
  .chart-tip-fig  { font-family: var(--serif); font-size: 21px; line-height: 1.1; color: var(--ink); margin-top: 2px; }

  /* Chart embed. /models does not load app.js, so it cannot use the ?embed=1 path
     the tool pages use; ?embed=chart is handled by the hub's own inline script.
     Everything except the workload strip and the chart is dropped. */
  .embed-chart body > *:not(main) { display: none !important; }
  .embed-chart main > *:not(.keep-embed) { display: none !important; }
  .embed-chart main { max-width: none; padding: 12px 14px; }
  .embed-chart .hide-embed { display: none !important; }
  /* The 42-name legend is the one part whose height runs away on a narrow host,
     and the iframe is a fixed height because no script of ours runs on the host
     page to resize it. Capping it keeps the embed inside its box. */
  .embed-chart #hlegend { max-height: 132px; overflow-y: auto; }
  .embed-chart #trajectory { margin-top: 0; padding-top: 0; border-top: 0; }

  /* The trajectory chart keeps a legible width on a phone and scrolls instead of
     being squashed: at 360px the 1100x560 viewBox renders very short otherwise. */
  .chart-scroll { overflow-x: auto; }
  @media (max-width: 640px) {
    .chart-scroll > svg { min-width: 720px; }
    .chart-scroll::after {
      content: "Chart scrolls sideways";
      position: sticky; left: 0; display: block; width: 100%; padding: 7px 2px 1px;
      font-family: var(--ui); font-size: 10.5px; letter-spacing: .12em;
      text-transform: uppercase; color: var(--muted);
    }
    /* 42 legend entries at 21px were under the 24px floor used everywhere else. */
    .hleg { padding-top: 4px; padding-bottom: 4px; }
  }

  /* The breakdown's section jump list. Sticky under the masthead, one line, and it
     scrolls sideways rather than wrapping so it never costs a second row
     (UI/UX audit F21, 2026-07-31). */
  .est-jump {
    position: sticky; top: 0; z-index: 20;
    display: flex; gap: 18px; overflow-x: auto; white-space: nowrap;
    margin: 0 0 4px; padding: 9px 0;
    background: var(--paper); border-bottom: 1px solid var(--rule-hair);
    font-family: var(--ui); font-size: 11.5px; letter-spacing: .1em; text-transform: uppercase;
  }
  .est-jump a { color: var(--muted); flex: none; }
  .est-jump a:hover { color: var(--ink); }
  #est-result [id^="est-s-"], #est-assumptions { scroll-margin-top: 56px; }

  /* Sortable column headings on /pricing and /models. The header cell carries
     aria-sort and this is its visible counterpart; there was previously no
     .sortable rule at all, so a sortable header did not even take a pointer
     cursor (UI/UX audit F17, 2026-07-31). The direction glyph is an arrow, not the
     delta chip's triangle, because a triangle already means a price moved. */
  th > button.sortable {
    display: inline; font: inherit; color: inherit; letter-spacing: inherit;
    text-transform: inherit; background: none; border: 0; padding: 0;
    text-align: inherit; cursor: pointer;
  }
  th > button.sortable:hover { color: var(--ink); }
  /* The label is 19px of text inside a 38px cell, so the hit area is stretched to
     the whole header cell rather than the words. */
  th[data-col] { position: relative; }
  th[data-col] > button.sortable::before { content: ""; position: absolute; inset: 0; }
  /* Non-breaking space, so the direction arrow can never wrap onto its own line
     away from the label it belongs to (AJ, 2026-07-31). */
  th[aria-sort="ascending"] > button.sortable::after  { content: "\00a0\2191"; }
  th[aria-sort="descending"] > button.sortable::after { content: "\00a0\2193"; }

  /* Two controls that open an in-page panel were anchors with href="#", so a
     failed handler scrolled the visitor to the top of the document instead of
     doing nothing (UI/UX audit F38). They are buttons now, and this keeps them
     reading as inline text rather than as chrome. */
  .link-like {
    display: inline; padding: 0; margin: 0; border: 0; background: none;
    font: inherit; letter-spacing: inherit; text-align: left; cursor: pointer;
  }

  /* Defined here rather than relied on from the Tailwind CDN: the announcements
     and the skip link are accessibility affordances and must not depend on a
     runtime script that can be blocked (UI/UX audit F22). */
  .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;
  }

  /* Skip link. Every page puts a wordmark, a five-item nav, a theme toggle and a
     scrolling price ticker ahead of the content, and a keyboard visitor had to tab
     through all of it on all 67 pages (UI/UX audit F5). Off screen until focused. */
  .skip-link {
    position: absolute; left: 8px; top: -80px; z-index: 100;
    padding: 10px 14px; background: var(--paper); color: var(--ink);
    border: 2px solid var(--ink); border-radius: 0;
    font-family: var(--ui); font-size: 13px; letter-spacing: .06em; text-transform: uppercase;
  }
  .skip-link:focus { top: 8px; }

  /* pulseGlow retired: Rev 02 permits exactly one arrival sequence and no other
     motion anywhere, and no shadows at all. A perpetually pulsing button was both. */

  /* The pinned live figure. Shown only while the estimator flow is on screen and
     the full answer card is not, so the number the visitor is working towards is
     never off screen (UI/UX audit F1, 2026-07-31). Ground shift plus a 2px rule is
     the system's elevation, which is why there is no shadow here: the 2px rule
     above a display figure is exactly the treatment DESIGN.md specifies. */
  .est-live {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    display: block; width: 100%; text-align: left; border-radius: 0;
    background: var(--paper); border: 0; border-top: 2px solid var(--ink);
    padding: 9px 16px calc(9px + env(safe-area-inset-bottom, 0px));
    cursor: pointer;
  }
  .est-live[hidden] { display: none; }
  .est-live-lab {
    display: block; font-family: var(--ui); font-size: 11px; line-height: 1.3;
    letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
  }
  .est-live-row {
    display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
  }
  .est-live .fig { font-size: 30px; line-height: 1.15; }
  .est-live-cta {
    font-family: var(--ui); font-size: 12px; color: var(--ink);
    border-bottom: 1px solid var(--ink); white-space: nowrap;
  }
  /* The strip overlays the foot of the estimator, so the section reserves room for
     it rather than letting it cover the last control. */
  body.est-live-on #estimator { padding-bottom: 84px; }

  /* A banked step in a stacked project. The default disclosure triangle is
     suppressed because the row already reads as a row: number, label, figure.
     The marker rules are duplicated because WebKit and the standard property
     are not interchangeable and only one of them lands per engine. */
  .est-step > summary { list-style: none; }
  .est-step > summary::-webkit-details-marker { display: none; }
  .est-step > summary::marker { content: ""; }
  /* Hide the body EXPLICITLY rather than trusting the browser default. Something
     in the cascade here (Tailwind preflight, or the FAQ disclosure rules) leaves
     details children laid out when closed, so a "collapsed" step still showed its
     model picker at full height and collapsing achieved nothing. Measured, not
     assumed: the picker reported 44px tall with the row shut. */
  .est-step:not([open]) > div { display: none; }
  .est-step > summary:hover { background: var(--surface); }
  /* Open state gets the ink rule the estimator uses for a chosen thing. */
  .est-step[open] > summary { border-bottom: 1px solid var(--rule-hair); }

  /* The project total, when a visitor stacks more than one workload. It is the
     .stat component DESIGN.md specifies: a 2px rule above a display figure, no
     box and no fill. It sits below the 62px single-workload answer rather than
     matching it, so the two read as part and whole instead of as two competing
     headlines. Declared after .stat so its display rule is the one that lands. */
  .est-total .fig { display: block; font-size: 40px; line-height: 1.05; letter-spacing: -0.03em; }

  /* A 15px square and a 12x24px glyph are not touch targets. Both keep their exact
     appearance and gain a centred 44px hit area instead, because growing the
     visible control would wreck an inline help mark and the modal corner
     (UI/UX audit F13, 2026-07-31). */
  .tip-btn, .modal-close { position: relative; }
  .tip-btn::after, .modal-close::after {
    content: ""; position: absolute; left: 50%; top: 50%;
    width: 44px; height: 44px; transform: translate(-50%, -50%);
  }

  /* Mobile: comfortable thumb targets (Apple HIG ~44px).
     The list named twelve ids and missed almost everything else on the site,
     including #gen-theme-toggle (the toggle on all 21 generated pages) and every
     estimator control, and it only ever set a height, so #theme-toggle stayed
     36px wide (UI/UX audit F13, 2026-07-31). */
  @media (max-width: 640px) {
    .preset-out, .preset-scale, .preset-arch, .prov-chip, .sample-chip,
    #btn-optimize, #btn-markdown, #btn-email, #btn-embed, #tip-next,
    .theme-toggle,
    [data-est-next], [data-est-back], [data-est-goto],
    [data-est-arch], [data-est-band], [data-est-size], [data-est-calls],
    [data-est-ctx], [data-est-tooling], [data-est-risk], [data-est-role],
    [data-est-add], [data-est-wl-edit], [data-est-wl-del],
    [data-est-format], [data-est-audience], [data-est-step-model],
    .chip-arch, #audit-sample, #est-open, #est-mail-cta {
      min-height: 44px;
    }
    /* The two row actions sit side by side inside a workload row, so they take a
       real gap rather than the 4px an inline chip carries. */
    [data-est-wl-edit] { margin-right: 16px; }
    .theme-toggle { min-width: 44px; }
    #results-body a { padding-top: 0.65rem; padding-bottom: 0.65rem; }
    /* Primary controls above get the 44px HIG target. Secondary navigation (the
       wordmark, the footer rows, breadcrumbs) is held to the WCAG 2.2 AA floor of
       24px instead, because making every footer link 44px would double the height
       of the footer to no one's benefit. */
    header .lp-wordmark { display: inline-flex; align-items: center; min-height: 32px; }
    footer a, .crumbs a, nav[aria-label="Breadcrumb"] a {
      display: inline-block; padding-top: 5px; padding-bottom: 5px;
    }
    /* Header nav sits on its own full-width row below md, so the taller targets
       cost one row of padding, and the masthead row gives most of it back. */
    header nav a, header .nav-tools > summary {
      display: inline-flex; align-items: center; min-height: 44px;
    }
    /* The sticky header already eats 15% of a 360x800 screen, so the taller nav
       targets are paid for out of the masthead's own padding and the row gap
       rather than added on top: 44px targets at the height the 20px ones had. */
    header > div:first-of-type {
      padding-top: .25rem; padding-bottom: .25rem; row-gap: 0;
    }
    /* FAQ rows were 26px of tappable area for a 13-question accordion. The FAQ
       accordions carry no class of their own, so this targets any disclosure in
       the content column; the Tools dropdown lives in the header and is unaffected. */
    main details > summary { min-height: 44px; padding-top: 9px; padding-bottom: 9px; }
  }

  /* Wide tables. Twelve pages carry tables 420 to 640px wide inside a 286 to 328px
     column, all in overflow-x:auto, and mobile scrollbars are invisible at rest, so
     the table looked cropped rather than scrollable and the label column scrolled
     away from its own numbers (UI/UX audit F8, 2026-07-31). The first column is
     pinned so every figure keeps its label, and the container states plainly that
     it moves. No gradient and no shadow: the rule and the ground do the work. */
  @media (max-width: 640px) {
    .overflow-x-auto > table th:first-child,
    .overflow-x-auto > table td:first-child {
      position: sticky; left: 0; z-index: 1;
      background: var(--paper); border-right: 1px solid var(--rule-hair);
    }
    .overflow-x-auto > table thead th:first-child { background: var(--surface); }
    .overflow-x-auto::after {
      content: "Table scrolls sideways";
      position: sticky; left: 0; display: block; width: 100%;
      padding: 7px 2px 1px;
      font-family: var(--ui); font-size: 10.5px; letter-spacing: .12em;
      text-transform: uppercase; color: var(--muted);
    }
  }
  @media (prefers-reduced-motion: reduce) {
    .fade-in { animation: none; }
    #drawer-optimize { transition: none; }
  }
  #drawer-optimize { transition: transform .3s ease; }
  .opt-pane { white-space: pre-wrap; word-break: break-word; }
  .embed-mode body, .embed-mode main { padding-bottom: 0 !important; }
  article p { margin-bottom: 1rem; line-height: 1.75; }
  article h3 { margin-top: 1.5rem; margin-bottom: .75rem; }
  /* #2B2825 is the dark-mode edge, so on paper this was a near-black rule: exactly
     the fixed-shade mistake DESIGN.md calls out by name (UI/UX audit F26). */
  .prose-table th, .prose-table td { padding: .5rem .75rem; border-bottom: 1px solid var(--rule-hair); text-align: left; }
  ::selection { background: rgba(24,22,18,0.16); }

