/* ============================================================
   FADER — shell.css   (Pass 3, Claude Design handback + fixes)

   Linked from every page that opts into the new shell:
       <link rel=stylesheet href="/static/shell.css">
   Nothing here needs brace-doubling — it is a static file, not
   an f-string.

   ADOPTION RULE, and the reason this file differs from the raw
   handback: the site is migrating page by page, so THEME's
   <style> block in webapp.py still ships alongside this file.
   Every rule here that would otherwise redefine a legacy
   element or class (html, body, a, input, ::scrollbar, .pill)
   is scoped under `body.app-shell`. Pages that have not been
   converted are byte-for-byte unaffected by loading this file.

   Owns: header, mobile sheet, chat dock, sub-bar, footer, gate,
   panel primitive, generic dense-table primitive, document
   archetype. Page CSS stays with the page.
   ============================================================ */

/* ---------- 1. tokens ---------------------------------------
   Values are IDENTICAL to the live THEME block, so loading this
   file on a legacy page is a no-op. The handback dropped
   --warnbg, --shadow and the whole --t-* type scale; they are
   restored here, because the live contract includes them and
   legacy page bodies (the FAQ, the desk, the calendar) render
   through them.
------------------------------------------------------------ */

:root {
  --sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
  --mono: ui-monospace, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;

  --bg: #f3f5f6;
  --panel: #ffffff;
  --panel2: #eaedef;
  --fg: #11161a;
  --fg2: #48525c;
  --mut: #7c8791;
  --line: #d6dbdf;
  --head: #e5e9eb;
  --up: #0f8a52;
  --down: #c8262f;
  --accent: #2f6096;
  --accent-ink: #ffffff;
  --warn: #a9700a;
  --warnbg: rgba(169, 112, 10, .14);
  --shadow: 0 1px 2px rgba(16, 24, 40, .06);

  --s1: oklch(0.55 0.115 200);
  --s2: oklch(0.55 0.115 235);
  --s3: oklch(0.55 0.115 270);
  --s4: oklch(0.55 0.115 305);
  --s5: oklch(0.55 0.115 340);

  --t-xs: 11px; --t-sm: 12.5px; --t-base: 14px; --t-md: 15px;
  --t-lg: 18px; --t-xl: 22px; --t-2xl: 28px; --t-hero: 34px;

  --row-h: 28px;
  --row-fs: 11.5px;

  /* The header is a fixed 40px and lives OUTSIDE the scrolling element,
     so nothing sticky inside the page needs to offset by it. Declared
     anyway because legacy page CSS reads it, and an unset var there
     silently fell back to 57px and floated the old /how TOC into the
     middle of the prose. */
  --hdrh: 40px;
}

[data-theme="dark"] {
  --bg: #0a0c0e;
  --panel: #101317;
  --panel2: #181d23;
  --fg: #dfe4ea;
  --fg2: #98a2ac;
  --mut: #606a75;
  --line: #1e242b;
  --head: #0d1014;
  --up: #3fbf7f;
  --down: #ef555f;
  --accent: #4d7fb8;
  --accent-ink: #eaf1f8;
  --warn: #dfa33a;
  --warnbg: rgba(223, 163, 58, .15);
  --shadow: 0 2px 10px rgba(0, 0, 0, .5);

  --s1: oklch(0.70 0.105 200);
  --s2: oklch(0.70 0.105 235);
  --s3: oklch(0.70 0.105 270);
  --s4: oklch(0.70 0.105 305);
  --s5: oklch(0.70 0.105 340);
}

/* density, set on <html> by shell.js */
[data-dens="S"] { --row-h: 22px; --row-fs: 10.5px; }
[data-dens="M"] { --row-h: 28px; --row-fs: 11.5px; }
[data-dens="L"] { --row-h: 38px; --row-fs: 12.5px; }

/* Folded row heights. data-lines is per-TABLE, not on <html>:
   a page may carry two tables at different widths (see /rank),
   and the handback's document-level flag made the last table to
   settle win for all of them. */
[data-dens="S"] .tbl[data-lines="2"], [data-dens="S"] .tbl[data-lines="3"] { --row-h: 18px; }
[data-dens="M"] .tbl[data-lines="2"], [data-dens="M"] .tbl[data-lines="3"] { --row-h: 24px; }
[data-dens="L"] .tbl[data-lines="2"], [data-dens="L"] .tbl[data-lines="3"] { --row-h: 34px; }

/* ---------- 2. reset — SHELL PAGES ONLY ---------- */

.app-shell * { box-sizing: border-box; }
html:has(body.app-shell), body.app-shell {
  margin: 0;
  height: 100%;
  max-width: none;
  padding: 0;
}

body.app-shell {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

.app-shell a {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in oklab, var(--accent) 40%, transparent);
}
.app-shell a:hover { color: color-mix(in oklab, var(--accent) 80%, white); text-decoration: none; }

.app-shell input,
.app-shell button,
.app-shell textarea,
.app-shell select { font-family: var(--mono); }
.app-shell button { cursor: pointer; }

.app-shell ::-webkit-scrollbar { width: 10px; height: 10px; }
.app-shell ::-webkit-scrollbar-thumb { background: var(--panel2); border: 2px solid var(--bg); }

.app-shell :where(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.mono-label { font: 600 8.5px var(--mono); letter-spacing: .14em; color: var(--mut); }
.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;
}

/* ---------- 3. app frame ---------- */

.app { height: 100%; min-height: 0; display: flex; flex-direction: column; overflow: hidden; position: relative; }
.app__body { flex: 1; min-height: 0; display: flex; }
.app__main { flex: 1; min-width: 0; min-height: 0; overflow: auto; display: flex; flex-direction: column; }
/* grow to fill a short page, but never shrink below content — `flex:1` alone
   (the handback) caps the page at the scroll container's height and clips
   long prose at the fold. */
.app__page { flex: 1 0 auto; }

/* ---------- 4. header ---------- */

.hdr {
  flex: none;
  height: 40px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 10px;
  background: var(--head);
  border-bottom: 1px solid var(--line);
}

.hdr__mark { font: 700 12px var(--mono); letter-spacing: .14em; color: var(--fg); white-space: nowrap; border: 0; }
.app-shell .hdr__mark:hover { color: var(--fg); }
.hdr__spacer { flex: 1; min-width: 0; }

.hdr__tier { display: flex; align-items: center; gap: 5px; padding: 2px 6px; border: 1px solid var(--line); white-space: nowrap; }
.hdr__tier i { width: 5px; height: 5px; background: var(--fg2); }
.hdr__tier span { font: 8.5px var(--mono); letter-spacing: .12em; color: var(--fg2); }

.nav { display: flex; gap: 1px; align-items: center; min-width: 0; }
.app-shell .nav__a {
  font: 10px var(--mono);
  letter-spacing: .09em;
  padding: 4px 7px;
  white-space: nowrap;
  color: var(--fg2);
  border: 0;
  border-bottom: 2px solid transparent;
}
.app-shell .nav__a:hover { color: var(--fg); }
.app-shell .nav__a[aria-current="page"] { color: var(--fg); font-weight: 600; border-bottom-color: var(--accent); }

/* The Desk group is ABSENT from the DOM for sessions without it —
   never rendered-then-hidden, never present-and-disabled. Its
   routes 404 rather than 403, so membership is never revealed. */
.navgrp { position: relative; }
.navgrp__btn { font: 10px var(--mono); letter-spacing: .09em; padding: 4px 7px; color: var(--fg2); background: none; border: 0; border-bottom: 2px solid transparent; }
.navgrp__btn[aria-expanded="true"] { color: var(--fg); border-bottom-color: var(--accent); }
.navgrp__menu {
  position: absolute; top: 30px; left: 0; z-index: 60;
  min-width: 190px;
  background: var(--panel);
  border: 1px solid var(--line);
  display: flex; flex-direction: column;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .4);
}
.navgrp__menu[hidden] { display: none; }
.navgrp__head { font: 8px var(--mono); letter-spacing: .14em; color: var(--mut); padding: 7px 10px 4px; }
.app-shell .navgrp__a { font: 10.5px var(--mono); letter-spacing: .06em; padding: 6px 10px; color: var(--fg2); border: 0; border-top: 1px solid var(--line); }
.app-shell .navgrp__a:hover { color: var(--fg); background: var(--panel2); }

/* regime chip — market state only. The page-owned veto line
   renders in .subbar, never here. */
.regime { display: flex; align-items: center; gap: 8px; padding: 3px 8px; border: 1px solid var(--line); white-space: nowrap; }
.regime__k { font: 8px var(--mono); letter-spacing: .13em; color: var(--mut); }
.regime__m { display: flex; align-items: baseline; gap: 4px; font: 10px var(--mono); font-variant-numeric: tabular-nums; }
.regime__m b { font-weight: 400; color: var(--mut); }

.iconbtn { display: flex; align-items: center; gap: 5px; padding: 4px 7px; background: none; border: 1px solid var(--line); color: var(--fg2); }
.iconbtn[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); }
.iconbtn span { font: 9.5px var(--mono); letter-spacing: .08em; }

.seg { display: flex; border: 1px solid var(--line); }
.seg > button { font: 600 9px var(--mono); letter-spacing: .08em; padding: 4px 7px; background: none; border: 0; border-left: 1px solid var(--line); color: var(--fg2); }
.seg > button:first-child { border-left: 0; }
.seg > button[aria-pressed="true"] { color: var(--accent-ink); background: var(--accent); }

.acct { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
.acct__email { font: 10px var(--mono); color: var(--fg2); max-width: 170px; overflow: hidden; text-overflow: ellipsis; }
.btn-primary { font: 600 9.5px var(--mono); letter-spacing: .08em; padding: 5px 10px; color: var(--accent-ink); background: var(--accent); border: 1px solid var(--accent); }
.btn-ghost { font: 600 9.5px var(--mono); letter-spacing: .08em; padding: 5px 10px; color: var(--fg); background: none; border: 1px solid var(--line); }
.btn-ghost:hover { border-color: var(--fg2); }

.burger { display: none; flex-direction: column; gap: 3px; padding: 8px 4px; background: none; border: 0; }
.burger i { width: 16px; height: 1.5px; background: var(--fg); }

/* header collapse */
@media (max-width: 1239px) {
  .hdr__tier, .regime { display: none; }
  .iconbtn span { display: none; }
}
@media (max-width: 1079px) {
  .hdr .nav, .hdr .navgrp, .hdr .seg, .hdr .acct { display: none; }
  .burger { display: flex; }
}

/* ---------- 5. mobile sheet ---------- */

.sheet { position: absolute; inset: 40px 0 0 0; z-index: 70; background: color-mix(in oklab, var(--bg) 82%, transparent); display: flex; flex-direction: column; }
.sheet[hidden] { display: none; }
.sheet__panel { background: var(--panel); border-bottom: 1px solid var(--line); display: flex; flex-direction: column; max-height: 100%; overflow: auto; }
/* display:block is load-bearing — these are <a> elements, so without it the
   whole mobile menu laid out inline and wrapped mid-label ("HOW IT WORKS"
   breaking across two rows), which is what the sheet exists to avoid. */
.app-shell .sheet__a { display: block; font: 12px var(--mono); letter-spacing: .08em; padding: 13px 14px; color: var(--fg2); border: 0; border-bottom: 1px solid var(--line); border-left: 2px solid transparent; text-align: left; background: none; }
.app-shell .sheet__a[aria-current="page"] { color: var(--fg); font-weight: 600; border-left-color: var(--accent); }
.sheet__grp { background: var(--panel2); }
.sheet__grphead { font: 8.5px var(--mono); letter-spacing: .14em; color: var(--mut); padding: 11px 14px 5px; background: var(--panel2); }
.sheet__row { padding: 13px 14px; border-bottom: 1px solid var(--line); display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.sheet__row .seg > button { padding: 7px 12px; font-size: 10px; }
.sheet__hint { padding: 12px 14px; display: flex; align-items: center; justify-content: space-between; font: 9.5px var(--mono); color: var(--mut); }

/* ---------- 6. sub-bar (page-owned context line) ---------- */

.subbar { flex: none; display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-bottom: 1px solid var(--line); background: var(--bg); flex-wrap: wrap; }
.subbar > i { width: 3px; height: 11px; background: var(--line); flex: none; }
.subbar--warn > i { background: var(--warn); }
.subbar--warn .subbar__t { color: var(--warn); }
.subbar__t { font: 9.5px var(--mono); letter-spacing: .06em; color: var(--fg2); }
.subbar__m { font: 9.5px var(--mono); color: var(--mut); }

/* ---------- 7. chat dock — takes layout width, never overlays ---------- */

.chat { width: 340px; flex: none; min-height: 0; border-left: 1px solid var(--line); background: var(--panel); display: flex; flex-direction: column; }
.chat[hidden] { display: none; }
.chat__hd { flex: none; display: flex; align-items: center; gap: 8px; padding: 9px 11px; border-bottom: 1px solid var(--line); }
.chat__log { flex: 1; min-height: 0; overflow: auto; padding: 11px; display: flex; flex-direction: column; gap: 11px; }
.chat__msg { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; }
.chat__msg--me { align-items: flex-end; }
.chat__who { font: 8px var(--mono); letter-spacing: .12em; color: var(--mut); }
.chat__bubble { max-width: 92%; font-family: var(--sans); font-size: 12px; line-height: 1.55; color: var(--fg2); border: 1px solid var(--line); padding: 7px 9px; }
.chat__msg--me .chat__bubble { color: var(--fg); background: var(--panel2); border-color: var(--panel2); }
.chat__in { flex: none; border-top: 1px solid var(--line); padding: 9px; display: flex; gap: 7px; }
.app-shell .chat__in input { flex: 1; min-width: 0; font: 11px var(--mono); color: var(--fg); background: var(--bg); border: 1px solid var(--line); border-radius: 0; padding: 8px; outline: none; }

/* below 760 chat becomes a full sheet — still never a floating button */
@media (max-width: 759px) {
  .chat { position: absolute; inset: 40px 0 0 0; width: auto; z-index: 80; border-left: 0; }
}

/* The two screens dock the real _chat_widget here rather than the presentational
   dock above, because that widget is wired to /chat and picks up a ticker from
   any row. It ships as a fixed floating panel with a launcher button; inside the
   shell both of those are overridden so it takes layout width instead. The
   floating launcher is the specific thing Pass 3 set out to remove — it
   permanently covered the last row of the table. */
.app-shell #chatfab { display: none !important; }
.app-shell #chatpanel.chat {
  position: static;
  width: 340px;
  height: auto;
  max-height: none;
  flex: none;
  min-height: 0;
  inset: auto;
  border: 0;
  border-left: 1px solid var(--line);
  border-radius: 0;
  box-shadow: none;
  background: var(--panel);
  z-index: auto;
}
#chatpanel.hide { display: none; }

/* On a phone the assistant is a BOTTOM SHEET, not a full-screen takeover.
   It used to cover the viewport from the header down, which hid the very
   table you were asking about; now it takes the lower half and the rows stay
   readable above it. */
@media (max-width: 759px) {
  .app-shell #chatpanel.chat {
    position: absolute;
    inset: auto 0 0 0;
    height: min(62vh, 460px);
    width: auto;
    z-index: 80;
    border-left: 0;
    border-top: 1px solid var(--line);
    box-shadow: 0 -10px 30px rgba(0, 0, 0, .28);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
}

/* Undocked pages (/desk/jobs) float the same widget instead of docking it, so
   it needs its own frame. The docked rules above are scoped to #chatpanel.chat
   and win where they apply. */
#chatpanel {
  position: fixed;
  right: 16px;
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  width: min(380px, 94vw);
  height: min(70vh, 520px);
  z-index: 301;
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--line);
  box-shadow: 0 16px 44px rgba(0, 0, 0, .34);
}
@media (max-width: 759px) {
  #chatpanel { right: 0; left: 0; bottom: 0; width: auto; height: min(62vh, 460px);
    border-left: 0; border-right: 0; }
}

/* the assistant's own furniture — chips, intro, launcher */
.chat__intro { display: flex; flex-direction: column; gap: 9px; }
.chat__intro p { margin: 0; font-size: 12px; line-height: 1.55; color: var(--fg2); }
.chat__chips { display: flex; flex-wrap: wrap; gap: 5px; }
.chat__chip {
  font: 10.5px var(--mono);
  letter-spacing: .02em;
  text-align: left;
  padding: 5px 7px;
  color: var(--fg2);
  background: none;
  border: 1px solid var(--line);
}
.chat__chip:hover { color: var(--fg); border-color: var(--fg2); background: var(--panel2); }
.chat__log { scroll-behavior: smooth; }
.chat__bubble { white-space: pre-wrap; }

/* the floating launcher, for pages that are not the docked screens */
#chatfab {
  position: fixed;
  right: 16px;
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  z-index: 300;
  font: 600 9.5px var(--mono);
  letter-spacing: .12em;
  padding: 10px 15px;
  color: var(--accent-ink);
  background: var(--accent);
  border: 1px solid var(--accent);
  cursor: pointer;
}
#chatfab:hover { background: color-mix(in oklab, var(--accent) 86%, black); }

/* ---------- 8. panel primitive ---------- */

.grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 10px; }
@media (max-width: 1079px) { .grid { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
@media (max-width: 759px)  { .grid { grid-template-columns: minmax(0, 1fr); } }

.panel { border: 1px solid var(--line); background: var(--panel); display: flex; flex-direction: column; min-width: 0; }
.panel--pad { padding: 11px 12px; gap: 8px; }
.panel__hd { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.panel__hd h3 { margin: 0; font: 600 8.5px var(--mono); letter-spacing: .14em; color: var(--mut); }
.panel__hd .meta { font: 9px var(--mono); color: var(--mut); }
/* exactly one number per panel gets size */
.panel__big { font: 600 21px var(--mono); font-variant-numeric: tabular-nums; color: var(--fg); }
.panel--hero .panel__big { font-size: 30px; letter-spacing: -.01em; }
.panel__sub { font: 9.5px var(--mono); line-height: 1.4; color: var(--mut); }
.panel__bar { height: 6px; background: var(--panel2); }
.panel__bar > i { display: block; height: 6px; background: var(--fg2); }

/* Panel-dashboard pages (/rank, /portfolio) inset their content. The
   full-bleed bands — .subbar, .paper — deliberately sit OUTSIDE this, so they
   still rule edge to edge; everything else was previously flush against the
   viewport with the h1 touching x=0. */
.page { padding: 16px 18px 26px; display: flex; flex-direction: column; gap: 12px; }
.page > h1 { margin: 0; font-size: 27px; font-weight: 600; letter-spacing: -.015em; line-height: 1.15; }
.page > p { margin: 0; }
@media (max-width: 759px) { .page { padding: 13px 13px 22px; } }

/* Prose inside a panel: the panel supplies the rhythm through its own gap, so
   a <p> must not also carry a 1em browser margin — that is what spread the
   four cadence rules on /rank down an entire screen. */
.panel p { margin: 0; font-size: 13px; line-height: 1.6; color: var(--fg2); }
.panel p b { color: var(--fg); font-weight: 600; }
/* a panel whose body is a set of short rules reads better as columns */
.panel__rules { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 12px 22px; }

.span-hero { grid-column: span 6; }
.span-stat { grid-column: span 3; }
.span-half { grid-column: span 6; }
.span-wide { grid-column: span 12; }
@media (max-width: 1079px) {
  .span-hero, .span-half, .span-wide { grid-column: span 6; }
  .span-stat { grid-column: span 3; }
}
@media (max-width: 759px) {
  .span-hero, .span-half, .span-wide { grid-column: span 1; }
  /* Stat panels stay TWO up on a phone. One-up turned five of them into 430px
     of stacked boxes you had to scroll past to reach the positions table. */
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .span-hero, .span-half, .span-wide { grid-column: span 2; }
  .span-stat { grid-column: span 1; }
  .panel--hero .panel__big { font-size: 25px; }
}

/* the bot switcher on /portfolio */
.bot-tabs { display: flex; gap: 6px; flex-wrap: wrap; }
.app-shell a.bot-tab {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 7px 11px; text-transform: none; letter-spacing: .04em;
  font: 11px var(--mono); color: var(--fg2);
}
.app-shell a.bot-tab.is-on { color: var(--fg); border-color: var(--fg2); font-weight: 600; }
.bot-tab__sub { font-size: 9.5px; color: var(--mut); }
@media (max-width: 759px) { .bot-tab__sub { display: none; } }

/* ---------- 9. paper-money band ---------- */

.paper { display: flex; align-items: stretch; border: 1px solid color-mix(in oklab, var(--warn) 55%, transparent); background: color-mix(in oklab, var(--warn) 8%, transparent); }
.paper > i { width: 4px; background: var(--warn); flex: none; }
.paper__t { padding: 10px 12px; display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.paper__t b { font: 700 11px var(--mono); letter-spacing: .16em; color: var(--warn); }
.paper__t span { font-size: 12px; line-height: 1.5; color: var(--fg2); }

/* ---------- 10. generic dense-table primitive ----------------
   NOT the Fade screen. /screener and /reverse ship their own
   richer table in static/screener.css (.screen-table) with its
   own fold engine; this is the light version for panel-dashboard
   pages (/rank standings, /desk journal) that need a dense table
   without the encoding library. The two vocabularies do not
   overlap — verified, zero class collisions.
------------------------------------------------------------ */

.tbl { width: 100%; }
.tbl__hd { position: sticky; top: 0; z-index: 20; display: grid; gap: 4px; align-items: center; padding: 0 10px; height: 24px; background: var(--head); border-bottom: 1px solid var(--line); font: 600 8.5px var(--mono); letter-spacing: .12em; color: var(--fg2); }
.tbl__hd button { background: none; border: 0; color: inherit; font: inherit; letter-spacing: inherit; padding: 0; text-align: left; white-space: nowrap; }
.tbl__hd button[aria-sort] { color: var(--fg); }
.tbl__row { display: grid; gap: 4px; align-items: center; padding: 0 10px; min-height: var(--row-h); border-bottom: 1px solid var(--line); border-left: 2px solid transparent; font-family: var(--mono); font-size: var(--row-fs); font-variant-numeric: tabular-nums; color: var(--fg); }
.tbl__row[aria-selected="true"] { background: var(--panel2); border-left-color: var(--accent); }
.tbl__row--screened { border-left-color: var(--down); background: color-mix(in oklab, var(--down) 7%, transparent); color: var(--mut); }
.tbl__stick { position: sticky; left: 0; z-index: 1; background: var(--bg); display: flex; align-items: center; gap: 5px; min-width: 0; padding-right: 4px; }
.tbl__row[aria-selected="true"] .tbl__stick { background: var(--panel2); }

/* Column widths come in as the custom property --cols, NOT as an inline
   grid-template-columns. An inline declaration beats any stylesheet rule, so
   setting the columns directly made the fold below a no-op: the table gained
   .tbl--fold and showed its per-cell labels, but kept its 8-column desktop
   grid and overflowed its panel on a phone. */
.tbl__hd, .tbl__row { grid-template-columns: var(--cols, repeat(6, minmax(0, 1fr))); }

/* folded rows: fixed fr grid so a cell lands in the same slot on every row */
.tbl--fold .tbl__hd { display: none; }
.tbl--fold .tbl__row { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 3px 8px; padding: 6px 8px; }
.tbl--fold3 .tbl__row { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.tbl--fold .tbl__stick { position: static; }
.tbl__lab { display: none; font: 8px var(--mono); letter-spacing: .1em; color: var(--mut); }
.tbl--fold .tbl__lab { display: inline; }

/* Named .fpill, not .pill: the legacy THEME block owns a rounded .pill
   that the FAQ, calendar and desk still render, and a distinct name
   cannot collide even inside the .app-shell scope. */
.fpill { display: inline-block; font-family: var(--sans); font-size: 8px; line-height: 12px; white-space: nowrap; letter-spacing: .07em; padding: 1px 3px; border: 1px solid var(--line); border-radius: 0; color: var(--mut); background: none; font-weight: 400; }
.fpill--down { color: var(--down); border-color: color-mix(in oklab, var(--down) 55%, transparent); background: color-mix(in oklab, var(--down) 16%, transparent); }
.fpill--warn { color: var(--warn); border-color: color-mix(in oklab, var(--warn) 50%, transparent); }
.fpill--good { color: var(--up); border-color: color-mix(in oklab, var(--up) 55%, transparent); }

.side { font-size: 8.5px; font-weight: 600; line-height: 12px; letter-spacing: .07em; padding: 2px 4px; }
.side--put { color: var(--up); background: color-mix(in oklab, var(--up) 16%, transparent); }
.side--call { color: var(--down); background: color-mix(in oklab, var(--down) 16%, transparent); }

.score { display: flex; height: calc(var(--row-fs) - 3px); background: var(--panel2); }
.score > i { display: block; }
.score .pen { background: repeating-linear-gradient(135deg, var(--down) 0 2px, transparent 2px 4px); }
.score .bonus { background: repeating-linear-gradient(45deg, var(--fg2) 0 1.5px, transparent 1.5px 4px); border: 1px solid var(--fg2); }

.track { position: relative; height: calc(var(--row-fs) - 4px); background: var(--panel2); }
.track > i { position: absolute; top: -2px; bottom: -2px; width: 2px; background: var(--fg); }

/* ---------- 11. document archetype ---------------------------
   One grid with three named zones. Prose never leaves `text`, so
   line length is identical at 768 and at 1440 — only the figures
   grow. `wide` reaches 150px further each side, `full` goes edge
   to edge, which is how a 1346px table row gets shown on a page
   whose measure is 68ch.
------------------------------------------------------------ */

.doc { display: flex; align-items: flex-start; min-width: 0; }

.doc__grid {
  flex: 1;
  min-width: 0;
  display: grid;
  row-gap: 26px;
  padding: 30px 0 40px;
  grid-template-columns:
    [full-start] minmax(14px, 1fr)
    [wide-start] minmax(0, 150px)
    [text-start] min(68ch, calc(100% - 28px))
    [text-end]   minmax(0, 150px)
    [wide-end]   minmax(14px, 1fr)
    [full-end];
}
.doc__grid > * { grid-column: text; min-width: 0; }
.doc__grid > .zone-wide { grid-column: wide; }
.doc__grid > .zone-full { grid-column: full; }

/* A section's own tables are content, not figures — let them use the
   `wide` gutters rather than squeezing a five-column table into 68ch.
   Below 1080 they stay in the measure and scroll if they must. */
.doc__sec table { width: 100%; border-collapse: collapse; font-size: 13px; }
@media (min-width: 1080px) {
  .doc__sec > table { width: calc(100% + 300px); margin-left: -150px; }
}
.doc__sec img { max-width: 100%; height: auto; }

/* the jump rail — lives in the left gutter, which the prose was
   never going to use. Sticky to top:0 of the scroller, not to a
   magic header offset. */
.rail { position: sticky; top: 0; width: 216px; flex: none; padding: 26px 10px 20px 16px; display: flex; flex-direction: column; gap: 2px; }
.rail__hd { font: 600 8.5px var(--mono); letter-spacing: .16em; color: var(--mut); padding: 0 6px 8px; }
.app-shell .rail__a { display: flex; gap: 8px; align-items: baseline; font: 10.5px var(--mono); letter-spacing: .03em; padding: 4px 6px; color: var(--fg2); border: 0; border-left: 2px solid transparent; }
.app-shell .rail__a:hover { color: var(--fg); background: var(--panel); }
.app-shell .rail__a[aria-current="true"] { color: var(--fg); background: var(--panel); border-left-color: var(--accent); }
.rail__a i { color: var(--mut); font-style: normal; }
.rail__pct { margin-top: 12px; padding: 8px 6px 0; border-top: 1px solid var(--line); font: 9px/1.5 var(--mono); color: var(--mut); }

/* below 1240 the rail becomes a sticky one-line bar with a
   drop-down section list, and a 2px progress bar rides its top */
.docbar { display: none; position: sticky; top: 0; z-index: 30; flex-direction: column; background: color-mix(in oklab, var(--bg) 92%, transparent); border-bottom: 1px solid var(--line); }
.progress { height: 2px; background: transparent; }
.progress > i { display: block; height: 2px; width: 0; background: var(--accent); }
.docbar__row { display: flex; align-items: center; gap: 8px; padding: 7px 12px; }
.docbar__cur { font: 9.5px var(--mono); letter-spacing: .06em; color: var(--fg); min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.docbar__list { display: flex; flex-direction: column; border-top: 1px solid var(--line); background: var(--panel); max-height: 60vh; overflow: auto; }
.docbar__list[hidden] { display: none; }
.app-shell .docbar__list .rail__a { font-size: 11.5px; padding: 11px 13px; border-bottom: 1px solid var(--line); }

@media (max-width: 1239px) {
  .rail { display: none; }
  .docbar { display: flex; }
}

.doc__col { width: 100%; max-width: 68ch; display: flex; flex-direction: column; gap: 22px; }
.doc h1 { margin: 0; font-size: 31px; line-height: 1.15; letter-spacing: -.015em; font-weight: 600; color: var(--fg); }
.doc h2 { margin: 0; font-size: 19px; line-height: 1.25; font-weight: 600; letter-spacing: -.005em; color: var(--fg); }
.doc h3 { margin: 0; font-size: 15px; line-height: 1.3; font-weight: 600; color: var(--fg); }
.doc p  { margin: 0; font-size: 14.5px; line-height: 1.7; color: var(--fg2); text-wrap: pretty; }
.doc ul, .doc ol { margin: 0; padding-left: 20px; font-size: 14.5px; line-height: 1.7; color: var(--fg2); }
.doc li + li { margin-top: 5px; }
.doc__lead { font-size: 15px; line-height: 1.65; }
.doc__rule { height: 1px; background: var(--line); border: 0; margin: 0; }
.doc__ex { display: flex; flex-direction: column; gap: 6px; border: 1px solid var(--line); background: var(--panel); padding: 12px; }
.doc__cap { font: 9px var(--mono); line-height: 1.5; color: var(--mut); }
/* a document page scrolls its own column; wide embeds scroll inside themselves */
.doc__scroll { overflow-x: auto; }
.doc__kicker { font: 600 9px var(--mono); letter-spacing: .2em; color: var(--mut); }
.doc__num { font: 600 9px var(--mono); letter-spacing: .18em; color: var(--mut); }
.doc__sec { display: flex; flex-direction: column; gap: 10px; }
.doc__fig { display: flex; flex-direction: column; gap: 6px; }
.doc__fighd { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; padding: 0 4px; }
.doc__figt { font: 600 8.5px var(--mono); letter-spacing: .14em; color: var(--mut); }
.doc__figm { font: 9px var(--mono); color: var(--mut); }
.doc__figbody { border: 1px solid var(--line); background: var(--panel); }
@media (max-width: 759px) {
  .doc__grid { padding: 18px 0 26px; }
  .doc h1 { font-size: 25px; }
}

/* ---------- 12. signed-out gate ---------- */

/* Real chrome, real headers, then structure-only skeletons.
   No blur: blurred real data is still data, and blurred fake
   data is a lie. How many real rows show above the skeletons is
   a product decision, set by the page, not by this file. */
.gate__skel { display: grid; gap: 4px; align-items: center; padding: 0 10px; min-height: var(--row-h); border-bottom: 1px solid var(--line); }
.gate__skel i { display: block; height: 7px; background: var(--panel2); }
.gate__cta { display: flex; justify-content: center; padding: 18px 12px 26px; }
.gate__card { width: 100%; max-width: 560px; border: 1px solid var(--line); background: var(--panel); padding: 16px; display: flex; flex-direction: column; gap: 11px; }
.gate__card h2 { margin: 0; font-family: var(--sans); font-size: 16px; line-height: 1.35; font-weight: 600; color: var(--fg); }
.gate__card p { margin: 0; font-family: var(--sans); font-size: 12.5px; line-height: 1.55; color: var(--fg2); }
.gate__form { display: flex; gap: 8px; flex-wrap: wrap; }
.app-shell .gate__form input { flex: 1; min-width: 180px; font: 11.5px var(--mono); color: var(--fg); background: var(--bg); border: 1px solid var(--line); border-radius: 0; padding: 9px 10px; outline: none; }

/* ---------- 13. footer ---------- */

.foot { flex: none; border-top: 1px solid var(--line); background: var(--head); padding: 14px 18px; display: flex; flex-direction: column; gap: 11px; }
.foot__top { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.foot__mark { font: 700 11px var(--mono); letter-spacing: .14em; color: var(--fg2); }
.foot__links { display: flex; gap: 14px; flex-wrap: wrap; }
.app-shell .foot__links a { font: 10.5px var(--mono); letter-spacing: .05em; color: var(--fg2); border-bottom-color: var(--line); }
.foot__meta { font: 9.5px var(--mono); color: var(--mut); }
.foot__legal { display: flex; gap: 10px; align-items: stretch; border-top: 1px solid var(--line); padding-top: 10px; }
.foot__legal > i { width: 2px; background: var(--line); flex: none; }
.foot__legal p { margin: 0; font-family: var(--sans); font-size: 11.5px; line-height: 1.6; color: var(--fg2); max-width: 960px; text-wrap: pretty; }
.foot__legal b { font-family: var(--mono); font-size: 9px; font-weight: 400; letter-spacing: .14em; color: var(--mut); }
@media (max-width: 759px) { .foot { padding: 12px 14px 18px; } }

/* ---------- 14. legacy page bodies inside the shell ----------
   `_page()` wraps any not-yet-rebuilt page body in `.legacy` and
   renders it through `_shell_page`. The body's own markup is
   untouched and still resolves against THEME's classes; all this
   block does is restore the padding and measure the old
   `body{max-width:1340px;padding:18px 22px 56px}` rule used to
   provide, and repair the three things that assumed the page
   itself was the scrolling element.
------------------------------------------------------------ */

.app-shell .legacy {
  max-width: 1340px;
  margin: 0 auto;
  padding: 18px 22px 40px;
  min-width: 0;
}
/* the old inline max-width:900px — for pages that really are one column of
   prose (a confirmation, a sign-in result), not for the table pages */
.app-shell .legacy--narrow { max-width: 900px; margin: 0; }

@media (max-width: 759px) {
  .app-shell .legacy { padding: 14px 13px 26px; }
}

/* The header now lives OUTSIDE the scroller, so a sticky column header must
   pin to the top of the scroller, not 40px down it. --hdrh stays as it is —
   the /how rail reads it. */
.app-shell .ftable thead th { top: 0; }

/* Legacy tables were authored for a full-width document and several are wider
   than a phone. Below the shell's own collapse width they scroll inside their
   own box so the PAGE never scrolls sideways. Above it they must NOT be in a
   scroll container, or the sticky thead stops pinning. */
.app-shell .scrollx { min-width: 0; }
@media (max-width: 1079px) {
  .app-shell .scrollx { overflow-x: auto; -webkit-overflow-scrolling: touch; }
}

/* the old page header is gone from every route; if a body still emits one,
   it must not draw a second nav */
.app-shell .apphdr { display: none; }

/* --- legacy primitives, retuned to Terminal Slate -------------
   The page bodies still emit .card / .btn / .pill / .kv, which were drawn for
   the pre-redesign look: 14px radii, drop shadows, pill-shaped everything.
   Next to /screener that reads as a different product, so the primitives are
   restyled here rather than rewritten in a dozen page bodies. Square corners,
   hairline rules, no shadow, mono for controls and labels — colour still only
   ever means data, so none of this introduces a hue.
------------------------------------------------------------ */

.app-shell .card,
.app-shell .tablewrap,
.app-shell #modalbox,
.app-shell .chatlog { border-radius: 0; box-shadow: none; }
.app-shell .card { padding: 13px 14px; margin: 10px 0; }

/* `.app-shell a` (0,2,0) outranks THEME's `.btn` (0,1,0), so an <a class=btn>
   inherited the link colour and rendered accent-on-accent — the Desk's "Log a
   fill" button was an empty blue rectangle. Set the colour here, where the
   specificity matches, and kill the link underline these gain as anchors. */
.app-shell a.btn { color: var(--accent-ink); border-bottom-style: solid; }
.app-shell a.btn.ghost { color: var(--fg); }
.app-shell .btn {
  border-radius: 0;
  color: var(--accent-ink);
  font: 600 9.5px var(--mono);
  letter-spacing: .08em;
  text-transform: uppercase;
  padding: 8px 12px;
  border: 1px solid var(--accent);
}
.app-shell .btn.ghost { border-color: var(--line); color: var(--fg); }
.app-shell .btn:hover { filter: none; background: color-mix(in oklab, var(--accent) 86%, black); }
.app-shell .btn.ghost:hover { background: var(--panel2); border-color: var(--fg2); }

.app-shell input,
.app-shell select,
.app-shell textarea { border-radius: 0; }
.app-shell input, .app-shell select { font: 11.5px var(--mono); padding: 8px 10px; }

.app-shell .pill,
.app-shell .watchadd {
  border-radius: 0;
  font: 9px var(--mono);
  letter-spacing: .06em;
  padding: 2px 5px;
  font-weight: 400;
  border: 1px solid var(--line);
  background: none;
}
.app-shell .pill.warn { color: var(--warn); border-color: color-mix(in oklab, var(--warn) 50%, transparent); }
.app-shell .pill.bad  { color: var(--down); border-color: color-mix(in oklab, var(--down) 55%, transparent); background: color-mix(in oklab, var(--down) 12%, transparent); }
.app-shell .pill.good { color: var(--up); border-color: color-mix(in oklab, var(--up) 55%, transparent); }
.app-shell .ftags .pill { font-size: 8.5px; padding: 1px 4px; }

.app-shell .mtab {
  border-radius: 0;
  font: 600 9.5px var(--mono);
  letter-spacing: .08em;
  text-transform: uppercase;
  padding: 7px 12px;
}

/* headings: one ramp with the document archetype, not the old 800-weight one */
.app-shell .legacy h1 { font-size: 27px; font-weight: 600; letter-spacing: -.015em; }
.app-shell .legacy h2 { font-weight: 600; }
.app-shell .legacy h3 { font-weight: 600; }
/* the legacy hero paragraph was sized against a 25px logo that no longer exists */
.app-shell .legacy .hero { font-size: 14.5px; line-height: 1.65; }

/* dense-ish legacy tables read closer to .tbl without being rebuilt */
.app-shell .ftable thead th { font-family: var(--mono); letter-spacing: .1em; }
.app-shell .kv td { border-bottom-color: var(--line); }

/* ---------- 15. print ---------- */

@media print {
  .hdr, .sheet, .chat, .subbar { display: none !important; }
  .app, .app__main { height: auto; overflow: visible; }
  .panel { break-inside: avoid; }
}
