/* app.css — the whole page's styles, moved out of index.html's <style> block
   on 2026-08-21 for ONE measured reason.

   index.html is served BY NODE with `Cache-Control: no-cache` because it
   carries the `?v=` stamps (asset-stamp.js), and Cloudflare marks it DYNAMIC
   and STRIPS the ETag when it brotli-compresses it — verified against
   production: the origin emits `ETag: W/"2c324-…"` and the browser never sees
   it. So the page had no validator and no cache, and 111KB of CSS — 62% of a
   179KB file, ~50KB brotli — was re-downloaded IN FULL on every single app
   open, including every time a shooter reopened the PWA at a bay on range
   LTE. As a stamped file it is cached forever instead (server.js serves any
   `?v=`-stamped asset `immutable`), and index.html drops to ~68KB raw.

   NOTHING about the styles themselves changed — this is a verbatim move,
   indentation included, so `git diff` shows it as one. The rules that were
   true inside the <style> block are still true here:

     • Design TOKENS on `:root`, never hardcoded colors.
     • The dark palette lives in TWO duplicated blocks — the
       `@media (prefers-color-scheme: dark) :root:not([data-theme="light"])`
       one and the `:root[data-theme="dark"]` one — because a media query and
       a plain selector cannot share a rule. A token added to one MUST be
       added to the other, or the topbar switch and the system preference
       disagree about what dark looks like.
     • The shot-timer LCDs stay outside the token system on purpose: a timer
       is a physical object and is the same object in both modes.

   The original comment here read "Self-contained on purpose — no style.css /
   utils.js / app-header.js", and that decision is UNCHANGED in the part that
   mattered: this app still pulls in none of revival-dashboard's shared chrome
   (its header, its admin cog), because this is an independent page with its
   own login and that chrome would invite the wrong questions. "Self-contained"
   was about not importing ANOTHER app's assets, never about inlining; one
   stylesheet of this page's own styles keeps that intact. There is still no
   build step and no framework.

   ⚠️ It is loaded by a `<link rel="stylesheet" href="app.css">` in
   index.html, which asset-stamp.js stamps for free (its ASSET regex already
   matches `<link … href="*.css">`, pinned by test/asset-stamp.test.js). A
   stylesheet pulled in from JS, or added to a second HTML page served
   straight off express.static, would NOT be stamped — same trap the script
   tags carry. */
  :root {
    color-scheme: light dark; /* native scrollbars/inputs/date-picker follow the theme */
    --bg: #f5f6f8; --card: #ffffff; --card-2: #fafbfc;
    --ink: #17202b; --mut: #5d6875; --line: #e2e6eb; --line-2: #edf0f3;
    --accent: #c2410c; --accent-dk: #9a3412; --accent-soft: #fdf1ea;
    --ring: rgba(194, 65, 12, .22);
    /* The ink that sits ON a filled swatch (--accent, --green-dk, --err), the
       way --ink sits on --bg. It was ten hardcoded `#fff`s, which is the
       "tokens, never hardcoded colors" rule broken in the one place it costs
       contrast: a filled token flips lightness across themes, so the ink on it
       has to flip too. Light: white on #c2410c is 5.18:1 (WCAG AA fix,
       2026-09-09). */
    --on-fill: #ffffff;
    --green: #86b386; --green-dk: #4b7a4b;
    /* class-band ramp for the verdict rail: one neutral hue, light→dark toward GM */
    --band-1: #e9ecef; --band-2: #d5dae0; --band-3: #bfc7d0;
    --band-4: #a7b2bf; --band-5: #8d9aab; --band-6: #718296;
    --warn-bg: #fff8e6; --warn: #8a5c06; --warn-line: #f0dfad;
    --err-bg: #fdeceb; --err: #b3261e; --err-line: #f2c5c2;
    /* The stage drawing's palette (footprint.js reads these by class; the
       light values are also its fallbacks, so a document with no stylesheet
       keeps this look). A token added here is added to BOTH dark blocks. */
    --fp-bg: #f6f5f0; --fp-grid: #e6e4dc; --fp-area: #e6c200; --fp-area-line: #d4b200;
    --fp-wall: #3f6b3a; --fp-sight: #2a6fd6; --fp-pos: #2a6fd6; --fp-ink: #222222; --fp-mut: #777777;
    --fp-paper: #8a7a2a; --fp-steel: #666666; --fp-ns: #c0392b; --fp-route: #c2410c; --fp-mark: #b3261e;
    --fp-handle: #c9ccd2; --fp-face: #ffffff;
    --shadow-sm: 0 1px 2px rgba(23, 32, 43, .05);
    --shadow-md: 0 2px 6px rgba(23, 32, 43, .05), 0 12px 32px -12px rgba(23, 32, 43, .10);
  }
  /* Dark palette, reachable two ways: the system preference (unless the
     shooter has pinned light) and an explicit `data-theme="dark"`. The two
     blocks below are DELIBERATE DUPLICATES — a media query and a plain
     selector cannot share one rule — so treat them as one unit: a token
     added or retuned in either must be changed in both, or the switch and
     the system preference start disagreeing about what dark looks like. */
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
    --fp-bg: #161a20; --fp-grid: #232830; --fp-area: #e6c200; --fp-area-line: #b89a1a;
    --fp-wall: #8fbf88; --fp-sight: #7aa6ff; --fp-pos: #7aa6ff; --fp-ink: #e6e8ec; --fp-mut: #9aa0ab;
    --fp-paper: #d4b866; --fp-steel: #a7abb3; --fp-ns: #f07a70; --fp-route: #ee7a3a; --fp-mark: #f09a9a;
    --fp-handle: #4b505c; --fp-face: #161a20;
      --bg: #10151c; --card: #181f28; --card-2: #1c242e;
      --ink: #e7ecf1; --mut: #93a0ad; --line: #2a3441; --line-2: #232d39;
      /* Retuned for WCAG AA, 2026-09-09. #e05a1f could not serve both jobs at
         once: as TEXT it was 4.47:1 on --card and 4.21 on --card-2 (links,
         pressed tab labels, every accent-on-soft chip), and as a FILL under
         white it was 3.72:1 — lightening fixes the first and worsens the
         second, so the ink moved to --on-fill and the accent went up to where
         text passes. Same hue (18.3°) and saturation, lightness .50 → .54, so
         it is the same orange. --accent-dk was DARKER than --accent in dark,
         which read as 3.17:1 on --accent-soft in the badges and the "next"
         cadence command; "dk" means the readable-on-background one in both
         modes (the --green-dk comment below), so here it is the lighter tint. */
      --accent: #e26731; --accent-dk: #eb8250; --accent-soft: #2b1c13;
      --ring: rgba(226, 103, 49, .32);
      --on-fill: #1a0c0b; /* near-black on a filled swatch: 5.65:1 on --accent, 8.78 on --green-dk */
      --warn-bg: #2a2210; --warn: #e2b862; --warn-line: #4a3d1c;
      --err-bg: #2c1716; --err: #f2887f; --err-line: #552521;
      --green-dk: #8fbb8f; /* "dk" is the readable-on-background green in both modes */
      --band-1: #222b36; --band-2: #2a3542; --band-3: #34414f;
      --band-4: #40505f; --band-5: #4e6070; --band-6: #5f7383;
      --shadow-sm: 0 1px 2px rgba(0, 0, 0, .3);
      --shadow-md: 0 2px 6px rgba(0, 0, 0, .3), 0 12px 32px -12px rgba(0, 0, 0, .5);
    }
  }
  :root[data-theme="dark"] {
    --fp-bg: #161a20; --fp-grid: #232830; --fp-area: #e6c200; --fp-area-line: #b89a1a;
    --fp-wall: #8fbf88; --fp-sight: #7aa6ff; --fp-pos: #7aa6ff; --fp-ink: #e6e8ec; --fp-mut: #9aa0ab;
    --fp-paper: #d4b866; --fp-steel: #a7abb3; --fp-ns: #f07a70; --fp-route: #ee7a3a; --fp-mark: #f09a9a;
    --fp-handle: #4b505c; --fp-face: #161a20;
    --bg: #10151c; --card: #181f28; --card-2: #1c242e;
    --ink: #e7ecf1; --mut: #93a0ad; --line: #2a3441; --line-2: #232d39;
    /* AA retune, 2026-09-09 — see the media-query block above for the why. */
    --accent: #e26731; --accent-dk: #eb8250; --accent-soft: #2b1c13;
    --ring: rgba(226, 103, 49, .32);
    --on-fill: #1a0c0b;
    --warn-bg: #2a2210; --warn: #e2b862; --warn-line: #4a3d1c;
    --err-bg: #2c1716; --err: #f2887f; --err-line: #552521;
    --green-dk: #8fbb8f; /* "dk" is the readable-on-background green in both modes */
    --band-1: #222b36; --band-2: #2a3542; --band-3: #34414f;
    --band-4: #40505f; --band-5: #4e6070; --band-6: #5f7383;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .3);
    --shadow-md: 0 2px 6px rgba(0, 0, 0, .3), 0 12px 32px -12px rgba(0, 0, 0, .5);
  }
  /* Native scrollbars, date pickers and form controls follow the PINNED
     choice too — :root's `color-scheme: light dark` only ever tracks the
     system, so without these a pinned theme leaves white dropdowns on a
     dark page. */
  :root[data-theme="light"] { color-scheme: light; }
  :root[data-theme="dark"] { color-scheme: dark; }
  * { box-sizing: border-box; }
  /* scroll-padding keeps scrollIntoView targets (the plan view) clear of the sticky topbar */
  /* scroll-padding keeps scrollIntoView targets (the plan view) clear of the
     sticky topbar. It TRACKS the header rather than hardcoding it: the 76px
     constant was sized for a 55px bar, and the 44px touch targets
     (2026-08-20) grew it to 65px, quietly cutting the clearance from 21px to
     11px. core.js measures the bar into --topbar-h; the fallback covers the
     frame before that runs and the no-JS case. */
  html { scroll-behavior: smooth; scroll-padding-top: calc(var(--topbar-h, 65px) + 20px); }
  body {
    margin: 0; font: 15px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    color: var(--ink); background: var(--bg);
    -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
    -webkit-tap-highlight-color: transparent;
  }
  button, a, input, select, textarea, summary, label { touch-action: manipulation; }
  /* Visible keyboard focus everywhere; .btn swaps the outline for its own ring */
  :where(button, a, summary):focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  a { color: var(--accent); }
  a:hover { color: var(--accent-dk); }

  /* ── Top bar ── */
  header.topbar {
    position: sticky; top: 0; z-index: 10;
    display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap;
    /* Installed on an iPhone (B1, 2026-09-07) the status bar is drawn OVER the
       page (black-translucent + viewport-fit=cover), so the bar starts under
       the clock by the phone's own inset — zero everywhere else, which is why
       nothing below changes in a browser tab. The inset strip is painted the
       app's dark shell colour in BOTH themes: the status bar's text is light on
       that style, and light text over the light theme's white bar would be an
       unreadable clock. A fixed hex, not a token, for the LCD's reason — the
       status bar is a physical object, the same in both modes. */
    padding: calc(10px + env(safe-area-inset-top)) max(clamp(14px, 4vw, 24px), env(safe-area-inset-right)) 10px max(clamp(14px, 4vw, 24px), env(safe-area-inset-left));
    background: color-mix(in srgb, var(--card) 88%, transparent);
    -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--line);
  }
  header.topbar::before {
    content: ''; position: absolute; top: 0; left: 0; right: 0;
    height: env(safe-area-inset-top); background: #10151c;
  }
  header.topbar .brand { font-weight: 700; font-size: 15.5px; letter-spacing: -.01em; display: inline-flex; align-items: center; gap: 8px; }
  header.topbar button {
    background: transparent; color: var(--mut); border: 1px solid transparent; border-radius: 8px;
    padding: 6px 12px; font: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
    transition: background .12s, color .12s;
  }
  header.topbar button:hover { background: var(--line-2); color: var(--ink); }
  /* Which glyph shows is keyed off the RESOLVED theme, stamped pre-paint, so
     the icon is right on the first frame instead of correcting itself once
     core.js runs. The icon names the destination: a moon means "tap for
     dark". Both glyphs start hidden and exactly one is switched back on —
     every selector below carries the `header.topbar` prefix so it outranks
     the shared sizing rule, which is what briefly showed BOTH icons at once
     when the light palette was pinned. */
  header.topbar #btn-theme svg { width: 21px; height: 21px; display: none; }
  header.topbar #btn-theme .i-moon { display: block; }
  :root[data-resolved-theme="dark"] header.topbar #btn-theme .i-moon { display: none; }
  :root[data-resolved-theme="dark"] header.topbar #btn-theme .i-sun { display: block; }

  main { max-width: 920px; margin: 0 auto; padding: clamp(16px, 4vw, 28px) clamp(14px, 4vw, 24px) calc(88px + env(safe-area-inset-bottom)); }

  /* ── Bottom tab bar (shell ratified 2026-08-18 from the C+A mockup;
     hamburger nav tried and REVERTED by owner same day — one tap wins) ── */
  nav.tabbar {
    position: fixed; inset: auto 0 0 0; z-index: 10;
    display: flex; justify-content: center; gap: 2px;
    background: color-mix(in srgb, var(--card) 92%, transparent);
    -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
    border-top: 1px solid var(--line);
    padding: 5px max(8px, env(safe-area-inset-right)) calc(7px + env(safe-area-inset-bottom)) max(8px, env(safe-area-inset-left));
  }
  nav.tabbar button {
    flex: 1; max-width: 130px; display: flex; flex-direction: column; align-items: center; gap: 3px;
    font: inherit; font-size: 11px; font-weight: 600; padding: 6px 0 3px;
    border: 0; background: none; color: var(--mut); cursor: pointer; border-radius: 10px;
    transition: color .12s;
  }
  nav.tabbar button svg { width: 22px; height: 22px; }
  nav.tabbar button[aria-pressed="true"] { color: var(--accent); }
  /* The Coach dot (#115): absolutely placed at the icon's top-right, so the
     bar's height and every button's box are exactly what they were (64px,
     59×51 at 320 — measured). 8px, accent, a hairline of card colour so it
     reads against the icon; hidden by attribute, shown by coach-page.js. */
  nav.tabbar button { position: relative; }
  .tab-dot {
    position: absolute; top: 5px; left: calc(50% + 6px);
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--accent); box-shadow: 0 0 0 2px var(--card);
  }
  .tab-dot[hidden] { display: none; }

  /* ── The two rails (concept B, ratified 2026-08-19): split provenance made
     visible before the build. Layout can go .block and stop the plan;
     procedure only ever warns. ── */
  .prov { display: grid; gap: 8px; margin: 0 0 12px; }
  .prov .r {
    display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
    padding: 9px 11px; border-radius: 10px; font-size: 12.5px;
    border: 1px solid var(--line); background: var(--card-2);
  }
  /* nowrap: at 375px "PROCEDURE" broke across two lines in a fixed column. */
  .prov .r .ax { flex: none; min-width: 62px; white-space: nowrap; font-size: 10.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--mut); }
  .prov .r .src { font-weight: 650; color: var(--ink); }
  .prov .r .chip, .prov .r .chips { margin-left: auto; }
  .prov .r .chips { display: flex; gap: 6px; flex-wrap: wrap; }
  .prov .r .chips .chip { margin-left: 0; }
  .prov .r.good { border-color: color-mix(in srgb, var(--green-dk) 45%, transparent); background: color-mix(in srgb, var(--green) 14%, var(--card)); }
  .prov .r.good .src { color: var(--green-dk); }
  .prov .r.warn { border-color: var(--warn-line); background: var(--warn-bg); }
  .prov .r.warn .src { color: var(--warn); }
  .prov .r.block { border-color: var(--err-line); background: var(--err-bg); }
  .prov .r.block .src { color: var(--err); }
  /* 44px tall (2026-09-09). Every .chip in the app is a button or a link —
     the division pills, "Browse the library", "Use my photo", "Paste the WSB",
     "Practice this", the credits top-up — so this is a target, not a label,
     and it was ~29px with no floor at all. The BOX grows rather than a
     pseudo-element: these sit in wrapping rows 6-8px apart, and an invisible
     ±7px would have overlapped the chip above and stolen its taps. */
  .chip {
    display: inline-flex; align-items: center; justify-content: center;
    min-height: 44px;
    font: inherit; font-size: 11.5px; font-weight: 650; padding: 5px 11px; border-radius: 99px;
    border: 1px solid var(--line); background: var(--card); color: var(--mut); cursor: pointer;
  }
  .chip:hover { border-color: var(--mut); color: var(--ink); }
  .chip[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }

  /* Division went MULTI (owner directive, 2026-08-20): checkboxes on the
     Profile tab hold every division the shooter shoots, and this row picks
     which ONE a plan is built for. Pills reuse .chip's shape on purpose —
     the top bar's merge decision, applied down here: a new control inherits
     the set instead of inventing a look. */
  .divrow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 14px 0 0; }
  .divrow .ax { flex: none; font-size: 10.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--mut); }
  .divrow .pills { display: flex; gap: 6px; flex-wrap: wrap; }
  .divrow .note { flex-basis: 100%; margin: 0; }
  .checks { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 4px; }
  .checks label {
    display: flex; align-items: center; gap: 7px; min-height: 44px; padding: 0 12px;
    border: 1px solid var(--line); border-radius: 10px; background: var(--card);
    font-size: 13px; cursor: pointer;
  }
  .checks label:hover { border-color: var(--mut); }
  .checks label:has(input:checked) { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
  .checks input { width: auto; margin: 0; flex: none; }

  /* ── Standing rules (backlog #55, 2026-08-20) ──────────────────────────
     ONE RULE PER ROW on the Profile tab, and the same rows again on the
     verdict with what the plan did about each. The editor's inputs inherit
     the shared input rule above, including the 16px mobile size — a rule is
     typed at the range like everything else here. */
  .rule-rows { list-style: none; margin: 0 0 8px; padding: 0; display: grid; gap: 8px; counter-reset: none; }
  .rule-rows li { display: flex; align-items: center; gap: 8px; }
  .rule-in { flex: 1; min-width: 0; }
  .rule-del {
    flex: none; width: 44px; height: 44px; border: 1px solid var(--line); border-radius: 10px;
    background: var(--card); color: var(--mut); font-size: 20px; line-height: 1; cursor: pointer;
  }
  .rule-del:hover { border-color: var(--err-line); color: var(--err); background: var(--err-bg); }
  .rule-del:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--ring); }
  .rule-add { min-height: 44px; }
  .rule-add[disabled] { opacity: .5; cursor: default; }

  /* The receipts. Deliberately quieter than the verdict rail above them: this
     is the plan's own account, not a measured fact, and it should not read
     with the authority of the numbers. */
  .rule-receipts { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
  .rule-receipts > li {
    background: var(--card-2); border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px;
  }
  .rule-receipts .rule-text { margin: 0; font-size: 13.5px; line-height: 1.45; }
  .rule-meta { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
  .rule-pill {
    font-size: 11px; font-weight: 650; padding: 4px 9px; border-radius: 99px;
    border: 1px solid var(--line); background: var(--card); color: var(--mut); white-space: nowrap;
  }
  .rule-pill.on { border-color: var(--green-dk); color: var(--green-dk); }
  .rule-pill.hot { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
  .rule-pill.none { border-style: dashed; }
  .rule-where { margin: 7px 0 0; font-size: 12px; line-height: 1.45; color: var(--mut); }

  /* Review rubric rows (2026-08-20): question and its Yes/No on one line, so
     three tri-state facts do not become three full-height sections on a
     phone. Reuses .checks for the controls — same 44px targets. */
  .rb-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 6px; }
  .rb-row .rb-q { font-size: 12.5px; font-weight: 550; color: var(--mut); min-width: 168px; }

  /* The library entry behind the rails — USPSA's document, labelled as such. */
  /* ── The picker (ratified "B, with A's chip", 2026-08-19) ── */
  /* In flow, not floating: the list pushes the rails down instead of covering
     them, so the Layout rail — the thing the pick is about to change — stays
     on screen while you choose (the ratified mockup's behaviour). */
  .sug {
    border: 1px solid var(--line); border-radius: 12px; background: var(--card-2);
    padding: 5px; margin: 6px 0 0; max-height: 264px; overflow-y: auto;
  }
  .sug .pick:hover, .sug .pick.sel { background: var(--card); }
  /* Grid items default to min-width:auto, so a long stage name made the whole
     list wider than the card on a phone — this is the other half of the fix
     on .pick .name below (375px, 2026-08-19). */
  .picklist { display: grid; gap: 2px; min-width: 0; }
  .picklist > * { min-width: 0; }
  .picklist .hd {
    display: flex; align-items: center; gap: 8px; padding: 7px 9px 5px;
    font-size: 10.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--mut);
  }
  .picklist .hd .n { margin-left: auto; font-variant-numeric: tabular-nums; letter-spacing: 0; text-transform: none; font-weight: 550; font-size: 11.5px; }
  .pick {
    width: 100%; display: flex; align-items: center; gap: 10px; text-align: left;
    background: transparent; border: 1px solid transparent; border-radius: 9px;
    padding: 8px 9px; font: inherit; color: var(--ink); cursor: pointer;
  }
  .pick:hover, .pick.sel, .pick:focus-visible { background: var(--card-2); border-color: var(--line); outline: none; }
  .pick .code { flex: none; font-weight: 700; font-variant-numeric: tabular-nums; min-width: 52px; }
  /* min-width:0 is what lets the name actually shrink inside the flex row —
     without it the stage name pushes the "in the library" badge off a phone
     and the list scrolls sideways (found at 375px, 2026-08-19). */
  .pick .name { flex: 1 1 auto; min-width: 0; color: var(--mut); font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .pick .state { flex: none; margin-left: auto; font-size: 11.5px; white-space: nowrap; }
  .pick .state.in { color: var(--green-dk); font-weight: 650; }
  .pick .state.out { color: var(--mut); }
  .pick.out .code { color: var(--mut); }
  .picklist .none { padding: 10px 9px 12px; font-size: 13px; color: var(--mut); }
  .shelf { border: 1px solid var(--line); border-radius: 12px; background: var(--card-2); padding: 8px; margin: 0 0 12px; }
  .shelf input[type=search] { margin: 2px 0 6px; }
  .shelf .picklist { max-height: 236px; overflow-y: auto; }
  .shelf .pick:hover, .shelf .pick.sel { background: var(--card); }

  .lib { display: grid; grid-template-columns: 92px 1fr; gap: 12px; padding: 12px; margin: 0 0 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--card-2); }
  .lib img { width: 100%; border: 1px solid var(--line); border-radius: 8px; background: var(--card); display: block; }
  /* The thumbnail is 92px wide — too small to read a start position off, and
     the diagram is the whole point of the entry. Both the image and the line
     under it open the stored page full size (asked for 2026-08-19). Opening
     it is a read, and logs like every other. */
  .libshot { display: block; border-radius: 8px; text-decoration: none; }
  .libshot:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
  /* One link around image and label: the picture on its own would be a second
     tab stop to the same place, and the alt text would duplicate the label. */
  .libshot span {
    display: block; margin-top: 5px; text-align: center;
    font-size: 11.5px; font-weight: 600; color: var(--mut);
  }
  .libshot:hover span { color: var(--accent); text-decoration: underline; }
  .lib h4 { margin: 0 0 4px; font-size: 14px; font-weight: 650; }
  .lib dl { margin: 0; font-size: 12.5px; display: grid; grid-template-columns: auto 1fr; gap: 2px 8px; }
  .lib dt { color: var(--mut); }
  .lib dd { margin: 0; }
  .lib .attrib { margin: 8px 0 0; font-size: 11px; color: var(--mut); }
  /* An entry written before the target/scoring gate (2026-08-20): the gap is
     stated where the numbers would be, not left as a dash to skim past. */
  .lib .libgap {
    margin: 8px 0 0; padding: 7px 9px; font-size: 11.5px;
    color: var(--warn); background: var(--warn-bg);
    border: 1px solid var(--warn-line); border-radius: 8px;
  }
  /* Editing a stored entry — the confirm step's twin, inside the card whose
     details it is correcting. */
  .lib .libedit { margin: 10px 0 0; }
  .lib .libedit .fld { margin-top: 10px; }
  .lib .rowbtns { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 12px 0 0; }
  /* Rescan: what the read pass proposed, what it read the same, and what it
     could not make out — the last of which is the load-bearing half, since a
     line it failed on is LEFT as the person confirmed it. */
  .lib .scan { margin: 8px 0 0; font-size: 11.5px; color: var(--mut); }
  .lib .scan:empty { display: none; }
  .lib .scan.good { color: var(--green-dk); }
  .lib .scan.warn { color: var(--warn); }
  /* A field the rescan changed under you. Marked rather than silent: the whole
     promise of a proposal is that you can see what it touched. */
  .lib .libedit .rescanned { border-color: var(--accent); background: var(--accent-soft); }
  /* "Practice this" (#40): the library entry doubles as a scored range-day
     drill. Wraps on a phone so the chip keeps its own line and its target. */
  .lib .practice { display: flex; flex-wrap: wrap; gap: 6px 8px; align-items: center; margin: 10px 0 0; }
  .lib .practice .chip { flex: none; }
  .lib .practice span { flex: 1 1 12em; font-size: 11px; color: var(--mut); }
  .gate { display: flex; gap: 6px; align-items: baseline; margin: 0 0 12px; font-size: 12px; color: var(--mut); }

  /* Confirm step: what was read, and what was not. A field the pass could not
     read is EMPTY and required — blank beats plausible-and-wrong. */
  #confirm-step { border: 1px solid var(--line); border-radius: 12px; padding: 14px; margin: 0 0 12px; background: var(--card-2); }
  #confirm-step h3 { margin: 0 0 4px; }
  .shot { display: grid; grid-template-columns: 96px 1fr; gap: 12px; align-items: center; padding: 10px; border: 1px solid var(--line); border-radius: 10px; background: var(--card); margin: 10px 0; }
  .shot img { width: 100%; border-radius: 6px; display: block; }
  .shot .cap { margin: 0; font-size: 12px; color: var(--mut); }
  .shot .cap b { display: block; color: var(--ink); font-size: 13.5px; margin-bottom: 2px; }
  .shot.bad { border-color: var(--err-line); background: var(--err-bg); }
  .shot.bad .cap b { color: var(--err); }
  .rb { display: grid; gap: 8px; margin: 10px 0; }
  .rb .f { border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; background: var(--card); }
  .rb .f .k { display: flex; justify-content: space-between; gap: 8px; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; color: var(--mut); }
  .rb .f .k .st { text-transform: none; letter-spacing: 0; font-size: 11px; font-weight: 650; color: var(--green-dk); }
  .rb .f .v { font-size: 14px; font-weight: 600; margin-top: 4px; }
  .rb .f.miss { border-color: var(--warn-line); background: var(--warn-bg); }
  .rb .f.miss .k, .rb .f.miss .k .st { color: var(--warn); }
  .rb .f.done { border-color: color-mix(in srgb, var(--green-dk) 45%, transparent); }
  /* Paper and steel are ONE act of counting, so they read as one row rather
     than two full-height fields on a phone. Shared with the library editor. */
  /* auto-fit rather than 1fr 1fr: the confirm step is full width and keeps
     both boxes side by side even at 320px, while the same markup inside the
     library card — whose right column is ~150px next to the thumbnail —
     stacks instead of shrinking each input to 60px (measured, 2026-08-20). */
  .two { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 8px; margin-top: 4px; }
  .two > span { min-width: 0; }
  .two label { display: block; font-size: 11px; font-weight: 550; color: var(--mut); margin: 0 0 3px; }
  /* Code's arithmetic on the shooter's own counts — an offer and a check,
     never a correction, so it is a note and not a banner. */
  .calc { margin: 6px 0 0; font-size: 11.5px; color: var(--mut); }
  .calc.warn { color: var(--warn); font-weight: 550; }
  .libtog { display: flex; gap: 9px; align-items: flex-start; margin: 12px 0 0; padding: 10px 12px; font-size: 13px; border: 1px solid var(--line); border-radius: 10px; background: var(--card); }
  .libtog input { width: auto; margin: 2px 0 0; flex: none; }
  .libtog small { display: block; color: var(--mut); font-size: 12px; margin-top: 2px; }
  #plan-extra > summary { cursor: pointer; font-size: 13px; font-weight: 600; color: var(--mut); }
  #plan-extra > summary .sum-note { font-weight: 400; color: var(--mut); opacity: .85; }

  /* ── Plan segments (verdict-first plan view) ── */
  .segs {
    display: flex; gap: 4px; background: var(--card-2); border: 1px solid var(--line);
    border-radius: 10px; padding: 3px; margin: 12px 0;
  }
  .segs button {
    /* 44px, the Web Interface Guidelines touch minimum the top bar's circles
       already keep (2026-08-20). These were 36px until the plan-first round
       measured them against that rule — same bar, same gloved hand. */
    flex: 1; font: inherit; font-size: 13px; font-weight: 600; padding: 0; min-height: 44px;
    border: 0; border-radius: 8px; background: none; color: var(--mut); cursor: pointer;
  }
  /* The pressed segment is ORANGE (owner-directed 2026-09-07, "highlight the
     focused tab in orange"): the same accent the pressed tab in the bottom
     bar wears, so where-you-are reads one way on both bars. One rule for
     every .segs — Range day's lanes, Reports' axis, the plan view, the
     Admin strip, Appearance. */
  .segs button[aria-pressed="true"] { background: var(--card); color: var(--accent); box-shadow: var(--shadow-sm); }
  /* A plan with no review yet marks its Review segment (owner, 2026-08-21).
     A dot, not a colour change: the segment bar's job is to say WHERE YOU ARE,
     and recolouring a button that also carries pressed state would give one
     control two meanings. Sits inside the button so it tracks the label at any
     bar width, and the accessible name says the same thing in words. */
  .segs button.needs { position: relative; }
  .segs button.needs::after {
    content: ''; position: absolute; top: 9px; left: 50%; margin-left: 18px;
    width: 6px; height: 6px; border-radius: 50%; background: var(--accent);
  }

  /* ── The stage bar (plan-first shell, ratified 2026-08-20) ────────────────
     Stage identity + the five segments, pinned under the top bar for the whole
     tab. Before this the segment bar scrolled away at 728px and never came
     back, so from anywhere inside a pane there was no way to reach another one.
     It is full-bleed inside the card: the negative margins are exactly the
     card's own padding, so `top` can sit flush against the sticky header
     without the card's background showing through behind it. --topbar-h is
     measured into :root by core.js. */
  .stage-bar {
    position: sticky; top: var(--topbar-h, 65px); z-index: 5;
    margin: calc(-1 * clamp(16px, 3vw, 24px)) calc(-1 * clamp(16px, 3vw, 24px)) 12px;
    padding: 10px clamp(16px, 3vw, 24px) 10px;
    background: var(--card); border-bottom: 1px solid var(--line);
    border-radius: 13px 13px 0 0;
  }
  .stage-row { display: flex; align-items: center; gap: 8px; }
  /* Under a stage the bar's card holds nothing but the bar (#116 — the
     fields moved after the plan), so the card is the bar: no body padding,
     no rule under it, all four corners. */
  #stage-view { padding-bottom: 0; }
  #stage-view .stage-bar { margin-bottom: 0; border-bottom: 0; border-radius: 13px; }
  /* One line, ellipsis: the code comes first, so a truncated name still says
     which stage this is — and a bar that changes height as you switch plans is
     a bar you cannot aim at. min-width:0 is what lets it actually shrink
     instead of pushing the button off the row. */
  /* `.stage-bar .stage-row h2`, not `.stage-bar h2`: `.card h2` further down
     this sheet carries `text-wrap: balance`, which is the same longhand
     (`text-wrap-mode`) that `white-space: nowrap` sets — equal specificity, and
     it comes later, so it won. The title wrapped to two lines the moment the
     button beside it grew into a pill (2026-08-20). */
  .stage-bar .stage-row h2 {
    flex: 1; min-width: 0;
    margin: 0; font-size: 16px; font-weight: 700; letter-spacing: -.015em;
    white-space: nowrap; text-wrap: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* New plan, in the plan's own bar (owner, 2026-08-20). It replaced a
     floating button that shrank to a "+" on scroll: it covered a line of the
     at-the-line card while the shooter was reading it, and a moving control
     over a document you are reading is a distraction whatever size it is.
     Here it never covers a word, it is always on screen because the bar is
     pinned, and nothing animates. Same 44px circle as the top bar's pair —
     the third button in the app to inherit that shape rather than invent one. */
  .stage-new {
    flex: none; height: 44px; padding: 0 16px; gap: 8px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 22px; border: 1px solid var(--accent);
    background: var(--accent); color: var(--on-fill); cursor: pointer;
    font: inherit; font-size: 14.5px; font-weight: 650; white-space: nowrap;
  }
  .stage-new svg { width: 19px; height: 19px; flex: none; }
  .stage-new:hover { background: var(--accent-dk); border-color: var(--accent-dk); }
  /* Below 360px the label costs more than it earns: the pill is a flat 126px
     at every width, and at 320 that left the stage NAME as "CM 99-11 · El …".
     Dropping to the icon hands ~82px back and the whole name fits again
     (measured 2026-08-20 — 206px of 208 available). The button keeps its
     aria-label at every width, so the accessible name never depends on
     whether the text is currently painted. Chosen over shrinking the type:
     this is a 44px touch target on the narrowest phone in the pilot. */
  @media (max-width: 359px) {
    .stage-new { width: 44px; padding: 0; gap: 0; }
    .stage-new .np-label { display: none; }
  }
  /* The MATCH bar's + is the circle at EVERY width (shell review 2026-09-07):
     starting a match is a once-a-weekend act, and the 126px pill beside the
     switcher truncated the name of the match on screen ("Walk Clu…" at 390).
     The stage bar's + New plan keeps its label — building is the tab's act. */
  .stage-new.stage-new-icon { width: 44px; padding: 0; gap: 0; }
  .stage-new.stage-new-icon .np-label { display: none; }
  .stage-bar h2:focus { outline: none; } /* programmatic focus target, never tabbed to */
  /* The score fold (#116): one 44px row until a run is logged. Native
     <details>, so the tap, the keyboard and the state are the browser's. */
  .scorefold { margin-top: 16px; border: 1px solid var(--line); border-radius: 12px; background: var(--card-2); }
  .scorefold summary {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    min-height: 44px; padding: 0 14px; cursor: pointer; list-style: none;
    font-weight: 650; font-size: 14.5px;
  }
  .scorefold summary::-webkit-details-marker { display: none; }
  .scorefold summary::after { content: '›'; color: var(--mut); font-size: 20px; transform: rotate(90deg); transition: transform .12s; }
  .scorefold[open] summary::after { transform: rotate(-90deg); }
  .scorefold .scorefold-hf { margin-left: auto; margin-right: 8px; font-weight: 500; font-size: 12.5px; color: var(--mut); font-variant-numeric: tabular-nums; }
  .scorefold > :not(summary) { padding: 0 14px; }
  .scorefold > .scorebox { margin-top: 4px; }
  .scorefold > p:last-child { padding-bottom: 12px; }
  .stage-bar .segs { margin: 8px 0 0; }

  /* ── The build sheet ──────────────────────────────────────────────────────
     Full-screen rather than the how-to's bottom sheet: this is a task with a
     photo, a confirm step and a progress state, not a page of reading. Same
     <dialog> mechanics though — see the markup comment. */
  .sheet {
    position: fixed; inset: 0; z-index: 20;
    width: 100%; max-width: 640px; height: 100%; max-height: 100%; margin: 0 auto;
    padding: 0; border: 0; background: var(--bg); color: var(--ink);
  }
  .sheet[open] { display: flex; flex-direction: column; }
  .sheet::backdrop { background: rgba(10, 14, 20, .45); }
  .sheet-head {
    flex: none; display: flex; align-items: center; gap: 8px;
    padding: max(8px, env(safe-area-inset-top)) 12px 8px;
    border-bottom: 1px solid var(--line); background: var(--card);
  }
  .sheet-head h2 { flex: 1; margin: 0; font-size: 16.5px; font-weight: 700; text-align: center; }
  .sheet-head h2:focus { outline: none; } /* programmatic focus target, never tabbed to */
  .sheet-nav {
    font: inherit; font-size: 14.5px; font-weight: 650; cursor: pointer;
    background: none; border: 0; color: var(--accent); border-radius: 8px;
    padding: 8px 10px; min-height: 44px; min-width: 44px;
  }
  /* Balances the Cancel button so the title sits centred. */
  .sheet-head::after { content: ""; min-width: 44px; }
  /* ── The bottom sheet (2026-09-07) ── one dialog for every ⋯ menu and every
     confirm (core.js scSheet). Rises from the bottom, 640 wide at most, a
     grip, rows on a 52px rhythm with one 20px icon each, the destructive row
     red and last, Cancel a full-width ghost. */
  .bsheet {
    position: fixed; inset: auto 0 0 0; z-index: 30; margin: 0 auto;
    width: 100%; max-width: 640px; padding: 0; border: 0; background: transparent; color: var(--ink);
  }
  .bsheet::backdrop { background: rgba(10, 14, 20, .45); }
  .bs-panel {
    background: var(--card); border: 1px solid var(--line); border-bottom: 0;
    border-radius: 18px 18px 0 0; box-shadow: 0 -8px 30px rgba(0, 0, 0, .25);
    padding: 8px 0 max(24px, env(safe-area-inset-bottom));
  }
  .bs-grip { width: 36px; height: 4px; border-radius: 2px; background: var(--line); margin: 0 auto 12px; }
  .bs-head { padding: 0 16px 12px; }
  .bs-t { font-size: 15px; font-weight: 700; letter-spacing: -.01em; }
  .bs-sub { font-size: 12.5px; color: var(--mut); margin-top: 2px; }
  .bs-rows { display: flex; flex-direction: column; }
  .bs-row {
    display: flex; align-items: center; gap: 12px; width: 100%; min-height: 52px; padding: 6px 16px;
    background: none; border: 0; border-top: 1px solid var(--line-2); color: var(--ink);
    font: inherit; text-align: left; cursor: pointer;
  }
  .bs-row:hover, .bs-row:focus-visible { background: var(--card-2); outline: none; }
  .bs-row.danger { color: var(--err); }
  .bs-ic { display: inline-flex; color: var(--mut); flex: none; }
  .bs-ic svg { width: 20px; height: 20px; }
  .bs-row.danger .bs-ic { color: var(--err); }
  .bs-body { display: flex; flex-direction: column; min-width: 0; }
  .bs-l { font-size: 15px; font-weight: 600; line-height: 1.3; }
  .bs-s { font-size: 12.5px; color: var(--mut); }
  .bs-foot { padding: 12px 16px 0; }
  .bs-foot .btn { width: 100%; }
  .bs-confirm { padding-left: 16px; padding-right: 16px; display: flex; flex-direction: column; gap: 12px; }
  .bs-badge {
    width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center;
    background: var(--err-bg); border: 1px solid var(--err-line); color: var(--err);
  }
  .bs-badge svg { width: 20px; height: 20px; }
  .bs-q { font-size: 19px; font-weight: 700; letter-spacing: -.01em; line-height: 1.2; }
  .bs-b { font-size: 14px; color: var(--mut); line-height: 1.45; margin-top: -4px; }
  .bs-b b { color: var(--ink); }
  /* The AI-upload consent sheet (#239, 2026-09-20). It rides `.bs-b`'s type
     and only adds the list shape — a sheet with several paragraphs and two
     bullets is still the confirm panel, not a second surface. Qualified by
     `.bs-b` because one stylesheet with no build step is one namespace. */
  .bs-b p { margin: 0 0 8px; }
  .bs-b p:last-child { margin-bottom: 0; }
  .bs-b .ac-h { color: var(--ink); font-weight: 700; font-size: 13px; margin-bottom: 4px; }
  .bs-b .ac-list { margin: 0 0 8px; padding-left: 18px; }
  .bs-b .ac-list li { margin-bottom: 4px; }
  .bs-b .ac-fine { font-size: 12px; opacity: .8; }
  /* The one danger row in the Profile hub (#238, 2026-09-20). It borrows the
     hub row's whole shape and changes only the ink, because a destructive
     row that looked like a different KIND of control would read as a mistake
     rather than as the serious thing it is. The chevron stays: it opens a
     preview, it does not act. */
  .row.danger .rt { color: var(--err); }
  .row.danger .chev { color: var(--err); }
  /* A sheet with more to say than a phone is tall (2026-09-20, #239's consent
     sheet: measured 1000px in a 568px viewport at 320). `.bsheet` is
     `inset: auto 0 0 0`, so without this the panel grows PAST THE TOP of the
     screen and takes its own title with it — a question you cannot read over
     a Yes you can press. Opt-in via `scSheet.confirm({ tall: true })`, so the
     other four callers of `.bs-panel` are untouched. `svh` rather than `vh`
     because iOS `vh` is the address-bar-collapsed height and overflows by
     the bar. The BODY scrolls and takes `tabindex="0"` with it, or the one
     part that scrolls is the one part a keyboard cannot reach. */
  .bs-tall { max-height: 92svh; display: flex; flex-direction: column; }
  .bs-tall .bs-b { overflow-y: auto; min-height: 0; overscroll-behavior: contain; }
  .bs-tall .bs-b:focus-visible { outline: 2px solid var(--acc); outline-offset: -2px; }
  /* WHAT A PACK IS (2026-09-17): three lines of arithmetic on the weights,
     inside the buy confirm — the sheet that asks is the place to say what is
     being added, and the figures are credits.js's (`packSpansRows`). */
  .bs-packs { margin: 8px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 6px; }
  .bs-packs li { padding-left: 11px; border-left: 2px solid var(--line); color: var(--ink); }
  /* The ask's field (2026-09-09). 16px so iOS does not zoom the page when it
     focuses, 44px tall like every other target on the sheet. */
  .bs-in {
    width: 100%; font: inherit; font-size: 16px; min-height: 44px;
    padding: 10px 12px; border-radius: 10px;
    border: 1px solid var(--line); background: var(--card-2); color: var(--ink);
  }
  .bs-in:focus { outline: none; border-color: var(--accent); background: var(--card); box-shadow: 0 0 0 3px var(--ring); }
  .bs-btns { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
  .bs-btns .btn { width: 100%; }
  /* The one filled red in the app (2026-09-07): the confirm sheet's own
     destructive button. The outline rule for `.btn.danger` on a SCREEN
     stands — there a solid red slab reads as the thing to press; here the
     question is the title, there are two buttons, and the filled one is the
     alert's affordance. It is never the default focus (Cancel takes it). The
     ink is --on-fill rather than the hardcoded near-black it shipped with:
     that black was right on dark's light --err (7.81:1) and wrong on light's
     deep red (2.92:1), which is the whole reason the token exists. */
  .bs-confirm .btn.danger { background: var(--err); border: 0; color: var(--on-fill); box-shadow: var(--shadow-sm); }
  .bs-confirm .btn.danger:hover { background: var(--err); filter: brightness(.94); }
  .sheet-body { flex: 1; overflow-y: auto; overscroll-behavior: contain; padding: 14px 14px calc(24px + env(safe-area-inset-bottom)); }
  /* The form is the sheet's whole body — the card frame around it would be a
     border drawn inside a border. */
  .sheet-body > #input-card { border: 0; box-shadow: none; background: none; margin: 0; padding: 0; }
  /* Build + hint (spend-surface #5, 2026-09-07). In the flow until the layout
     rail is set; then a sticky foot on the rd-startbar pattern — measured, a
     library pick pushed Build to 1091px in a 1183px sheet at 390, under the
     fold on the one screen whose job is the tap. Sticky, not fixed: it sits
     in the flow once scrolled to, and it stays inside the sheet's own
     scroller. The build drops the class while it runs, so the progress block
     is never under it. */
  .build-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin: 14px 0 0; }
  /* A held Build says why, in a colour that reads as a reason rather than as
     the price copy that sits in the same slot when nothing is wrong. Added
     2026-09-13, after the owner looked at a disabled button with the
     explanation already beside it and had to ask what was wrong. */
  #build-hint.blocked { color: var(--warn); font-weight: 550; }
  #build-hint.blocked a { color: var(--warn); text-decoration: underline; }
  .build-row.build-foot {
    position: sticky; bottom: 0; z-index: 2;
    flex-direction: column; align-items: stretch; gap: 6px;
    margin: 14px -14px 0; padding: 12px 14px calc(12px + env(safe-area-inset-bottom));
    border-top: 1px solid var(--line-2); background: var(--card);
  }
  .build-row.build-foot #btn-build { width: 100%; }
  /* ── The pinned verdict strip (2026-09-09) ────────────────────────────────
     Was `.verdict-hero`, a card inside the Verdict PANE — so tapping Plan to
     read the engagement order took the class pace, the predicted hit factor,
     the rail and the gap sentence off the screen, at the bay, which is where
     they are wanted. It lives in the stage bar now, above the pane switcher,
     and no pane can hide it (Strava's rule: the summary panel is persistent
     across all views). Every number is still filled by updatePredVerdict from
     the same math as the tables behind Why & WSB — the rail and the bars are
     pictures of `planFigures.assessRun`'s output, never new judgment. (It was
     `assessRunClient`, the page's own copy, until #177 made the page and the
     server call one function.)

     It is SIZED to be carried: the sentence drops 21px → 16.5px, the rail 30px
     → 20px, and both go left-aligned, because a strip that has to sit above
     the switcher forever pays for every pixel out of the pane below it. The
     class-band widths are untouched — those are the honest part. */
  .v-strip { margin-top: 9px; padding-top: 9px; border-top: 1px solid var(--line-2); }
  .v-strip .verdict-line { margin: 0; font-size: 16.5px; text-align: left; }
  .v-strip .verdict-sub { margin: 1px 0 0; font-size: 11.5px; text-align: left; }
  .v-strip .rail-wrap { margin: 21px 1px 0; }
  .v-strip .rail { height: 20px; }
  .v-strip .you { top: -5px; bottom: -5px; }
  .v-strip .you-label { top: -18px; }
  .v-strip .gap-note { margin: 9px 1px 0; font-size: 12.5px; text-align: left; }
  .v-strip .v-nopred { margin: 0; font-size: 12.5px; }
  /* The one line of "At the line" that is wanted mid-stage. The card it came
     from also carried the top three reminders, a duplicate of the first three
     rows of the full list — that went, and the full list is a fold on Plan. */
  .v-reload { margin: 9px 1px 0; font-size: 12.5px; line-height: 1.5; }
  .v-reload b { color: var(--accent-dk); font-weight: 700; }

  /* COMPACT, ONCE THE PAGE MOVES. The strip is sticky, so at rest it is a
     summary and in motion it is a lid — walk 2026-09-09-3 measured the lid at
     410px on a 568px screen, with `#review-note-save` underneath it and
     untappable. Scrolled, the sentence and the reload call stay (what is
     wanted mid-stage) and the sub, the rail and the gap note fold away (what
     is read before the beep). A snap, not a transition: this is read one-handed
     in the sun while walking, and a height animating under a thumb is worse
     than a change that has already happened.

     `display: none` rather than visibility or opacity, so the bar actually
     gives the height back — hiding it in place would have kept every pixel
     the measurement objected to. */
  .stage-bar.v-compact .v-strip .verdict-sub,
  .stage-bar.v-compact .v-strip .rail-wrap,
  .stage-bar.v-compact .v-strip .v-noclass,
  .stage-bar.v-compact .v-strip .gap-note { display: none; }
  .stage-bar.v-compact .v-strip { margin-top: 7px; padding-top: 7px; }
  .stage-bar.v-compact .v-strip .verdict-line { font-size: 14px; }
  .stage-bar.v-compact .v-reload { margin-top: 5px; }
  /* The coach note is theirs to read once, not to carry down the stage. */
  .stage-bar.v-compact #plan-coach-note { display: none; }

  /* THE BROWSER SCROLLS THINGS UNDER STICKY HEADERS unless told the header is
     there. `--bar-h` is published by syncVerdictStrip from the bar's measured
     height, so this follows the bar between its two states instead of guessing
     a constant. The fallback matters: a plan rendered before the first scroll
     tick still needs a sane number. */
  #plan-view [id],
  #plan-view summary,
  #plan-view .btn,
  #plan-view input,
  #plan-view textarea { scroll-margin-top: calc(var(--bar-h, 170px) + 10px); }
  .meta-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 12px; }
  .meta-chips span {
    font-size: 11.5px; font-weight: 650; padding: 3px 9px; border-radius: 99px;
    border: 1px solid var(--line); background: var(--card); color: var(--mut);
  }
  .meta-chips .src-ok { border-color: var(--green-dk); color: var(--green-dk); }
  .meta-chips .src-warn { border-color: var(--warn-line); background: var(--warn-bg); color: var(--warn); }
  .verdict-line { text-align: center; margin: 2px 0; font-size: 21px; font-weight: 750; letter-spacing: -.015em; }
  .verdict-line .cls { color: var(--accent); }
  .verdict-sub { text-align: center; margin: 2px 0 0; color: var(--mut); font-size: 13px; font-variant-numeric: tabular-nums; }
  /* #230 — the app saying what it does NOT know. A run's letter is a
     percentage of one stage's HHF; a shooter with no classification used to
     get the letter and the ladder above it with nothing to say they are
     unclassified. Quiet by design: it qualifies the verdict, it is not a
     warning about it. */
  .v-noclass { margin: 8px 2px 0; padding: 7px 9px; font-size: 12.5px; line-height: 1.45;
    color: var(--mut); background: var(--card-2); border: 1px solid var(--line-2);
    border-radius: 8px; text-align: left; }
  /* In the PINNED strip it is one line and carries no chrome — the strip is
     already a card, and a box inside it was 200px of the 568px viewport. */
  .v-strip .v-noclass { margin: 4px 0 0; padding: 0; background: none; border: 0;
    font-size: 12px; text-align: center; }
  /* Above a list whose first row is a bordered card (the Match tab's stage
     rows and results sheet, Home's recent rows, Reports › What-ifs —
     2026-09-23) the note needs air under it, or it reads as the row's lid.
     Reports › Plans and › Classifiers are followed by a date heading and
     already breathe. Home's section is a flex column whose gap already
     spaces it, so there the note only drops its own top margin. */
  #m-noclass, #wi-noclass { margin-bottom: 8px; }
  #home-recent-noclass > .v-noclass { margin-top: 0; }
  .rail-wrap { position: relative; margin: 30px 2px 6px; }
  /* The rail no longer CLIPS its labels (2026-09-09). The band widths are the
     real class percentages, so at 320px the GM band is ~13px wide and "GM" at
     the 11px floor is ~17px — it does not fit, and `overflow: hidden` on the
     rail was cutting it. The band widths are the honest part and must not
     move, so the clip goes instead: the rounded ends come from the first and
     last band's own radii, and a label wider than its band bleeds over the
     neighbouring one, which is empty there (M's own label is ~8px inside a
     ~26px cell). Nothing wraps — a class letter has no break point — so the
     old 9px shrink was buying nothing but illegibility. */
  .rail { display: flex; height: 30px; border-radius: 7px; }
  .rail div { display: grid; place-items: center; font-size: 11px; font-weight: 700; color: var(--ink); white-space: nowrap; }
  .rail .b1 { width: 40%; background: var(--band-1); border-radius: 7px 0 0 7px; }
  .rail .b2 { width: 20%; background: var(--band-2); }
  .rail .b3 { width: 15%; background: var(--band-3); }
  .rail .b4 { width: 10%; background: var(--band-4); }
  .rail .b5 { width: 10%; background: var(--band-5); }
  .rail .b6 { width: 5%;  background: var(--band-6); border-radius: 0 7px 7px 0; }
  /* The tightened tracking stays on phones — it buys the narrow bands a
     pixel; the font size does not shrink with it any more. */
  @media (max-width: 640px) { .rail div { letter-spacing: -.02em; } }
  .you {
    position: absolute; top: -7px; bottom: -7px; border: 2px solid var(--accent);
    border-radius: 5px; background: color-mix(in srgb, var(--accent) 16%, transparent);
  }
  .you i { position: absolute; top: -2px; bottom: -2px; width: 2px; background: var(--accent); }
  .you-label {
    position: absolute; top: -24px; transform: translateX(-50%);
    font-size: 11px; font-weight: 800; color: var(--accent); white-space: nowrap;
  }
  .gap-note { margin: 12px 2px 0; font-size: 13.5px; text-align: center; }
  /* The review's points field and the run it computes (#63, 2026-08-21). The
     readout is the ONE number on the tab that is not a prediction, so it gets
     the accent border the predicted verdict does not. */
  .rb-hint { font-size: 12.5px; margin: 6px 2px 0; }
  .rb-run:not(:empty) {
    margin: 10px 2px 0; padding: 10px 12px; font-size: 13.5px;
    border: 1px solid var(--accent); border-left-width: 3px; border-radius: 10px;
    background: var(--accent-soft); color: var(--ink);
  }
  .rb-run b { font-weight: 700; font-variant-numeric: tabular-nums; }
  .rb-run-bad { color: var(--err); font-weight: 600; }

  /* ── Compare two runs at the same stage (#67, 2026-08-21) ────────────────
     Newest column on the LEFT, matching the list order the reader arrived
     from. The headline is the only number allowed to be a verdict, and it
     renders only when BOTH runs are real — everything else is a table you
     read, not a claim the app is making. */
  .cmp-title { margin: 0 0 10px; font-size: 17px; letter-spacing: -.01em; }
  .cmp-title b { font-variant-numeric: tabular-nums; }
  .cmp-headline {
    margin: 0 0 16px; padding: 12px 14px; border-radius: 12px;
    font-size: 17px; font-weight: 750; letter-spacing: -.01em;
    border: 1px solid var(--line); background: var(--card-2);
  }
  .cmp-headline span { display: block; margin-top: 3px; font-size: 13px; font-weight: 400; color: var(--mut); }
  .cmp-headline.up { border-color: var(--green-dk); color: var(--green-dk); }
  .cmp-headline.down { border-color: var(--warn-line); color: var(--warn); }
  .cmp-headline.flat { font-size: 15px; font-weight: 650; color: var(--mut); }
  table.cmp { width: 100%; border-collapse: collapse; font-size: 14px; }
  table.cmp thead th {
    text-align: left; padding: 0 10px 8px; font-size: 11px; font-weight: 700;
    letter-spacing: .06em; text-transform: uppercase; color: var(--mut);
    font-variant-numeric: tabular-nums;
  }
  table.cmp th[scope="row"] {
    text-align: left; font-weight: 500; color: var(--mut); font-size: 13px;
    padding: 9px 10px 9px 0; white-space: nowrap;
  }
  table.cmp td {
    padding: 9px 10px; vertical-align: top; font-variant-numeric: tabular-nums;
    border-top: 1px solid var(--line-2); width: 38%;
  }
  table.cmp th[scope="row"] { border-top: 1px solid var(--line-2); }
  table.cmp tbody tr:first-child td, table.cmp tbody tr:first-child th { border-top: 0; }
  table.cmp .cmp-strong td { font-size: 15.5px; }
  .cmp-sub { display: block; font-size: 12px; font-weight: 400; color: var(--mut); margin-top: 1px; white-space: normal; }
  .cmp-better, .cmp-sub.cmp-better { color: var(--green-dk); }
  .cmp-worse, .cmp-sub.cmp-worse { color: var(--warn); }
  .cmp-notes { margin-top: 22px; }
  .cmp-note { border-left: 2px solid var(--line); padding: 2px 0 2px 12px; margin-bottom: 12px; }
  .cmp-note p { margin: 0; font-size: 14px; }
  .cmp-note-when { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--mut); margin-bottom: 3px !important; }
  .cmp-foot { font-size: 12.5px; margin: 8px 0 0; }
  /* The review ask, at the FOOT of the verdict (owner, 2026-08-21). Last thing
     in the pane on purpose: you get the ask having actually read the verdict,
     which is what you opened the plan for. Quiet border rather than a warn
     banner — an unwritten review is a to-do, not a problem with the plan. */
  .review-ask {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
    margin: 18px 2px 0; padding: 14px 16px;
    border: 1px dashed var(--line); border-radius: 12px; background: var(--card-2);
  }
  .review-ask p { margin: 0; flex: 1 1 220px; font-size: 13.5px; color: var(--mut); }
  /* 44px, the Web Interface Guidelines minimum the top bar's circles and the
     stage bar's segments both keep. The shared .btn is padding-derived at 42px
     app-wide, which is a near-miss everywhere and not this change's to fix — so
     the floor is set here rather than moved under every button in the app. */
  .review-ask .btn { flex: 0 0 auto; min-height: 44px; }
  .v-label { margin: 18px 2px 8px; font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--mut); }
  /* The four titled blocks inside "How the verdict was worked out" — the fold
     at the foot of Why & WSB. Plain headings rather than nested <details>:
     these are read together or not at all, so a second tap per block would be
     charging twice for one decision. */
  .just-blk { margin: 0 0 20px; }
  .just-blk:last-child { margin-bottom: 0; }
  .just-blk > h4 { margin: 0 0 8px; font-size: 14px; font-weight: 700; letter-spacing: -.01em; }
  .just-blk > h4 + .meta-chips { margin-top: 0; }
  /* The remaining-class ladder, under the class-targets table it is arithmetic
     on. The NEXT class up is pinned in the strip, so this is reference — the
     "To make <Class>" prose box that used to repeat the strip's own sentence
     is gone (2026-09-09). */
  .ladder-h { margin: 16px 0 6px; font-size: 13px; font-weight: 650; }
  .phasebar { display: flex; gap: 2px; height: 20px; margin: 0 2px; }
  .phasebar div { border-radius: 4px; background: var(--band-3); min-width: 3px; }
  .phasebar div:nth-child(2n) { background: var(--band-4); }
  .phaselist { margin: 8px 2px 0; font-size: 12.5px; color: var(--mut); }
  .phaselist b { color: var(--ink); font-weight: 650; font-variant-numeric: tabular-nums; }
  .levers { display: grid; gap: 8px; margin: 0 2px; }
  .lever .l-top { display: flex; justify-content: space-between; gap: 8px; font-size: 13.5px; }
  .lever .l-top b { font-weight: 650; }
  .lever .l-top .s { color: var(--green-dk); font-weight: 750; font-variant-numeric: tabular-nums; white-space: nowrap; }
  .lever .l-bar { height: 5px; border-radius: 3px; background: var(--line-2); margin-top: 4px; overflow: hidden; }
  .lever .l-bar i { display: block; height: 100%; background: var(--green-dk); border-radius: 3px; }
  .lever .l-where { font-size: 12px; color: var(--mut); margin-top: 2px; }
  /* Review segment: one card per drill (owner, 2026-08-18 — drills live
     here for post-match follow-up, not in the levers) */
  .drill-card { border: 1px solid var(--line); background: var(--card); border-radius: 10px; padding: 10px 14px; margin-bottom: 8px; }
  .drill-card .drill-what { margin: 0; font-weight: 650; font-size: 14px; }
  .drill-card .drill-why { margin: 3px 0 0; font-size: 12.5px; color: var(--mut); }
  .drill-card .drill-why .s { color: var(--green-dk); font-weight: 700; }
  .review-note { margin: 2px 0 22px; border-bottom: 1px solid var(--line-2); padding-bottom: 18px; }
  .review-note .v-label { margin-top: 0; }
  .review-note textarea { min-height: 110px; }
  /* ── Position-card plan (concept A, ratified 2026-08-18): one card per
     shooting position, move markers between, code-verified round tally ── */
  .start-line {
    display: flex; gap: 8px; align-items: baseline; font-size: 13px;
    background: var(--card-2); border: 1px solid var(--line); border-radius: 10px;
    padding: 9px 12px; margin-bottom: 12px;
  }
  .start-line .k { font-size: 10.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--mut); flex: none; }
  .pos-card { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 12px 14px; box-shadow: var(--shadow-sm); }
  .pos-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
  .pos-head .p { font-size: 15px; font-weight: 750; letter-spacing: -.01em; }
  .pos-head .p small { font-weight: 500; color: var(--mut); font-size: 12.5px; margin-left: 6px; }
  .rds-badge { flex: none; font-size: 12px; font-weight: 750; color: var(--accent-dk); background: var(--accent-soft); border-radius: 99px; padding: 2px 10px; font-variant-numeric: tabular-nums; }
  .pos-entry { margin: 2px 0 8px; font-size: 12.5px; color: var(--mut); }
  .tgt-line { display: flex; gap: 8px; align-items: baseline; padding: 6px 0; border-top: 1px solid var(--line-2); font-size: 13.5px; }
  .tgt-line .t { font-weight: 700; flex: none; min-width: 52px; }
  .tgt-line .n { flex: none; color: var(--mut); font-size: 12px; font-variant-numeric: tabular-nums; }
  .tgt-line .d small { display: block; color: var(--mut); font-size: 12px; }
  .pos-exit { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line-2); font-size: 12.5px; font-weight: 650; color: var(--accent-dk); }
  .move-marker {
    display: flex; align-items: center; gap: 10px; margin: 10px 4px; padding: 0 6px;
    color: var(--mut); font-size: 12px; font-weight: 650; letter-spacing: .02em;
  }
  .move-marker::before, .move-marker::after { content: ''; flex: 1; min-width: 14px; border-top: 2px dashed var(--line); }
  .move-marker .arrow { color: var(--accent); font-size: 15px; flex: none; }
  .tally { margin: 12px 2px 0; font-size: 13px; text-align: center; color: var(--green-dk); font-weight: 650; font-variant-numeric: tabular-nums; }
  .why-link { text-align: center; margin: 14px 0 0; font-size: 13px; }

  /* ── Login shot-timer strip (front door, ratified 2026-08-18: concept A
     as mocked, minus the numeric readout + icon direction 1). A shot timer
     is a physical object — black case, amber LCD — so these colors are
     deliberately NOT theme tokens; the strip reads the same in both modes. ── */
  #login-view { max-width: 430px; margin: 0 auto; }
  .lcd {
    background: #0b0e12; border: 1px solid #1f2732; border-radius: 14px;
    padding: 14px 16px 12px; margin-bottom: 14px;
    box-shadow: inset 0 2px 8px rgba(0, 0, 0, .6);
  }
  .lcd-state {
    display: block;
    font: 700 15px/1.2 ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
    letter-spacing: .1em; color: #ffb424; text-transform: uppercase;
  }
  .lcd.fired .lcd-state { color: #7ee08a; }
  .lcd-cadence {
    margin: 8px 0 0;
    font: 500 10px/1.4 ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
    letter-spacing: .12em; color: #5b6675; text-transform: uppercase;
  }
  #login-view .btn.primary { display: flex; width: 100%; margin-top: 16px; }

  /* ── The porch (public landing above the gate; REDESIGNED 2026-09-07 —
     owner-ratified "Option A" from the landing round, artifact a718dc1a…,
     sources docs/wireframes/landing-2026-09-07/). Three signed-out PAGES in
     one section — landing, How it works, For coaches — switched by
     uspsa-page.js, plus the gate heading over the login card, the request
     form and the footer. Every colour is a theme token so both modes hold;
     the login card's LCD is the one physical object on the page and keeps its
     own palette. The hero's decorative timer went with the redesign — the
     login LCD IS the timer at the gate now.
     Classes are porch-/ps-/rq- prefixed because one stylesheet is one
     namespace (the .ready collision rule). ── */
  /* main is 920px for the app; the landing's two-column rows want 1040px
     signed out. :has() keeps the widening tied to the porch being on screen
     rather than to a body class nothing else needs. */
  main:has(#porch:not(.hidden)) { max-width: 1080px; }
  #porch { margin: 0 auto 8px; }
  .porch-wrap { max-width: 1040px; margin: 0 auto; }
  .porch-page > section { padding: 40px 0; }
  .porch-page > section + section { border-top: 1px solid var(--line); }
  @media (min-width: 760px) { .porch-page > section { padding: 64px 0; } }
  .porch-display {
    font-family: "Barlow Condensed", "Arial Narrow", -apple-system, sans-serif;
    text-transform: uppercase; letter-spacing: .01em; line-height: 1.03; font-weight: 700;
    margin: 0; text-wrap: balance; color: var(--ink);
  }
  .porch-display em { font-style: normal; color: var(--accent); }
  .porch-cmd {
    display: inline-block; font: 700 11px/1 ui-monospace, Menlo, monospace; letter-spacing: .22em;
    color: #ffb424; background: #131007; border-radius: 6px; padding: 7px 11px; text-transform: uppercase;
  }
  .porch-stack { display: flex; flex-direction: column; gap: 14px; align-items: flex-start; }
  .porch-sub { color: var(--mut); font-size: 15.5px; margin: 0; max-width: 560px; }
  .porch-sub b, .porch-p b { color: var(--ink); }
  .porch-p { color: var(--mut); font-size: 15.5px; margin: 0; max-width: 460px; }
  .porch-fine { margin: 0; font-size: 12.5px; color: var(--mut); }
  .porch-btns { display: flex; gap: 10px; flex-wrap: wrap; }
  #porch a.btn, #porch-request a.btn { display: inline-flex; text-decoration: none; }
  #porch .btn, #porch-request .btn { padding: 12px 20px; font-size: 15px; }
  /* Hero */
  .porch-hero { display: grid; grid-template-columns: 1fr; gap: 36px; align-items: center; }
  .porch-h1 { font-size: clamp(50px, 12vw, 78px); }
  .porch-hero .porch-sub { font-size: 17px; line-height: 1.5; }
  @media (min-width: 760px) {
    .porch-hero { grid-template-columns: 1.05fr 1fr; gap: 48px; }
    .porch-hero .porch-sub { font-size: 19px; }
    .porch-hero .porch-phone { transform: rotate(-3deg); }
  }
  /* Phone frames: a bezel and a screen, no fake status bar — on a real phone
     the real one draws on top. The screens are DRAWN with the app's own
     tokens, not screenshots (backlog #152 replaces them with real captures). */
  .porch-phone {
    width: 300px; max-width: 100%; margin: 0 auto; background: #0a0a0a; border-radius: 38px; padding: 10px;
    box-shadow: inset 0 1px 0 rgba(255,255,255,.08), 0 30px 60px -30px rgba(0,0,0,.6); flex: none;
  }
  .porch-screen {
    background: var(--bg); border-radius: 29px; padding: 44px 12px 20px; min-height: 500px;
    overflow: hidden; color: var(--ink); display: flex; flex-direction: column; gap: 8px; font-size: 12.5px;
  }
  .ps-head { font-size: 12px; color: var(--mut); display: flex; justify-content: space-between; align-items: center; gap: 8px; }
  .ps-head b { color: var(--ink); font-weight: 650; }
  .ps-title { text-align: center; font-size: 19px; font-weight: 750; letter-spacing: -.015em; }
  .ps-title em { font-style: normal; color: var(--accent); }
  .ps-sub { text-align: center; font-size: 12px; color: var(--mut); font-variant-numeric: tabular-nums; }
  .ps-lbl { font: 700 10px/1 ui-monospace, Menlo, monospace; letter-spacing: .2em; color: var(--mut); text-transform: uppercase; margin-top: 2px; }
  .ps-rail { position: relative; margin: 18px 0 4px; }
  .ps-rail .rail { height: 26px; margin: 0; }
  .ps-rail .ps-you { position: absolute; left: 62%; top: -3px; bottom: -3px; width: 2px; background: var(--accent); }
  .ps-rail .ps-youlbl { position: absolute; left: 62%; top: -16px; transform: translateX(-50%); font-size: 10px; font-weight: 800; color: var(--accent); white-space: nowrap; }
  .ps-pos { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; box-shadow: var(--shadow-sm); }
  .ps-pos .p { font-size: 14px; font-weight: 750; letter-spacing: -.01em; display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
  .ps-pos .p small { font-weight: 500; color: var(--mut); font-size: 11.5px; }
  .ps-rds { font-size: 11px; font-weight: 750; color: var(--accent-dk); background: var(--accent-soft); border-radius: 99px; padding: 2px 8px; font-variant-numeric: tabular-nums; }
  .ps-tgt { display: flex; gap: 8px; align-items: baseline; padding: 5px 0; border-top: 1px solid var(--line-2); font-size: 12px; }
  .ps-tgt .t { font-weight: 700; min-width: 44px; flex: none; }
  .ps-tgt .n { color: var(--mut); font-size: 11px; flex: none; }
  .ps-tgt.sb { justify-content: space-between; }
  .ps-exit { margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--line-2); font-size: 11.5px; font-weight: 650; color: var(--accent-dk); }
  .ps-move { display: flex; align-items: center; gap: 8px; margin: 2px 4px; color: var(--mut); font-size: 11px; font-weight: 650; }
  .ps-move::before, .ps-move::after { content: ""; flex: 1; border-top: 2px dashed var(--line); }
  .ps-move i { color: var(--accent); font-style: normal; }
  .ps-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
  .ps-row .k { font-weight: 700; font-size: 13px; }
  .ps-row .m { font-size: 11.5px; color: var(--mut); }
  .ps-row .r { font-size: 11px; color: var(--accent-dk); font-weight: 650; text-align: right; }
  .ps-row .v { font-weight: 750; font-variant-numeric: tabular-nums; }
  .ps-row .v.hit { color: var(--green-dk); }
  .ps-chip { font-size: 10.5px; font-weight: 650; padding: 3px 8px; border-radius: 99px; border: 1px solid var(--line); color: var(--mut); white-space: nowrap; }
  .ps-chip.on { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
  .ps-chip.fill { background: var(--accent); border-color: var(--accent); color: var(--on-fill); }
  .ps-offer { padding: 9px 11px; border: 1px solid var(--accent); border-radius: 10px; background: var(--accent-soft); font-size: 12.5px; }
  .ps-offer.note { border-left-width: 3px; line-height: 1.5; }
  .ps-num { font-weight: 800; color: var(--accent); flex: none; }
  .ps-quote { font-size: 12.5px; line-height: 1.5; }
  .ps-foot { margin-top: auto; }
  .ps-btn { display: inline-flex; align-items: center; justify-content: center; min-height: 40px; border-radius: 9px; padding: 8px 14px; font-size: 13px; font-weight: 600; background: var(--accent); color: var(--on-fill); flex: 1; }
  .ps-btn.ghost { background: var(--card); border: 1px solid var(--line); color: var(--mut); }
  .ps-btns { display: flex; gap: 8px; margin-top: auto; }
  .ps-drop { border: 2px dashed var(--line); border-radius: 12px; padding: 16px 12px; text-align: center; color: var(--mut); font-size: 12px; display: flex; flex-direction: column; align-items: center; gap: 6px; }
  .ps-drop svg { width: 22px; height: 22px; color: var(--accent); }
  .ps-chips { display: flex; gap: 6px; flex-wrap: wrap; }
  .ps-segs { display: flex; gap: 4px; }
  .ps-mut { font-size: 11.5px; color: var(--mut); }
  .ps-center { text-align: center; }
  /* The ‹ at the top of a subpage (2026-09-07). Same voice as every in-app
     ‹ Back: quiet, accent, one line, a real 44px tap box on a phone. */
  .porch-back { display: inline-flex; align-items: center; min-height: 44px; margin: -12px 0 -8px; color: var(--accent); font-weight: 600; font-size: 14px; text-decoration: none; align-self: flex-start; }
  .porch-back:hover { text-decoration: underline; }
  /* Proof strip — four equal cells, 2×2 on a phone and one row from 760px.
     Tidied 2026-09-07 (owner: "clean up the layout"): the heading never
     wraps (it is the cell's label, and a wrapped label reads as two items),
     the subtitle wraps as a balanced pair of lines rather than orphaning
     its last word, the cells share a top edge and a vertical rhythm, and
     the line box is fixed so a one-line subtitle beside a two-line one does
     not leave the row ragged. */
  .porch-proof { border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--card); padding: 20px 0; margin: 0 calc(-1 * clamp(14px, 4vw, 24px)); }
  .porch-proof .porch-wrap { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px 16px; padding: 0 clamp(14px, 4vw, 24px); align-items: start; }
  .porch-proof .porch-wrap > div { display: grid; gap: 4px; align-content: start; }
  .porch-proof .porch-display { font-size: clamp(15px, 4.8vw, 18px); line-height: 1.1; white-space: nowrap; }
  .porch-proof .porch-display + span { font-size: 12.5px; line-height: 1.35; color: var(--mut); text-wrap: balance; max-width: 22ch; }
  @media (min-width: 760px) {
    .porch-proof { padding: 24px 0; }
    .porch-proof .porch-wrap { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 24px; }
    .porch-proof .porch-display { font-size: 21px; }
    .porch-proof .porch-display + span { font-size: 13px; max-width: 24ch; }
  }
  /* Three steps */
  .porch-h2 { font-size: clamp(34px, 8vw, 52px); }
  .porch-h3 { font-size: clamp(30px, 7vw, 36px); margin: 10px 0 8px; }
  .porch-steps { display: grid; grid-template-columns: 1fr; gap: 22px; margin-top: 28px; }
  @media (min-width: 760px) { .porch-steps { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 32px; margin-top: 44px; } }
  .porch-step { display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }
  .porch-step .ic { width: 40px; height: 40px; border-radius: 10px; display: grid; place-items: center; background: var(--accent-soft); color: var(--accent-dk); }
  .porch-step .ic svg { width: 22px; height: 22px; }
  .porch-step h3 { font-size: 19px; margin: 0; }
  .porch-step p { margin: 0; color: var(--mut); font-size: 14.5px; }
  /* Feature rows */
  .porch-row { display: grid; grid-template-columns: 1fr; gap: 28px; align-items: center; }
  @media (min-width: 760px) { .porch-row { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 64px; } .porch-row.flip > .porch-stack { order: 2; } }
  .porch-ticks { margin: 12px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 6px; font-size: 14px; color: var(--ink); }
  .porch-ticks li { display: flex; gap: 8px; align-items: flex-start; }
  .porch-ticks svg { width: 16px; height: 16px; color: var(--green-dk); flex: none; margin-top: 3px; }
  .porch-stepnum { display: flex; align-items: center; gap: 10px; }
  .porch-stepnum b { font: 700 13px/1 ui-monospace, Menlo, monospace; color: var(--accent); }
  /* Lefties card — the mirror demo (the only proof this page can offer a
     left-handed shooter, kept through every porch rewrite since 2026-08-19). */
  .porch-mirror { border: 1px solid var(--line); border-radius: 12px; background: var(--card-2); padding: 24px 20px; display: grid; grid-template-columns: 1fr; gap: 20px; align-items: center; }
  @media (min-width: 760px) { .porch-mirror { grid-template-columns: 1fr 1.2fr; gap: 48px; padding: 36px 40px; } }
  .porch-side { display: flex; align-items: center; gap: 8px; margin: 6px 0 0; font-size: 13px; color: var(--mut); }
  .porch-side button {
    font-family: inherit; font-size: 13px; font-weight: 650; line-height: 1; min-height: 44px;
    padding: 0 16px; border-radius: 99px;
    border: 1px solid var(--line); background: var(--card); color: var(--mut); cursor: pointer;
  }
  .porch-side button[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: var(--on-fill); }
  .porch-demo { display: grid; gap: 8px; }
  .porch-pos { border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; background: var(--card); box-shadow: var(--shadow-sm); }
  .porch-pos b { font-size: 14px; font-weight: 750; letter-spacing: -.01em; }
  .porch-pos b small { font-weight: 500; color: var(--mut); font-size: 11.5px; margin-left: 4px; }
  .porch-pos p { margin: 4px 0 0; font-size: 12.5px; color: var(--ink); }
  .porch-pos p b { font-size: inherit; }
  .porch-move { display: flex; align-items: center; gap: 8px; color: var(--mut); font-size: 11px; font-weight: 650; margin: 0 4px; }
  .porch-move::before, .porch-move::after { content: ""; flex: 1; border-top: 2px dashed var(--line); }
  /* "Keep your apps" — the gap cards, three across (A from the positioning
     round, owner-ratified 2026-08-23, artifact 95c70d0b…; one line each now). */
  .porch-keep { display: grid; grid-template-columns: 1fr; gap: 12px; margin-top: 20px; }
  @media (min-width: 760px) { .porch-keep { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
  .porch-gapcard { border: 1px solid var(--line); border-radius: 12px; background: var(--card); padding: 14px 16px; }
  .porch-gapcard .gc-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
  .porch-gapcard .gc-app { font-weight: 750; font-size: 15px; color: var(--ink); }
  .porch-gapcard .gc-keep {
    display: inline-block; font: 700 10px/1 ui-monospace, Menlo, monospace; letter-spacing: .14em;
    color: var(--green-dk); border: 1px solid var(--green-dk); border-radius: 999px; padding: 4px 8px; text-transform: uppercase;
  }
  .porch-gapcard p { margin: 8px 0 0; font-size: 13.5px; color: var(--mut); }
  .porch-gapcard p em { font-style: normal; color: var(--ink); font-weight: 600; }
  /* Q&A rows (How it works) */
  .porch-qa { display: grid; grid-template-columns: 1fr; gap: 4px; border-top: 1px solid var(--line-2); padding-top: 14px; }
  .porch-qa b { color: var(--ink); }
  .porch-qa span { color: var(--mut); font-size: 14.5px; }
  @media (min-width: 760px) { .porch-qa { grid-template-columns: 1fr 2fr; gap: 24px; } }
  /* A multi-part answer, inside the one answer cell (2026-09-17): the
     price answer is four sentences where its neighbours are two, and ten
     unbroken lines at 390 is the shape a reader skips. */
  .porch-qa-p { display: block; }
  .porch-qa-p + .porch-qa-p { margin-top: 9px; }
  .porch-qas { display: flex; flex-direction: column; gap: 16px; margin-top: 20px; }
  /* Gate heading over the login card */
  .porch-gate { text-align: center; padding: 40px 0 8px; border-top: 1px solid var(--line); display: flex; flex-direction: column; align-items: center; gap: 12px; }
  .porch-gate .porch-display { font-size: clamp(36px, 8vw, 56px); }
  .porch-gate .porch-sub { text-align: center; max-width: 520px; }
  /* Top-bar text links — desktop only. At 320 the bar's budget is three
     44px circles and the wordmark (40.4px slack signed out, measured
     2026-08-23); two text links do not fit and the phone gets them in the
     hero, the coaches row and the footer instead. Hidden signed in with the
     theme button. */
  #porch-nav { display: none; align-items: center; gap: 18px; margin-right: 6px; font-size: 14px; font-weight: 600; }
  #porch-nav a { color: var(--mut); text-decoration: none; padding: 6px 2px; }
  #porch-nav a:hover, #porch-nav a[aria-current="page"] { color: var(--ink); }
  @media (min-width: 640px) { #porch-nav:not(.hidden) { display: inline-flex; } }
  /* The request form (owner, 2026-09-07): one form on every signed-out page,
     the only way in the site offers. No name and no address on the page. */
  #porch-request { padding: 40px 0 8px; border-top: 1px solid var(--line); }
  #porch-request .porch-wrap { display: grid; grid-template-columns: 1fr; gap: 24px; align-items: start; }
  @media (min-width: 760px) { #porch-request { padding: 64px 0 24px; } #porch-request .porch-wrap { grid-template-columns: 1fr 1.2fr; gap: 56px; } }
  .rq-form { display: flex; flex-direction: column; gap: 12px; padding: 18px 16px; }
  @media (min-width: 760px) { .rq-form { padding: 24px; } }
  .rq-two { display: grid; grid-template-columns: 1fr; gap: 12px; }
  @media (min-width: 560px) { .rq-two { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  .rq-form .fld { margin-top: 0; }
  .rq-form input, .rq-form textarea { width: 100%; }
  .rq-roles { display: flex; gap: 6px; flex-wrap: wrap; }
  .rq-role {
    font: inherit; font-size: 13px; font-weight: 650; min-height: 44px; padding: 0 14px; border-radius: 99px;
    border: 1px solid var(--line); background: var(--card); color: var(--mut); cursor: pointer;
  }
  .rq-role[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
  .rq-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
  .rq-foot .porch-fine { flex: 1; min-width: 160px; }
  /* The honeypot: off-screen, never hidden with display:none (some bots skip
     those), out of the tab order, out of the accessibility tree. */
  .rq-hp { position: absolute; left: -9999px; top: auto; width: 1px; height: 1px; overflow: hidden; }
  #porch-foot { text-align: center; color: var(--mut); font-size: 12px; margin: 26px auto 0; max-width: 520px; }
  /* Five links since Privacy and Terms (2026-09-11): they no longer fit one
     row at 320px, so the row WRAPS rather than shrinking the 6px padding
     that makes each a usable tap target. */
  .porch-footlinks { display: flex; flex-wrap: wrap; justify-content: center; gap: 0 16px; margin-bottom: 8px; font-weight: 600; }
  .porch-footlinks a { color: var(--mut); text-decoration: none; padding: 6px 2px; }
  .porch-footlinks a:hover { color: var(--ink); }
  /* No opacity dimming here: --mut is already the muted token, and fading
     12px text to 80% over the page background measured 3.5:1 in light mode —
     under the 4.5:1 the page is audited to. Full token is 5.2:1. */
  .porch-checked { display: block; margin-top: 6px; }
  /* The legal pages carry their own date; the footer's CHECKED stamp is the
     porch + how-to audit's, so it steps aside rather than disagree. */
  main:has(.porch-page[data-legal]:not(.hidden)) #porch-stamp { display: none; }
  /* Privacy and Terms (2026-09-11): prose pages in the porch's type, one
     reading column. Headings reuse .porch-display at a size between the
     proof strip and .porch-h3; body copy is .porch-p's muted 15.5px. */
  .porch-legal { display: grid; gap: 28px; max-width: 680px; }
  .porch-legal-h { font-size: clamp(24px, 6vw, 30px); margin: 0 0 8px; }
  .porch-legal p, .porch-legal li { color: var(--mut); font-size: 15.5px; line-height: 1.55; margin: 0; }
  .porch-legal .porch-fine { font-size: 12.5px; }
  .porch-legal b { color: var(--ink); }
  .porch-legal ul { margin: 0; padding-left: 20px; display: grid; gap: 8px; }
  .porch-legal a { color: var(--accent); }
  .btn-link {
    display: block; width: 100%; margin-top: 10px; background: none; border: 0; padding: 8px;
    font: inherit; font-size: 13.5px; font-weight: 500; color: var(--mut);
    cursor: pointer; text-align: center;
  }
  .btn-link:hover { color: var(--ink); }
  /* Credits (decision 1, 2026-09-07). The price hint under a spend button —
     the numbers in ink, tabular; the ceiling's offer is a link in the accent.
     The Credits row's chip goes accent at 0, the one place it lights up. */
  .price-hint { font-size: 12.5px; line-height: 1.45; }
  .price-hint b { color: var(--ink); font-weight: 650; font-variant-numeric: tabular-nums; }
  .price-hint a { color: var(--accent); font-weight: 650; text-decoration: none; }
  .price-hint a:hover { text-decoration: underline; }
  .rdr-hint { margin: 6px 0 0; }
  .credits-chip { text-decoration: none; flex: none; }
  /* The Credits row's label is a door beside the chip (2026-09-16). */
  .row .row-door { flex: 1 1 auto; min-width: 0; min-height: 44px; display: flex; align-items: center; gap: 11px; padding: 0; border: 0; background: none; font: inherit; color: inherit; text-align: left; cursor: pointer; }
  /* The one hub row whose value WRAPS (2026-09-16): the chip leaves this row
     118px for its label at 320, and its value is the whole point of the row.
     Every other .rv keeps the hub's truncation rule. */
  #credits-row .rv { white-space: normal; }
  .row .row-door:hover .rt { color: var(--accent); }
  .row .row-door:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 8px; }
  /* Profile › Credits (2026-09-16) */
  .cr-hero { background: var(--card-2); border: 1px solid var(--line); border-radius: 13px; padding: 15px; display: flex; flex-direction: column; gap: 10px; margin: 8px 0 4px; }
  .cr-bal { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
  .cr-n { font-size: 40px; font-weight: 700; letter-spacing: -.03em; line-height: 1; font-variant-numeric: tabular-nums; }
  .cr-u { font-weight: 600; color: var(--mut); }
  .cr-spans { font-size: 12.5px; flex-basis: 100%; }
  .cr-policy { margin: 0; font-size: 12.5px; }
  .cr-h-s { font-weight: 500; font-size: 12px; margin-left: 6px; }
  .cr-free { font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
  .cr-wrap { white-space: normal; }
  .cr-amt { font-weight: 700; font-variant-numeric: tabular-nums; min-width: 34px; text-align: right; }
  .cr-amt.up { color: var(--green-dk); }
  .cr-more { margin: 8px 0 0; text-align: center; }
  /* The free-always line, under what a credit buys (2026-09-17): it is the
     answer's other half, so it sits with the prices rather than in the hero. */
  .cr-note { margin: 8px 2px 0; }
  /* CREDITS ON HOME (2026-09-17, shape B): Home's own tile grammar, two up at
     390 and two up at 320 — the prices are the figures, so the grid never has
     to carry a long word. */
  .home-cred-line { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--mut); }
  .home-cred-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  .home-cred-tile { display: flex; flex-direction: column; gap: 2px; padding: 9px 11px; border: 1px solid var(--line); border-radius: 10px; }
  .hct-n { font-size: 18px; font-weight: 700; line-height: 1; color: var(--accent); font-variant-numeric: tabular-nums; }
  .hct-l { font-size: 11.5px; color: var(--mut); }
  /* The place an act lives WRAPS (2026-09-17), the one other .rv that does.
     Measured at 320: three of the five truncated — "Match › Build my st…" —
     and where the act lives is the point of the row, exactly the reason
     #credits-row's own value wraps. Scoped to these rows, so History and the
     counters keep the hub's truncation rule. */
  .row.cr-buy .rv { white-space: normal; }
  .credits-chip.out { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
  /* A "go there" line under a saved review (spend-surface #7, 2026-09-07):
     one sentence and the door, boxed so it reads as a next step and not as
     part of the saved-timestamp line above it. */
  .goto {
    display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; margin: 12px 0 0;
    padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px;
    background: var(--card); font-size: 13px; line-height: 1.4;
  }
  .goto a { color: var(--accent); font-weight: 650; white-space: nowrap; text-decoration: none; }
  .goto a:hover { text-decoration: underline; }

  /* Brand mark: the SHOW CLEAR. wordmark (docs/brand/logos/svg, ratified 2026-09-06),
     inlined in index.html. Letters are currentColor so the mark follows the
     theme; the dot is the fixed brand orange (#F95108), matching icon.svg.
     18px tall is 103px wide — the same footprint the old dot + text held, so
     the top bar's 320px budget is untouched. No ™ on the mark (owner, 2026-09-11):
     the notice is a porch-footer line, so the mark keeps this footprint. */
  .wordmark { height: 18px; width: auto; display: block; flex: none; }
  .wordmark .wm-dot { fill: #F95108; }

  /* ── Cards ── */
  .card {
    background: var(--card); border: 1px solid var(--line); border-radius: 14px;
    padding: clamp(16px, 3vw, 24px); margin-bottom: 18px; box-shadow: var(--shadow-sm);
  }
  .card { overflow-wrap: break-word; }
  .card h2 { margin: 0 0 12px; font-size: 17px; font-weight: 650; letter-spacing: -.015em; text-wrap: balance; }
  .card h3 { margin: 16px 0 6px; font-size: 14px; font-weight: 650; }

  /* ── Forms ── */
  /* p.fld labels a GROUP of controls (the division checkboxes) — a <label>
     with no control to point at would be an empty association. */
  label.fld, p.fld { display: block; font-size: 12.5px; font-weight: 550; color: var(--mut); margin: 12px 0 4px; }
  input[type=text], input[type=email], input[type=password], input[type=number], input[type=date], input[type=search], textarea, select {
    width: 100%; padding: 9px 12px; border: 1px solid var(--line); border-radius: 9px;
    font: inherit; background: var(--card); color: var(--ink);
    transition: border-color .12s, box-shadow .12s; appearance: none; -webkit-appearance: none;
  }
  select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%2394a0ad' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right 12px center; padding-right: 32px;
  }
  input:focus, textarea:focus, select:focus {
    outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--ring);
  }
  input[type=checkbox] { accent-color: var(--accent); width: 15px; height: 15px; }
  /* ≥16px inputs on small screens or iOS Safari zooms/pans the page on focus */
  @media (max-width: 640px) {
    input[type=text], input[type=email], input[type=password], input[type=number], input[type=date], input[type=search], textarea, select { font-size: 16px; }
  }
  textarea { min-height: 72px; resize: vertical; }

  /* ── Baseline steppers (2026-08-20) ──
     Five profile fields are seconds off a timer, nudged rather than typed, and
     they are edited on a phone at the range — so each gets −/+ at the Web
     Interface Guidelines' 44px touch minimum, the unit drawn INSIDE the field
     (a shooter typing "1.5s" used to lose the entry entirely), and the native
     spinners suppressed so they neither duplicate the buttons nor sit under
     the unit. Steppers are DISABLED while the field is empty: they nudge a
     measurement, and seeding one from nothing would be the app inventing a
     number for someone to nod at. Colors are tokens only — the pair below is
     the same hairline/ghost shape as .btn.ghost, so it inherits both themes. */
  /* Capped: a full-width box for "1.5" pushed the unit to the far right,
     reading as unrelated to the centred value (preview check 2026-08-20). */
  .stepper { display: flex; align-items: stretch; gap: 8px; max-width: 232px; }
  .stepper .stp {
    flex: 0 0 auto; width: 44px; min-height: 44px;
    border: 1px solid var(--line); border-radius: 9px;
    background: var(--card); color: var(--mut);
    font: inherit; font-size: 18px; font-weight: 600; line-height: 1;
    cursor: pointer; transition: border-color .12s, color .12s, transform .06s;
  }
  .stepper .stp:hover:not(:disabled) { color: var(--ink); border-color: var(--mut); }
  .stepper .stp:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
  .stepper .stp:active:not(:disabled) { transform: translateY(1px); }
  .stepper .stp:disabled { opacity: .4; cursor: default; }
  .stepper .swrap { position: relative; flex: 1 1 auto; }
  .stepper .swrap input { padding-right: 28px; text-align: center; }
  .stepper .unit {
    position: absolute; right: 11px; top: 50%; transform: translateY(-50%);
    font-size: 12.5px; color: var(--mut); pointer-events: none;
  }
  .stepper input[type=number]::-webkit-outer-spin-button,
  .stepper input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
  .stepper input[type=number] { -moz-appearance: textfield; }
  .fld-hint { margin: 5px 0 0; font-size: 12px; color: var(--mut); }
  .fld-hint.gap { color: var(--warn); }
  fieldset { border: 1px solid var(--line); border-radius: 10px; background: var(--card-2); }
  legend { color: var(--ink); }

  /* ── Buttons ── */
  /* 44px floor (#66, 2026-09-05): the shared button was 42px — padding 10 +
     10 around a 14px/1.5 line — under the touch minimum the top bar's circles,
     the stage bar's pills and the range-day ticks already keep. A min-height,
     not more padding, so the text stays where it was and the row that wraps a
     button grows by exactly the 2px the floor asks for; inline-flex centres
     the label inside the taller box. Measured at 320 before and after
     (dev/walk/out/2026-09-06-66): the tab bar (64), top bar (65) and stage
     bar row (44) are untouched, no card crossed the fold. */
  .btn {
    display: inline-flex; align-items: center; justify-content: center; min-height: 44px;
    border: 0; border-radius: 9px; padding: 10px 18px;
    font: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
    transition: background .12s, border-color .12s, color .12s, transform .06s, box-shadow .12s;
  }
  /* An <a class="btn"> is a real link (the squad invite's sms: hand-off is
   one — only the browser can open the share sheet), and a link brings its
   own underline and accent colour with it. Reset both here so a control
   styled as a button reads as one whichever element it is. */
a.btn { text-decoration: none; }
a.btn.primary, a.btn.primary:hover { color: var(--on-fill); }

.btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
  .btn:active { transform: translateY(1px); }
  .btn.primary { background: var(--accent); color: var(--on-fill); box-shadow: var(--shadow-sm), inset 0 1px 0 rgba(255,255,255,.12); }
  .btn.primary:hover { background: var(--accent-dk); }
  .btn.primary:disabled { background: var(--line); color: var(--mut); cursor: default; box-shadow: none; transform: none; }
  .btn.ghost { background: var(--card); border: 1px solid var(--line); color: var(--mut); }
  /* On a screen a destructive act is a red TEXT act with an ellipsis
     (2026-09-07), and the confirm sheet always follows — `.btn.danger` below
     stays an outline there (its rule: the one filled button on a screen is
     the primary). The filled red exists in exactly one place, inside the
     confirm sheet (`.bs-confirm`, further down). */
  .act-danger {
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    background: none; border: 0; padding: 8px 10px; min-height: 44px; cursor: pointer;
    font: inherit; font-size: 13.5px; font-weight: 500; color: var(--err);
  }
  .act-danger svg { width: 14px; height: 14px; }
  .act-danger:hover { text-decoration: underline; }
  .btn.ghost:hover { color: var(--ink); border-color: var(--mut); }

  .row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
  @media (max-width: 640px) { .row2 { grid-template-columns: 1fr; } }
  .mut { color: var(--mut); font-size: 13px; }

  /* ── The profile hub (ratified "A + C", 2026-08-20) ─────────────────────
     See the markup comment on #profile-panel for what this replaced and why.
     Everything below is the shell that makes a 3,871px form readable: a
     grouped list whose rows carry their own current value, an identity card
     with the plan-readiness meter, the baseline tiles, and one save bar. All
     of it is tokens — a new surface inherits both themes rather than
     inventing a look (the topbar merge decision, applied down here). */

  /* Group heading over a list of rows — the iOS-settings shape, which is
     what a shooter already knows this pattern from. */
  /* h3.grouph, not .grouph: `.card h3` sets its own size a few rules up, and
     an equal-specificity selector later in the sheet is what beats it. */
  h3.grouph {
    font-size: 10.5px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
    color: var(--mut); margin: 20px 4px 7px; display: flex; align-items: center; gap: 7px;
  }
  .rows {
    background: var(--card); border: 1px solid var(--line); border-radius: 13px;
    overflow: hidden; box-shadow: var(--shadow-sm);
  }
  /* One row = one section. Rendered as a <button> when it opens something and
     a <div class="static"> when it does not, so only the ones that navigate
     are in the tab order. 56px clears the Web Interface Guidelines' 44px
     touch minimum with the second line inside it. */
  .row {
    display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
    padding: 11px 13px; min-height: 56px;
    border: 0; border-top: 1px solid var(--line-2); background: none;
    font: inherit; color: var(--ink); text-decoration: none;
  }
  .rows > :first-child.row { border-top: 0; }
  button.row, a.row { cursor: pointer; }
  button.row:hover, a.row:hover { background: var(--card-2); }
  button.row:focus-visible, a.row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
  .row .rl { flex: 1 1 auto; min-width: 0; }
  .row .rt { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; font-size: 14px; font-weight: 600; }
  .row .rt b { font-weight: 700; }
  /* The second line IS the point of the row — the value you came to read. It
     truncates rather than wrapping, so ten rows stay ten rows. */
  .row .rv { display: block; font-size: 12.5px; color: var(--mut); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .row .chev { flex: none; color: var(--mut); font-size: 16px; }
  .row .ricon {
    flex: none; width: 32px; height: 32px; border-radius: 9px;
    background: var(--card-2); border: 1px solid var(--line);
    display: grid; place-items: center; color: var(--mut);
  }
  .row .ricon svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.9; }
  #account-row { flex-wrap: wrap; }
  /* Appearance: three positions as a compact .segs, the same control the
     Match tab's Stages/Results wears, sized for a hub row. */
  .theme-pick { flex: none; padding: 2px; }
  .theme-pick button { padding: 7px 10px; font-size: 12.5px; min-height: 36px; }
  #account-row #who { font-weight: 700; }
  /* A row-shaped state chip: "3 untimed", "not set". Warn is a gap the plan
     will have to work around; ok is a section that is answered. */
  .flag { flex: none; font-size: 10.5px; font-weight: 700; border-radius: 99px; padding: 2px 8px; white-space: nowrap; }
  .flag.gap { background: var(--warn-bg); color: var(--warn); border: 1px solid var(--warn-line); }
  .flag.ok { color: var(--green-dk); border: 1px solid var(--green-dk); }

  /* Identity card — name, member number, and the chips that say what the app
     will build a plan as. */
  .idcard { background: var(--card-2); border: 1px solid var(--line); border-radius: 13px; padding: 15px; }
  .idhead { display: flex; gap: 13px; align-items: center; }
  .idmark {
    flex: none; width: 52px; height: 52px; border-radius: 15px; display: grid; place-items: center;
    background: var(--accent-soft); color: var(--accent-dk);
    font-weight: 700; font-size: 19px; letter-spacing: -.01em;
  }
  :root[data-resolved-theme="dark"] .idmark { color: var(--accent); }
  .idhead .nm { display: block; margin: 0; font-size: 18px; font-weight: 700; letter-spacing: -.02em; line-height: 1.25; }
  .idhead .nm.none { color: var(--mut); font-weight: 600; }
  .idhead .sub { display: block; font-size: 12.5px; color: var(--mut); font-variant-numeric: tabular-nums; }
  .pf-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 11px; }
  .pf-chip {
    font-size: 11.5px; font-weight: 650; border-radius: 99px; padding: 4px 10px;
    border: 1px solid var(--line); background: var(--card); color: var(--mut);
  }
  .pf-chip.cls { border-color: transparent; background: var(--band-3); color: var(--ink); }
  .pf-chip.on { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }

  /* The readiness meter: what THIS plan-builder is actually standing on,
     counted over the same seven inputs uspsa.js's profileGaps names — strong
     side, divisions, and the five measured baselines. A gap beside its own
     field says "this one is empty"; only a total says "your plans are running
     on generic numbers", which is the sentence that sends someone to the
     range with a timer. */
  .ready { margin-top: 14px; padding-top: 13px; border-top: 1px solid var(--line); }
  .ready .lbl { display: flex; justify-content: space-between; gap: 10px; font-size: 12px; font-weight: 600; color: var(--mut); margin-bottom: 7px; }
  .ready .lbl span { font-variant-numeric: tabular-nums; flex: none; }
  .meter { display: flex; gap: 3px; }
  .meter i { flex: 1; height: 6px; border-radius: 99px; background: var(--line); }
  .meter i.f { background: var(--accent); }
  .meter i.w { background: var(--warn-line); }
  .ready .why { margin: 9px 0 0; font-size: 12px; color: var(--mut); }
  .ready .why.gap { color: var(--warn); }

  .hub-foot { margin: 22px 0 0; }

  /* ── Sub-views ───────────────────────────────────────────────────────────
     Not slide-overs and not a new tab: the same in-tab swap the plan view's
     segments already do (the shell's no-side-panels rule, owner 2026-08-18).
     Only the visibility moves — every field stays in the DOM, so Save is
     still one PUT of the whole profile. */
  .pv-back {
    display: inline-flex; align-items: center; gap: 6px; min-height: 44px;
    border: 0; background: none; padding: 4px 0; margin: -8px 0 2px;
    font: inherit; font-size: 13.5px; font-weight: 600; color: var(--accent); cursor: pointer;
  }
  .pv-back:hover { color: var(--accent-dk); }
  .pv-back:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 8px; }
  .pv h2 { margin: 0 0 4px; }

  /* ── Baseline tiles (concept C) ──────────────────────────────────────────
     The five measured numbers read value-first, because they are the only
     profile fields that change often. The tile is the READ; tapping it opens
     the same stepper as before, on the same input, in place — the input is
     never moved in the DOM, so nothing about reading or saving the form
     changes. An untimed tile carries the gap in its own face rather than in
     a hint under a field nobody scrolled to. */
  .tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
  /* Two up at 375px (the design's phone) and still two up at 360; one column
     only on a 320px iPhone SE, where a 25px number plus its label cannot
     share a row. Measured, not guessed — same rule as the tab bar. */
  @media (max-width: 340px) { .tiles { grid-template-columns: 1fr; } }
  .tile {
    text-align: left; width: 100%; border: 1px solid var(--line); background: var(--card);
    border-radius: 12px; padding: 11px 12px; min-height: 76px;
    font: inherit; color: var(--ink); cursor: pointer; box-shadow: var(--shadow-sm);
    transition: border-color .12s, box-shadow .12s;
  }
  .tile:hover { border-color: var(--mut); }
  .tile:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--ring); }
  .tile[aria-expanded="true"] { border-color: var(--accent); box-shadow: 0 0 0 3px var(--ring); }
  .tile .tl { display: block; font-size: 11.5px; color: var(--mut); font-weight: 600; margin-bottom: 3px; }
  .tile .tv { display: block; font-size: 25px; font-weight: 700; letter-spacing: -.03em; font-variant-numeric: tabular-nums; line-height: 1.1; }
  .tile .tv u { text-decoration: none; font-size: 13px; font-weight: 600; color: var(--mut); margin-left: 2px; }
  .tile .ts { display: block; margin-top: 5px; font-size: 10.5px; color: var(--mut); }
  .tile.gap { background: var(--warn-bg); border-color: var(--warn-line); }
  .tile.gap .tl, .tile.gap .tv, .tile.gap .ts { color: var(--warn); }
  /* A field named by a profile WARNING (2026-08-20): the banner at the top of
     the panel says what is wrong, the tile says which numbers it means. */
  .tile.flagged { border-color: var(--warn-line); }
  .tile.flagged .ts { color: var(--warn); }
  .bl { display: contents; }
  .bl-edit { grid-column: 1 / -1; border: 1px solid var(--accent); border-radius: 12px; padding: 12px; background: var(--card); }
  .bl-edit .fld { margin-top: 0; }

  /* ── The save bar ────────────────────────────────────────────────────────
     Shown only when the form actually differs from what is saved, and sticky
     above the tab bar so it is in view from whichever sub-view made the
     change — the old three-buttons-at-the-bottom row was up to 3,000px away
     from most of what it saved. Opaque, not translucent: it sits over form
     fields, and reading a value through the bar that saves it is worse than
     the extra weight. */
  .savebar {
    position: sticky; z-index: 6;
    /* Clear of the tab bar, which is fixed and 64px tall (measured, like the
       tab bar's own sizing calls) plus the phone's home indicator. A bar that
       tucks under the tabs is a Save button nobody can press. */
    bottom: calc(76px + env(safe-area-inset-bottom));
    display: flex; align-items: center; gap: 12px;
    margin: 18px 0 0; padding: 11px 13px;
    background: var(--card); border: 1px solid var(--line); border-radius: 12px;
    box-shadow: var(--shadow-md);
  }
  .savebar .sm { flex: 1 1 auto; min-width: 0; font-size: 12.5px; font-weight: 600; color: var(--mut); }
  .savebar .sm.ok { color: var(--green-dk); }

  /* Squad-code number pair (2026-08-20): two short fields side by side, each
     keeping the 16px input size the mobile audit pinned (a smaller one zooms
     iOS Safari on focus). They wrap rather than shrink on a narrow phone. */
  .inv-squad-nums { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 10px; }
  .inv-squad-nums span { flex: 1 1 120px; }
  .inv-squad-nums input { width: 100%; }

  /* The tip jar's two homes. Both are links in running text, not buttons —
     an ask, not a call to action. */
  #donate-line a { color: var(--accent); font-weight: 600; }
  #donate-acct .rt { color: var(--accent); }

  /* ── Banners ── */
  /* The service-worker update strip (B1, 2026-09-07): the savebar's seat —
     fixed just above the tab bar, clear of the home indicator — in the
     savebar's dress, so it reads as the app's own notice and not a browser
     bar. Shown by core.js, dismissed by the reload it offers. */
  /* The line under the top bar (2026-09-16): in flow, one sentence, a door. */
  .credits-line {
    display: block; width: 100%; min-height: 36px; padding: 8px 16px; cursor: pointer;
    border: 0; border-bottom: 1px solid var(--line); background: var(--card-2); color: var(--mut);
    font: 600 12.5px/1.3 inherit; font-family: inherit; text-align: center;
  }
  .credits-line:hover { color: var(--ink); }
  .credits-line:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
  /* A day in progress on screen is never interrupted, and the wrap's Save
     stays above the fold at 390: rangeday-page.js marks the session and
     the wrap on <html>, and the line hides only while that tab is showing. */
  html.rd-day-on:has(#rangeday-card:not(.hidden)) #credits-line { display: none; }
  .sw-update {
    position: fixed; left: 50%; transform: translateX(-50%); z-index: 11;
    bottom: calc(76px + env(safe-area-inset-bottom));
    display: flex; align-items: center; gap: 12px;
    width: min(calc(100% - 28px), 420px); padding: 9px 9px 9px 14px;
    background: var(--card); border: 1px solid var(--line); border-radius: 12px;
    box-shadow: var(--shadow-md); font-size: 13px; font-weight: 600; color: var(--ink);
  }
  .sw-update span { flex: 1 1 auto; }
  .install-steps { padding-left: 22px; margin: 12px 0 14px; }
  .install-steps li { margin: 8px 0; line-height: 1.45; }
  .share-glyph { width: 17px; height: 17px; vertical-align: -3px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
  .banner { border-radius: 10px; padding: 10px 14px; font-size: 13.5px; margin: 10px 0; }
  .banner.warn { background: var(--warn-bg); color: var(--warn); border: 1px solid var(--warn-line); }
  .banner.warn a { color: inherit; font-weight: 600; }
  .banner.err { background: var(--err-bg); color: var(--err); border: 1px solid var(--err-line); }
  .banner.tip { background: var(--card-2); color: var(--mut); border: 1px solid var(--line); }
  .banner.tip a { color: var(--accent); font-weight: 600; }
  .banner.tip .x {
    float: right; margin: -2px -4px 0 8px; width: 28px; height: 28px; line-height: 1;
    border: 0; border-radius: 8px; background: none; color: var(--mut); font: inherit;
    font-size: 16px; cursor: pointer;
  }
  .banner.tip .x:hover { color: var(--ink); }
  .banner.tip .x:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
  /* Success banners: issuing an invite and landing a reset both need a "that
     worked" that isn't the warning yellow. Mixed from --green so it tracks
     both themes rather than hardcoding a pair of literals. */
  .banner.ok {
    background: color-mix(in srgb, var(--green) 18%, var(--card));
    color: var(--green-dk);
    border: 1px solid color-mix(in srgb, var(--green-dk) 40%, transparent);
  }
  .banner.ok b, .banner.ok code { color: var(--ink); }
  /* The saved banner carries the report door (spend-surface #4, 2026-09-07):
     the line on the left, one small button on the right, in the banner's own
     green so it reads as part of the "that worked" and not as a second ask. */
  .banner.ok.rd-saved { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
  .rd-saved .btn {
    flex: none; min-height: 36px; padding: 6px 12px; font-size: 13px;
    background: var(--card); color: var(--green-dk);
    border: 1px solid color-mix(in srgb, var(--green-dk) 40%, transparent);
  }

  /* ── Front door: remembered shooter + invite ticket (ratified 2026-08-19) ── */
  /* The device remembers WHO, never the passcode — one field on return. */
  .idchip {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    background: var(--card-2); border: 1px solid var(--line);
    border-radius: 10px; padding: 10px 12px; margin: 12px 0 4px;
  }
  /* The name block takes the room; without flex-basis it collapses to its
     narrowest wrap and the member line breaks over four lines on a phone. */
  .idchip > div { flex: 1 1 auto; min-width: 0; }
  .idchip b { display: block; font-size: 15px; }
  .idchip span { display: block; font-size: 12.5px; color: var(--mut); }
  /* .btn-link is width:100% for the stacked links under the form; inside the
     chip it sits beside the name, so it has to size to its own text. */
  .idchip .btn-link { margin: 0; flex: none; width: auto; align-self: center; padding: 8px 4px; }
  /* The ticket the shooter arrived with — states what the link opens before
     they type anything. .bad is a dead link caught before the passcode. */
  .ticketchip {
    display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
    background: var(--accent-soft); color: var(--accent-dk);
    border: 1px solid transparent; border-radius: 10px;
    padding: 8px 12px; margin: 12px 0 4px; font-size: 13px; font-weight: 550;
  }
  .ticketchip code { font-size: 13px; letter-spacing: .5px; color: inherit; }
  .ticketchip.bad { background: var(--err-bg); color: var(--err); border-color: var(--err-line); }
  .fldnote { margin: 4px 0 0; font-size: 12px; color: var(--mut); }

  /* ── Invites card (owner only, on the Admin tab since 2026-08-20) ────── */
  #invites-card h2 .dv-pill, #assess-card h2 .dv-pill { vertical-align: 3px; margin-left: 6px; }
  #inv-reset-row { border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px; margin: 10px 0; background: var(--card-2); }
  #inv-email-row { margin: 12px 0 0; }
  #inv-list { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
  .inv-row {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; background: var(--card-2);
  }
  .inv-row code { font-size: 13.5px; letter-spacing: .5px; font-weight: 600; }
  .inv-row .inv-meta { flex: 1 1 160px; font-size: 12.5px; color: var(--mut); }
  .inv-state {
    flex: none; font-size: 11px; font-weight: 650; border-radius: 99px;
    padding: 2px 9px; border: 1px solid var(--line); background: var(--card); color: var(--mut);
  }
  .inv-state.unused { color: var(--accent-dk); background: var(--accent-soft); border-color: transparent; }
  .inv-state.used { color: var(--green-dk); border-color: var(--green-dk); background: transparent; }
  .inv-row.spent code { color: var(--mut); }
  .inv-row.revoked code { text-decoration: line-through; color: var(--mut); }
  /* Expired shares revoked's struck-through look: both mean "this code no
     longer opens the gate", and the meta line carries which one it was. The
     unstyled default would have rendered it as live. */
  .inv-state.expired, .inv-state.revoked { color: var(--mut); border-color: var(--line); background: transparent; }
  .inv-btn {
    flex: none; font-size: 12px; padding: 4px 10px; border-radius: 8px;
    border: 1px solid var(--line); background: var(--card); color: var(--mut); cursor: pointer;
  }
  .inv-btn:hover { border-color: var(--accent); color: var(--accent); }

  /* ── Photo drop ── */
  .photo-drop {
    /* a <button>, so the picker opens from the keyboard too — reset button chrome */
    display: block; width: 100%; font: inherit;
    border: 1.5px dashed var(--line); border-radius: 10px; padding: 18px 14px; text-align: center;
    color: var(--mut); cursor: pointer; background: var(--card-2);
    transition: border-color .12s, background .12s, color .12s;
  }
  .photo-drop:hover { border-color: var(--accent); color: var(--ink); background: var(--accent-soft); }
  .photo-drop img { max-width: 100%; max-height: 220px; border-radius: 8px; display: block; margin: 0 auto 8px; }

  .hidden { display: none !important; }
  @keyframes sp { to { transform: rotate(360deg); } }

  /* ── Build-progress card ── */
  .progress { margin-top: 14px; border: 1px solid var(--line); border-radius: 10px; padding: 14px 16px; background: var(--card-2); }
  .prog-title { margin: 0; font-weight: 650; letter-spacing: -.01em; }
  .prog-sub { margin: 2px 0 12px; color: var(--mut); font-size: 13px; }
  .prog-bar { height: 4px; border-radius: 2px; background: var(--line-2); overflow: hidden; }
  .prog-fill { height: 100%; width: 0; border-radius: 2px; background: var(--accent); transition: width .4s linear; }
  ol.prog-steps { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 9px; font-size: 13.5px; }
  .prog-steps li { display: flex; align-items: center; gap: 9px; color: var(--mut); }
  .prog-steps li::before { flex: none; width: 13px; height: 13px; text-align: center; line-height: 13px; }
  .prog-steps li.pending::before { content: ''; border: 2px solid var(--line); border-radius: 50%; }
  .prog-steps li.active { color: var(--ink); font-weight: 600; }
  .prog-steps li.active::before { content: ''; border: 2px solid var(--accent); border-top-color: transparent; border-radius: 50%; animation: sp .8s linear infinite; }
  .prog-steps li.done::before { content: '✓'; color: var(--green-dk); font-weight: 700; }
  @media (prefers-reduced-motion: reduce) {
    .prog-steps li.active::before { animation: none; border-top-color: var(--accent); }
    .prog-fill { transition: none; }
  }

  /* ── Plan output ── */
  details.plan-sec { border-top: 1px solid var(--line-2); }
  details.plan-sec summary { cursor: pointer; list-style: none; display: flex; align-items: center; gap: 8px; border-radius: 8px; }
  details.plan-sec summary:hover h3 { color: var(--accent); }
  details.plan-sec summary::-webkit-details-marker { display: none; }
  details.plan-sec summary::before { content: '▸'; color: var(--mut); font-size: 12px; transition: transform .15s; }
  details.plan-sec[open] summary::before { transform: rotate(90deg); }
  details.plan-sec summary h3 { margin: 11px 0; transition: color .12s; }
  details.plan-sec > .sec-body { padding-bottom: 12px; }
  table.eng { width: 100%; border-collapse: collapse; font-size: 13.5px; font-variant-numeric: tabular-nums; }
  table.eng th, table.eng td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--line-2); vertical-align: top; }
  table.eng th { color: var(--mut); font-weight: 600; font-size: 11px; text-transform: uppercase; letter-spacing: .05em; }
  table.eng tr:last-child td { border-bottom: 0; }
  ul.rem { margin: 6px 0 0; padding-left: 20px; }
  ul.rem li { margin-bottom: 4px; }

  /* ── History: "the log" (ratified 2026-08-21, artifact ea8f1d88…) ───────
     Sticky head + date groups + a row that leads with the outcome. The head
     is full-bleed inside the card the way .stage-bar is: the negative margins
     are exactly the card's own padding, so it can pin flush under the top bar
     without the card's background showing through behind it. */
  .log-head {
    position: sticky; top: var(--topbar-h, 65px); z-index: 5;
    margin: calc(-1 * clamp(16px, 3vw, 24px)) calc(-1 * clamp(16px, 3vw, 24px)) 0;
    padding: 12px clamp(16px, 3vw, 24px) 10px;
    background: var(--card); border-bottom: 1px solid var(--line);
    border-radius: 14px 14px 0 0;
  }
  .log-title { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-width: 0; }
  .log-title h2 { margin: 0; min-width: 0; }
  /* Section headings on the Range day and Reports tabs match Home's
     (owner-directed 2026-09-07): the titled-card header of #132's option B —
     15px/700, edge to edge, a hairline under it — instead of the 17px/650
     card title. Same face on every heading inside those two tabs: the
     Range day card's title, the Reports card's sticky title, the aisle group
     labels (which drop the mono-caps — reserved for commands, the §7 rule)
     and the Logged days summary. */
  #rangeday-card > h2 {
    margin: calc(-1 * clamp(16px, 3vw, 24px)) calc(-1 * clamp(16px, 3vw, 24px)) 12px;
    padding: 12px 14px 10px; border-bottom: 1px solid var(--line-2);
    font-size: 15px; font-weight: 700; letter-spacing: -.01em; line-height: 1.2; text-wrap: auto;
  }
  /* The sticky head keeps the card's own side padding: the axis and the chips
     bleed to the card edge by exactly that amount (#history-axis, .chips),
     so a narrower head sends them past the card — seen on the desktop
     window after the first cut of this rule (2026-09-07). */
  #history-panel .log-head { border-bottom-color: var(--line-2); }
  #history-panel .log-title h2 { font-size: 15px; font-weight: 700; letter-spacing: -.01em; line-height: 1.2; }
  #rangeday-card .rd-grp {
    margin: 18px 0 8px; font-size: 15px; font-weight: 700; letter-spacing: -.01em;
    text-transform: none; color: var(--ink); line-height: 1.2;
  }
  #rangeday-card .rd-grp small { color: var(--mut); font-size: 13px; }
  #rangeday-card details.plan-sec summary h3 { font-size: 15px; font-weight: 700; letter-spacing: -.01em; }
  /* The topbar pair's shape (44px, hairline, muted → accent on hover) applied
     to an in-card control. One icon-button look in the app, not two: the
     merge comment on #btn-howto/#btn-theme says the next one added inherits
     the set rather than inventing a third, and that reasoning does not stop
     at the header. Not a circle here — a control sitting on a card's title
     row alongside squared chips reads as furniture when it is round. */
  .icon-btn {
    flex: none; width: 44px; height: 44px; margin: -5px 0; padding: 0;
    display: grid; place-items: center;
    border: 1px solid var(--line); border-radius: 12px;
    background: var(--card-2); color: var(--mut); cursor: pointer;
  }
  .icon-btn:hover { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
  .icon-btn svg { width: 19px; height: 19px; }
  .log-title .count { color: var(--mut); font-size: 13px; font-weight: 500; margin-left: 7px; font-variant-numeric: tabular-nums; }
  .log-search { margin-top: 10px; }
  .log-search input { width: 100%; }
  .log-legend { margin: 10px 2px 0; font-size: 11.5px; color: var(--mut); }

  /* Filter chips. Same pressed treatment as the porch's side toggle — one
     idiom for "pick one of these", not a second one invented here. */
  /* Bleeds to the card edge so a chip that runs off the end reads as "scroll
     me" rather than as a clipped control stopping short of a margin. */
  .chips {
    display: flex; gap: 6px; margin: 10px calc(-1 * clamp(16px, 3vw, 24px)) 0;
    padding: 0 clamp(16px, 3vw, 24px) 2px;
    overflow-x: auto; scrollbar-width: none;
  }
  .chips::-webkit-scrollbar { display: none; }
  .chips button {
    flex: 0 0 auto; font: inherit; font-size: 12.5px; font-weight: 600; white-space: nowrap;
    min-height: 34px; padding: 0 12px; border-radius: 999px; cursor: pointer;
    border: 1px solid var(--line); background: var(--card-2); color: var(--mut);
  }
  .chips button b { font-weight: 600; opacity: .65; margin-left: 4px; font-variant-numeric: tabular-nums; }
  .chips button[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: var(--on-fill); }
  .chips button[aria-pressed="true"] b { opacity: .8; }

  /* Group heads pin UNDER the log head, whose height changes when the search
     field opens — so the offset is measured into --loghead-h by the page
     rather than hardcoded, the way core.js measures --topbar-h. */
  .hist-group > h3 {
    position: sticky; top: calc(var(--topbar-h, 65px) + var(--loghead-h, 96px)); z-index: 4; margin: 0;
    padding: 14px 2px 6px; background: var(--card);
    font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--mut);
  }
  /* The list runs full-bleed inside the card — same negative margins as
     .log-head and .stage-bar — and pays 10px back as row padding. Net +12px
     of text column, which is the difference between the meta line fitting on
     one line and wrapping on every row (21px each, measured 2026-08-21). */
  #history-list { margin: 0 calc(-1 * clamp(16px, 3vw, 24px)); padding: 0 10px; }
  .hist-group > h3 { padding-left: 6px; padding-right: 6px; }
  .hist-row { display: flex; gap: 0; margin-bottom: 8px; align-items: stretch; }
  .hist-item {
    position: relative; overflow: hidden;
    flex: 1; display: grid; grid-template-columns: 46px 1fr; gap: 12px; align-items: start;
    text-align: left; background: var(--card); color: var(--ink);
    border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px 13px;
    cursor: pointer; font: inherit;
    transition: border-color .12s, background .12s;
  }
  .hist-item:hover { border-color: var(--accent); background: var(--card-2); }
  /* The grade block: the plan's predicted class + percent, the pair the
     verdict pane leads with. No official HHF prints "—" — never a guess. */
  .hist-grade { display: grid; justify-items: center; gap: 1px; padding-top: 1px; }
  .hist-grade .cls { font-size: 22px; font-weight: 750; line-height: 1; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
  .hist-grade .pct { font-size: 11.5px; color: var(--mut); font-variant-numeric: tabular-nums; line-height: 1.2; }
  .hist-grade.none .cls { color: var(--mut); font-weight: 600; }
  .hist-title { display: block; font-size: 15px; line-height: 1.25; letter-spacing: -.005em; }
  .hist-title b { font-weight: 700; font-variant-numeric: tabular-nums; }
  .hist-title span { color: var(--mut); }
  /* Provenance appears here ONLY as a caveat: "library" on every row is a
     build detail nobody scans for, the verdict's meta chips still carry both
     sources one tap away, and dropping it is what keeps this line from
     wrapping — 21px a row, measured. */
  .hist-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; font-size: 12px; line-height: 1.35; color: var(--mut); margin-top: 2px; }
  .hist-meta .dot { opacity: .5; }
  .hist-meta .flag { color: var(--warn); font-weight: 600; }
  /* The rail: the verdict pane's class-band instrument run along the card's
     bottom edge, so it costs 5px of row height instead of 23. Band widths are
     the real class percentages, same as .rail above. */
  /* 1px gaps, so six SEGMENTS read as a scale. Butted together at 5px with no
     labels the ramp reads as a part-filled progress bar — "67% full" instead
     of "a B, here on the D→GM rail" — which is the one thing this mark must
     not say (caught in verification, 2026-08-21). The verdict pane's own rail
     can butt them because it is 30px tall with the class letters inside. */
  .hist-rail { position: absolute; left: 0; right: 0; bottom: 0; display: flex; gap: 1px; height: 5px; overflow: hidden; }
  .hist-rail .b1 { width: 40%; background: var(--band-1); }
  .hist-rail .b2 { width: 20%; background: var(--band-2); }
  .hist-rail .b3 { width: 15%; background: var(--band-3); }
  .hist-rail .b4 { width: 10%; background: var(--band-4); }
  .hist-rail .b5 { width: 10%; background: var(--band-5); }
  .hist-rail .b6 { width: 5%;  background: var(--band-6); }
  .hist-rail i { position: absolute; top: 0; bottom: 0; width: 3px; background: var(--accent); transform: translateX(-1.5px); }
  /* A tick for a run that actually happened reads harder than one for an
     estimate (#63): the predicted mark is thinner and softer, so scanning the
     column tells you which rows are facts before you read the tilde. */
  .hist-rail:not(.real) i { width: 2px; opacity: .6; transform: translateX(-1px); }
  .hist-status { font-size: 12.5px; line-height: 1.3; display: flex; align-items: center; gap: 6px; margin-top: 5px; }
  .hist-status svg { width: 13px; height: 13px; flex: 0 0 auto; }
  .hist-status.todo { color: var(--accent); font-weight: 650; }
  .hist-status.ok { color: var(--green-dk); }
  .hist-status.over { color: var(--warn); }
  .hist-status.read { color: var(--mut); }
  /* Delete left the row for a 44px overflow (the Web Interface Guidelines
     minimum the top bar and stage bar already keep; the old button was 43px
     of permanent destructive furniture). A coach-built row has no menu at
     all — the existing ownership rule, made visible. */
  .hist-more {
    width: 44px; flex: 0 0 44px; border: 0; background: none; color: var(--mut);
    border-radius: 10px; cursor: pointer; font-size: 17px; line-height: 1;
    transition: background .12s, color .12s;
  }
  .hist-more:hover { background: var(--card-2); color: var(--ink); }
  .hist-more[disabled] { opacity: 0; pointer-events: none; }
  .hist-menu {
    position: absolute; z-index: 30; min-width: 172px; background: var(--card);
    border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--shadow-md); padding: 5px;
  }
  .hist-menu button {
    display: block; width: 100%; text-align: left; font: inherit; font-size: 14px;
    min-height: 44px; padding: 0 11px; border: 0; border-radius: 8px;
    background: none; color: var(--ink); cursor: pointer;
  }
  .hist-menu button:hover { background: var(--card-2); }
  .hist-menu button.danger { color: var(--err); }
  .hist-none { padding: 26px 4px; text-align: center; font-size: 13.5px; color: var(--mut); }
  /* Archive over delete (owner-ratified 2026-09-07) — the range-day log's
     .rdlog-undo / .rdlog-archived / .rdlog-arch, on History's row shape. */
  .hist-undo { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; margin: 0 0 10px; }
  .hist-undo .btn-link { display: inline; width: auto; margin: 0; padding: 4px 6px; font-weight: 600; color: inherit; text-decoration: underline; }
  /* 12.5px and 8px of padding: at 320 a 70px word beside the grade block
     wrapped the title and grew the foot row 83 → 99px; this holds it. */
  .hist-restore { width: auto; flex: 0 0 auto; padding: 0 8px; border: 1px solid var(--line); background: var(--card); font-size: 12.5px; font-weight: 600; }
  .hist-restore:hover { border-color: var(--accent); background: var(--card-2); }
  .hist-archived { margin-top: 12px; }
  /* The Reports segment (2026-09-07) draws Home's report rows on this tab's
     own card, so the rows' container drops its border and fill — the card is
     the border. Card-in-card was the 09-07 shell review's width finding. */
  #history-panel .rep-rows { border: 0; border-radius: 0; background: none; }
  #history-panel .rep-rows .home-row { padding-left: 4px; padding-right: 4px; }
  #history-panel .rep-archived { margin-top: 12px; border-top: 0; } /* .home-archived styles the summary */
  .hist-archived > summary { cursor: pointer; color: var(--mut); font-size: 13.5px; font-weight: 600; padding: 8px 4px; list-style: none; }
  .hist-archived > summary::-webkit-details-marker { display: none; }
  .hist-archived > summary::before { content: '▸ '; }
  .hist-archived[open] > summary::before { content: '▾ '; }

  /* ── Logged range days ──────────────────────────────────────────────────
     These ARE the pre-2026-08-21 history row, kept verbatim under their own
     names. Range day borrowed .hist-row / .hist-item / .hist-del when both
     lists were the same flat shape; the History round turned those into a
     grade column, a rail and an overflow, which cropped this list's text into
     a 46px gutter. Two lists that no longer look alike do not share classes. */
  .rdlog-row { display: flex; gap: 8px; margin-bottom: 8px; align-items: stretch; }
  /* The card is a DIV wrapping a header button and the details, not one big
     <button> with the details inside it (2026-08-21). A day's details now
     carry their own controls — the report button, and a target photo — and
     interactive content nested inside a button is invalid and unclickable:
     the button swallows the tap. The border, padding and hover moved out to
     the wrapper so the row looks exactly as it did. */
  .rdlog-item {
    display: block; flex: 1; background: var(--card); color: var(--ink);
    border: 1px solid var(--line); border-radius: 10px; overflow: hidden;
    transition: border-color .12s, background .12s;
  }
  .rdlog-item:hover { border-color: var(--accent); background: var(--card-2); }
  .rdlog-head {
    display: block; width: 100%; text-align: left; background: none; color: inherit;
    border: 0; padding: 10px 14px; cursor: pointer; font: inherit;
  }
  .rdlog-head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 10px; }
  .rd-details { padding: 0 14px 12px; }
  .rd-report-btn { margin-top: 10px; }
  .rdlog-del {
    background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 0 13px;
    cursor: pointer; font-size: 15px; color: var(--mut); transition: border-color .12s, color .12s;
  }
  .rdlog-del:hover { border-color: var(--err); color: var(--err); }
  /* Archive / Restore (owner-ratified 2026-09-07) are WORDS in the gutter,
     not a glyph, because a one-tap reversible act needs its name where the
     old 🗑 needed a confirm step. Not red: nothing here destroys anything.
     13px keeps the row at one line beside "Sep 6 · 2/2 bars" at 320. */
  .rdlog-arch { font-size: 13px; font-weight: 600; padding: 0 10px; }
  .rdlog-arch:hover { border-color: var(--accent); color: var(--ink); }
  .rdlog-undo { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; margin-bottom: 10px; }
  .rdlog-undo .btn-link { display: inline; width: auto; margin: 0; padding: 4px 6px; font-weight: 600; color: inherit; text-decoration: underline; }
  .rdlog-archived { margin-top: 12px; }
  .rdlog-archived > summary { cursor: pointer; color: var(--mut); font-size: 13.5px; font-weight: 600; padding: 8px 0; list-style: none; }
  .rdlog-archived > summary::-webkit-details-marker { display: none; }
  .rdlog-archived > summary::before { content: '▸ '; }
  .rdlog-archived[open] > summary::before { content: '▾ '; }
  .rdlog-archived > .rdlog-row:first-of-type { margin-top: 4px; }

  /* ── Range day: launcher, aisles picker, session, wrap (owner-ratified
     2026-08-20, artifact 311bfc22…). Type runs larger than the rest of the
     app on purpose — this is the one surface read from a bench with ammo
     boxes on it, in the sun, holding a timer. ── */
  .rd-grp { margin: 16px 2px 8px; font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--mut); }
  .rd-grp small { text-transform: none; letter-spacing: 0; font-weight: 500; font-size: 11.5px; }
  .rd-row { border: 1px solid var(--line); border-radius: 12px; background: var(--card); margin-bottom: 8px; }
  .rd-row.sel { border-color: var(--accent); box-shadow: 0 0 0 1px var(--ring); }
  .rd-rowhead { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-start; padding: 10px 12px; cursor: pointer; margin: 0; font-size: inherit; font-weight: inherit; color: inherit; }
  /* The chips are a full-width LAST ROW of the head, not part of the text
     column (2026-09-07, measured when the media chips landed): beside the
     rounds badge and the ⤢ button that column is ~155px at 375, so every
     chip past the first took a line of its own and a drill with a diagram
     and a video ran to four. Under the label the row is ~215px — the old
     "New · Sets …" pair now shares a line too. Still inside the <label>, so
     a chip tap toggles the drill exactly as it always did. */
  .rd-rowhead > .rd-chips { flex: 1 1 100%; margin: -2px 0 0 28px; }
  /* With wrap on, the text column must be told to shrink or it takes its
     content width and pushes the rounds badge onto a line of its own. */
  .rd-rowhead > div { flex: 1 1 0; min-width: 0; }
  .rd-rowhead input[type=checkbox] { width: 18px; height: 18px; margin: 2px 0 0; flex: none; }
  .rd-lbl { font-weight: 650; font-size: 14px; line-height: 1.35; }
  .rd-why { font-size: 12.5px; color: var(--mut); margin-top: 1px; }
  .rd-rds { margin-left: auto; flex: none; font-size: 11.5px; color: var(--mut); font-variant-numeric: tabular-nums; padding-top: 3px; }
  /* See the setup before the drill goes in the day (owner, 2026-08-21).
     ONE button on the row and nothing else: a line of dimension text was
     built first and removed the same day — at 375px it wrapped to two lines
     on most drills and grew every row for something you only want about the
     one drill you are undecided on. So the row is unchanged and the setup is
     one tap away, which keeps "a menu stays a menu" intact instead of
     bending it.

     ACCENT-filled rather than a ghost outline (owner, same day): it is the
     only control on the row carrying this affordance, and a grey square
     beside a grey checkbox reads as decoration. Same treatment as the
     session block's Setup strip, so the two are visibly one feature. 44px is
     the touch minimum the top bar and the stage bar already keep. */
  /* The drill author's layout preview (2026-08-21). */
  .dr-layout { border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; margin: 10px 0 4px; background: var(--card-2); }
  .dr-layout-head { display: flex; align-items: center; gap: 9px; font-weight: 650; font-size: 14px; cursor: pointer; margin: 0 0 9px; }
  .dr-layout-head input { width: 18px; height: 18px; flex: none; }
  .dr-layout-prev { border: 1px solid var(--line); border-radius: 10px; background: var(--card); overflow: hidden; }
  .dr-layout-prev svg { display: block; width: 100%; height: auto; }
  .dr-layout-hint { font-size: 12px; color: var(--mut); margin: 8px 0 0; line-height: 1.45; }
  .rd-row.has-card { position: relative; }
  .rd-row.has-card .rd-rowhead { padding-right: 56px; } /* 44px button + a gutter, or the rounds badge butts against it */
  .rd-rowcard {
    position: absolute; top: 5px; right: 4px; width: 44px; height: 44px;
    display: grid; place-items: center; border-radius: 10px; cursor: pointer;
    background: var(--accent-soft); border: 1px solid var(--accent);
    color: var(--accent-dk); font-size: 17px; line-height: 1;
  }
  :root[data-resolved-theme="dark"] .rd-rowcard { color: var(--accent); }
  .rd-rowcard:hover { background: var(--accent); color: var(--on-fill); border-color: var(--accent); }
  .rd-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
  .rd-chips span { font-size: 11px; border-radius: 99px; padding: 2px 8px; border: 1px solid var(--line); background: var(--card-2); color: var(--mut); font-variant-numeric: tabular-nums; }
  .rd-chips .cnt b { color: var(--green-dk); font-weight: 700; }
  .rd-chips .new { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); font-weight: 650; }
  .rd-chips .from-plan { color: var(--accent-dk); background: var(--accent-soft); border-color: transparent; font-weight: 600; }
  /* Challenge Profile (owner directive 2026-08-20): the chip that says this
     drill plays for one of the shooter's measured profile numbers. Green
     rather than accent so it reads as "a number you can bank", and so it is
     visibly not the plan-provenance chip beside it. */
  .rd-chips .rd-chal { color: var(--green-dk); border-color: color-mix(in srgb, var(--green-dk) 45%, transparent); background: color-mix(in srgb, var(--green) 14%, var(--card)); font-weight: 650; }
  /* A CHECK drill reads a baseline and never banks it (2026-08-20). Muted and
     dashed, deliberately NOT the green "Sets" chip: the two say different
     things about your profile and must not be mistaken for each other at a
     glance. Colour alone never carries it — the words differ too. */
  .rd-chips .rd-check { color: var(--mut); border-style: dashed; font-weight: 600; }
  /* What is behind the ⤢ (owner-ratified "B + C", 2026-09-07, wireframes in
     docs/wireframes/drill-media-2026-09-07/): "Diagram" and "Video · m:ss"
     chips on the row, ink-coloured and iconed so they read as facts about
     the drill rather than as the logged/new state beside them. */
  .rd-chips .rd-media { color: var(--ink); font-weight: 600; display: inline-flex; align-items: center; gap: 4px; }
  .rd-chips .rd-media svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.8; flex: none; }
  /* The filter row under the search box: History's `.chips`, because this
     narrows one view and never picks another (`.segs` is for that). */
  .rd-media-filter { margin-top: 0; margin-bottom: 10px; }
  .rd-media-filter button { display: inline-flex; align-items: center; gap: 5px; }
  .rd-media-filter button svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.8; flex: none; }
  /* The aisle card's diagram and video counts — whole-shelf facts, right of
     the pills. Muted: the count pill is the answer, these say what kind. */
  .rd-cat-media { margin-left: auto; display: inline-flex; align-items: center; gap: 9px; font-size: 11px; font-weight: 600; color: var(--mut); font-variant-numeric: tabular-nums; }
  .rd-cat-media span { display: inline-flex; align-items: center; gap: 3px; }
  .rd-cat-media svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.8; flex: none; }
  /* An aisle with nothing on the active filter dims rather than vanishes. */
  .rd-cat-card.rd-cat-none { opacity: .55; }
  /* Sticky, 2026-08-20: this bar was measured 438px below the fold on the
     category grid and 1,249px down inside an aisle, so the tally saying what
     you had picked was never on screen while you picked. */
  .rd-startbar {
    position: sticky; bottom: 0; z-index: 2;
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    margin-top: 14px; padding: 12px 0 2px; border-top: 1px solid var(--line-2);
    background: var(--card);
  }
  /* Aisles picker (ratified "C with logged pill" 2026-08-19, artifact
     ebeb53aa…): the picker opens as a category grid — each card carries a
     drill count, the picked tally, and the aisle's own logged pill. */
  .rd-cat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  @media (max-width: 400px) { .rd-cat-grid { grid-template-columns: 1fr; } }
  .rd-cat-card {
    font: inherit; text-align: left; color: inherit; cursor: pointer;
    border: 1px solid var(--line); border-radius: 12px; background: var(--card); padding: 12px;
    transition: border-color .12s, background .12s;
  }
  .rd-cat-card:hover { border-color: var(--accent); background: var(--card-2); }
  .rd-cat-card.picked { border-color: var(--accent); box-shadow: 0 0 0 1px var(--ring); }
  .rd-cat-name { font-weight: 650; font-size: 13.5px; }
  .rd-cat-sub { font-size: 11.5px; color: var(--mut); margin-top: 1px; }
  .rd-cat-meta { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 8px; }
  .rd-cat-n {
    font-size: 11px; border-radius: 999px; padding: 2px 8px;
    background: var(--accent-soft); color: var(--accent-dk); font-weight: 700; font-variant-numeric: tabular-nums;
  }
  .rd-search { margin: 0 0 8px; }
  #rd-tally { font-variant-numeric: tabular-nums; }
  .rd-setup { font-size: 14.5px; line-height: 1.6; margin: 10px 0 6px; }
  .rd-bar { font-size: 13px; color: var(--mut); margin: 0 0 4px; }
  .rd-bar b { color: var(--ink); }


  /* ── The launcher. Lanes are the aisle card's own shape at full width, so
     the tab has ONE tappable-thing idiom rather than two. ── */
  .rd-lane { width: 100%; margin-bottom: 8px; }
  .rd-lane-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
  .rd-chev { flex: none; color: var(--mut); font-size: 17px; line-height: 1.2; }
  .rd-resume {
    border: 1px solid var(--accent); border-radius: 12px;
    background: var(--accent-soft); padding: 12px 14px; margin: 0 0 14px;
  }
  .rd-resume-btns { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
  /* Two-step, never a native confirm(): the second tap is the destructive
     one and it is labelled with what it destroys. */
  .rd-danger { color: var(--err); border-color: var(--err); }
  .rd-danger:hover { color: var(--err); border-color: var(--err); }
  .rd-last { border-top: 1px solid var(--line-2); margin-top: 16px; padding-top: 12px; }
  .rd-last-h { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; font-size: 13.5px; }
  .rd-last-h span { flex: none; color: var(--mut); font-weight: 600; font-variant-numeric: tabular-nums; }
  .rd-disclose { margin: 14px 0 0; font-size: 12px; }

  /* ── The session: accordion blocks, one open, under a sticky bar that
     keeps Finish in reach from any scroll position. ── */
  .rd-sessbar {
    position: sticky; top: 0; z-index: 3;
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 10px 0; background: var(--card); border-bottom: 1px solid var(--line-2);
  }
  .rd-prog { height: 3px; margin: 10px 0 12px; border-radius: 2px; background: var(--line-2); overflow: hidden; }
  .rd-prog i { display: block; height: 100%; background: var(--accent); transition: width .25s ease; }
  .rd-blk { border: 1px solid var(--line); border-radius: 12px; background: var(--card); margin-bottom: 8px; }
  .rd-blk.open { border-color: var(--accent); box-shadow: 0 0 0 1px var(--ring); }
  .rd-blk-h {
    display: flex; align-items: flex-start; gap: 11px; width: 100%; min-height: 56px;
    padding: 12px 13px; border: 0; background: none; font: inherit; color: inherit;
    text-align: left; cursor: pointer;
  }
  .rd-blk-h:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 12px; }
  .rd-mark {
    flex: none; width: 22px; height: 22px; margin-top: 1px; border-radius: 50%;
    border: 1.5px solid var(--line); display: grid; place-items: center;
    font-size: 11.5px; font-weight: 700; color: var(--mut);
  }
  .rd-blk.done .rd-mark { border-color: var(--green-dk); color: var(--green-dk); }
  .rd-blk.miss .rd-mark { border-color: var(--warn-line); background: var(--warn-bg); color: var(--warn); }
  .rd-blk-l { min-width: 0; }
  .rd-blk-s { display: block; margin-top: 3px; font-size: 12.5px; color: var(--mut); font-variant-numeric: tabular-nums; }
  .rd-blk-b { padding: 0 13px 14px; }
  .rd-flds { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; }
  @media (max-width: 400px) { .rd-flds { grid-template-columns: 1fr; } }
  /* One prior number under the field it belongs to. A count, not a trend —
     the ≥3-days chart gate is untouched by this. */
  .rd-prev { margin: 5px 0 0; font-size: 11.5px; color: var(--mut); font-variant-numeric: tabular-nums; }
  .rd-togs { display: flex; gap: 8px; margin-top: 14px; }
  .rd-tog {
    flex: 1; min-height: 44px; padding: 10px; border-radius: 9px;
    border: 1px solid var(--line); background: var(--card-2); color: var(--mut);
    font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
  }
  .rd-tog:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
  .rd-tog[aria-pressed="true"] { border-color: var(--green-dk); color: var(--green-dk); background: color-mix(in srgb, var(--green) 14%, var(--card)); }
  .rd-tog.miss[aria-pressed="true"] { border-color: var(--warn-line); color: var(--warn); background: var(--warn-bg); }
  /* ── The training log (backlog #77, ratified 2026-08-22) ──────────────────
     A drill is a card holding a table with a row per string. It replaces the
     accordion, whose open block measured 744px against 183px here — 279px of
     that was prose the shooter had already read, which now sits behind the ⓘ.
     Every card is open at once, so scroll is the only navigation. */
  .rd-drill { border: 1px solid var(--line); border-radius: 14px; background: var(--card); margin: 10px 0; overflow: hidden; }
  .rd-drill.done { border-color: var(--green-dk); }
  .rd-dhead { display: flex; align-items: flex-start; gap: 10px; padding: 12px 13px 10px; }
  .rd-dh-l { min-width: 0; flex: 1; }
  /* The name is the door to "how to run it" (2026-09-07): a text button in
     the name's own face with a › after it, wrapping as text does. */
  .rd-dname {
    display: inline; background: none; border: 0; padding: 0; margin: 0; cursor: pointer; text-align: left;
    font: inherit; font-weight: 600; font-size: 14.5px; line-height: 1.3; color: var(--ink);
  }
  .rd-dname svg { width: 14px; height: 14px; vertical-align: -2px; margin-left: 2px; color: var(--mut); }
  .rd-dname:hover { color: var(--accent); }
  /* The redraw after a tick or a pip used to leave the name underlined (seen
     on showclear.app 2026-09-07): focus-visible after a programmatic redraw.
     A keyboard user gets the ring; a pointer never does. */
  .rd-dname:focus { outline: none; }
  .rd-dname:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
  .rd-dlogged { display: inline-flex; align-items: center; gap: 4px; margin-left: 8px; color: var(--green-dk); font-weight: 600; }
  .rd-dlogged svg { width: 13px; height: 13px; }
  :root[data-resolved-theme="dark"] .rd-dlogged { color: var(--green); }
  .rd-dmenu { margin: -4px 0 0; width: 36px; height: 36px; border-radius: 10px; }
  .rd-dmenu svg { width: 18px; height: 18px; }
  .rd-menu { margin: 0; }
  .rd-sess-r { display: flex; align-items: center; gap: 8px; flex: none; }
  /* + Add run as a row of the card, the workout-app shape: dashed, full
     width, 40px — the tap target the old 13.5px text link never was. */
  .rd-addrun {
    display: flex; align-items: center; justify-content: center; gap: 6px; width: 100%; min-height: 40px;
    border: 1px dashed var(--line); border-radius: 9px; background: none; color: var(--mut);
    font: inherit; font-size: 13.5px; font-weight: 600; cursor: pointer;
  }
  .rd-addrun svg { width: 14px; height: 14px; }
  .rd-addrun:hover { color: var(--ink); border-color: var(--mut); }
  .rd-dsub { font-size: 12px; color: var(--mut); margin-top: 3px; font-variant-numeric: tabular-nums; }
  /* Unreferenced since the ⓘ was retired for the card's name-as-door
     (2026-09-07, pinned by test/controls.test.js) — raised to the 44px floor
     anyway so a reuse inherits it rather than re-inheriting 34. */
  .rd-info {
    flex: none; width: 44px; height: 44px; min-height: 44px; border-radius: 50%;
    border: 1px solid var(--line); background: var(--card-2); color: var(--mut);
    font-family: Georgia, "Times New Roman", serif; font-size: 15px; font-weight: 700;
    font-style: italic; cursor: pointer; display: grid; place-items: center;
  }
  .rd-info:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
  table.rd-runs { width: 100%; border-collapse: collapse; }
  table.rd-runs th {
    font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--mut);
    font-weight: 700; text-align: center; padding: 0 4px 6px;
  }
  table.rd-runs th small { display: block; font-size: 11px; letter-spacing: .04em; text-transform: none; font-weight: 500; }
  /* D's Prev column (2026-09-07): last day's numbers, one per metric, in
     the LCD face — the target to beat sits in the row, not only as a ghost. */
  table.rd-runs .rd-prev {
    width: 46px; padding: 0 2px; text-align: center; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11.5px; color: var(--mut); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 64px;
  }
  table.rd-runs th.rd-prev { font-family: inherit; font-size: 10.5px; letter-spacing: .07em; font-weight: 700; }
  /* The row tints with its tick (D): met green, missed amber. */
  table.rd-runs tr.rd-r.met td { background: color-mix(in srgb, var(--green) 9%, transparent); }
  table.rd-runs tr.rd-r.miss td { background: color-mix(in srgb, var(--warn) 8%, transparent); }
  .rd-cellw { display: flex; align-items: center; gap: 6px; }
  .rd-cell.narrow { width: 52px; flex: none; padding-left: 4px; padding-right: 4px; }
  /* B's pips (2026-09-07): one metric "of N" on the drill, N ≤ 12 — tap a
     pip to set the count; the lit ones are the value. The dry lane's shape. */
  .rd-pipin { display: flex; gap: 3px; flex: 1; min-width: 0; }
  .rd-pip {
    flex: 1 1 0; min-width: 0; height: 30px; padding: 0; border-radius: 5px; cursor: pointer;
    background: var(--line-2); border: 1px solid var(--line);
  }
  .rd-pip.on { background: var(--green-dk); border-color: var(--green-dk); }
  :root[data-resolved-theme="dark"] .rd-pip.on { background: var(--green); border-color: var(--green); }
  .rd-pip:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
  /* At 320 the ten pips beside the value box are 9px each (measured on the
     walk) — under 360 they drop to their own line under the value, 20px+. */
  @media (max-width: 360px) {
    .rd-td-pips .rd-cellw { flex-wrap: wrap; }
    .rd-td-pips .rd-cell.narrow { width: 100%; flex: 1 1 100%; }
    .rd-pipin { flex-basis: 100%; }
  }
  /* The keypad sheet (C). */
  .kp-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 0 16px 10px; }
  .kp-t { font-size: 13px; font-weight: 700; }
  .kp-m { font-size: 10.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--mut); }
  .kp-lcd {
    display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 16px;
    padding: 12px 16px; border-radius: 12px; background: #0b0f14; border: 1px solid var(--line);
  }
  .kp-read { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 40px; line-height: 1; color: #e7ecf1; letter-spacing: .04em; font-variant-numeric: tabular-nums; }
  .kp-ghost { color: #3a4655; }
  .kp-caret { color: #3a4655; animation: kp-blink 1s steps(2) infinite; }
  @keyframes kp-blink { to { opacity: 0; } }
  .kp-side { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; text-align: right; }
  .kp-of { font-size: 10.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: #93a0ad; }
  .kp-lb { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: #93a0ad; }
  .kp-chips { display: flex; gap: 8px; margin: 10px 16px 0; }
  .kp-chips:empty { display: none; }
  .kp-chip {
    flex: 1; min-height: 40px; border-radius: 9px; border: 1px solid var(--line); background: var(--card);
    color: var(--mut); font: inherit; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; cursor: pointer;
  }
  .kp-chip:hover { color: var(--ink); border-color: var(--mut); }
  .kp-keys { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin: 10px 16px 0; }
  .kp-key {
    min-height: 52px; border-radius: 10px; border: 0; background: var(--card-2); color: var(--ink); cursor: pointer;
    font: inherit; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 22px; display: grid; place-items: center;
  }
  .kp-key.alt { background: var(--line-2); }
  .kp-key svg { width: 22px; height: 22px; }
  .kp-key:active { filter: brightness(1.15); }
  .kp-foot { display: flex; gap: 8px; margin: 12px 16px 0; }
  .kp-foot .btn { flex: 1; min-height: 48px; }
  .kp-foot .icon-btn { width: 48px; height: 48px; margin: 0; border-radius: 9px; }
  table.rd-runs th:first-child, table.rd-runs td:first-child { padding-left: 13px; width: 22px; text-align: left; }
  /* The last column holds the 44px tick AND the PR pill under it, so it is the
     pill that sizes it. At 11px "PR 12.34" over "was 12.99" needs ~62px of
     content box (was ~52 at 9.5px), so the column takes 62 and hands 4px back
     off its right padding — a net ~7px at 320px, spent out of the metric cells,
     which is the honest price of a legible pill. */
  table.rd-runs th:last-child, table.rd-runs td:last-child { padding-right: 9px; width: 62px; text-align: right; }
  table.rd-runs td { padding: 3px 4px; vertical-align: middle; }
  table.rd-runs tr.rd-r td { border-top: 1px solid var(--line-2); }
  .rd-rn { font-size: 13px; color: var(--mut); font-variant-numeric: tabular-nums; }
  /* Screen-reader-only header text: the columns are obvious to a sighted
     shooter and the labels would cost width the numbers need. */
  .rd-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .rd-cell {
    width: 100%; font: inherit; font-size: 16px; font-variant-numeric: tabular-nums;
    padding: 9px 6px; border-radius: 9px; border: 1px solid transparent;
    background: var(--card-2); color: var(--ink); text-align: center; -moz-appearance: textfield;
  }
  .rd-cell::-webkit-outer-spin-button, .rd-cell::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
  /* The ghost is last day's number sitting in the cell it would be typed
     into — a target to beat, not a hint under the field. */
  .rd-cell::placeholder { color: var(--mut); opacity: .5; }
  .rd-cell:focus { outline: none; border-color: var(--accent); background: var(--card); box-shadow: 0 0 0 3px var(--ring); }
  /* 44x44, not 40 (#66's floor finally applied here, 2026-09-09): this is the
     control a shooter taps most, with a gloved hand, and it is the one on this
     row that had no excuse — the last column already reserves 52px, so the box
     grew inside its own cell and no other cell moved. */
  .rd-tick {
    width: 44px; height: 44px; border-radius: 11px; border: 1px solid var(--line);
    background: var(--card-2); color: var(--mut); font-size: 16px; font-weight: 700;
    cursor: pointer; display: inline-grid; place-items: center;
    /* The ✓ stays visible unanswered, at a third opacity: an empty box beside
       three number cells reads as a fourth input, not as something to tap
       (found in the browser at 375px). Answered states turn it solid. */
    opacity: .34;
  }
  .rd-tick[aria-pressed="true"] { opacity: 1; }
  .rd-tick:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
  /* Met is FILLED green, missed amber with a ✕ (D, 2026-09-07): the state
     reads from across the bay, and the row tints to match. */
  .rd-tick svg { width: 18px; height: 18px; }
  .rd-tick.met { background: var(--green-dk); border-color: var(--green-dk); color: var(--on-fill); }
  .rd-tick.miss { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); }
  /* A personal record, on the row, while the shooter is still on the bay —
     it used to arrive on the wrap screen minutes later. */
  /* The PR pill says what it beat (phase 4, 2026-09-09), which is two facts and
     therefore two lines — a shooter standing on a bay is the person least able
     to remember their own old number, so "PR" alone made them. Each line is
     nowrap on purpose: as one run of text it wrapped to 40x61px inside the
     tick cell at 320. Split, it is one line per fact.
     11px, not 9.5 (2026-09-09): this was the last text in the sheet under the
     legibility floor the rest of the app was raised to, and it was left behind
     because it is what sets the width of the run table's last column. So the
     COLUMN gave way, not the type — the same call `.scorebox` already made for
     "HIT FACTOR" at 11px. The tracking comes down from .07em to .03em, which
     buys back about a third of the growth, and the column below widens by the
     rest. */
  .rd-pr {
    display: block; margin-top: 3px; font-size: 11px; font-weight: 800; letter-spacing: .03em;
    color: var(--on-fill); background: var(--accent); border-radius: 5px; padding: 1px 3px;
    font-variant-numeric: tabular-nums;
  }
  .rd-pr b, .rd-pr i { display: block; white-space: nowrap; font-style: normal; }
  .rd-pr i { font-weight: 600; opacity: .82; letter-spacing: .01em; }
  .rd-dfoot { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 6px 13px 11px; }
  .rd-dextra { padding: 0 13px 13px; border-top: 1px solid var(--line-2); margin-top: 2px; padding-top: 11px; }
  .rd-dextra .fld { margin-top: 10px; }

  /* Target photos (owner-directed 2026-08-21). The empty state is a full-width
     button because it is a THING YOU DO at the bay with one thumb, not a
     setting; once there is a photo the row collapses to a thumbnail plus two
     small actions, so the picture never pushes Bar met/missed under the fold
     (the same measurement that kept the setup diagram behind a tap). */
  .rd-photo-w { margin-top: 12px; }
  .rd-photo-b {
    display: flex; align-items: center; justify-content: center; gap: 8px;
    width: 100%; min-height: 44px; padding: 10px; border-radius: 9px;
    border: 1px dashed var(--line); background: var(--card-2); color: var(--mut);
    font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
  }
  .rd-photo-b:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
  .rd-photo:has(.rd-photo-t) { display: flex; align-items: center; gap: 10px; }
  .rd-photo-t {
    flex: none; display: block; width: 68px; height: 68px; border-radius: 9px;
    overflow: hidden; border: 1px solid var(--line); background: var(--card-2);
  }
  /* An opened day in the log shows its targets at a readable size — the
     picture IS what an expanded day is for. Capped in height so five drills
     with photos stay a day you can scroll, not a gallery. */
  .rd-shot {
    display: block; margin-top: 8px; max-width: 100%; max-height: 260px;
    border-radius: 9px; border: 1px solid var(--line); background: var(--card-2);
  }
  .rd-photo-t img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .rd-photo-t:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
  .rd-photo-a { display: flex; gap: 8px; flex-wrap: wrap; }
  /* The shared .btn is 42px (backlog #66) and these two shrink further at a
     13px font, so they set the 44px floor on themselves rather than moving it
     under every button in the app — #65's precedent. At 320px they wrap to
     two rows beside the thumbnail, which is the honest cost of a label that
     says "photo" (there is a second Remove in this block). */
  .rd-photo-a .btn { min-height: 44px; padding: 8px 12px; font-size: 13px; }
  /* What code measured off the photo, under the photo it measured. Muted and
     small: it is a fact about the picture, not a second score — the number the
     bar is judged on is the one in the field above. */
  .rd-read { margin-top: 7px; font-size: 12px; color: var(--mut); line-height: 1.5; font-variant-numeric: tabular-nums; }
  .rd-read-cap { font-variant-numeric: normal; }
  /* THE READ WALL (2026-09-17): the four free target reads, said once under
     the day rather than on every photo row. Dashed, because it is an offer
     and not a warning — the day above it still works. */
  .rd-read-wall { margin: 12px 0 0; padding: 14px; border: 1px dashed var(--line); border-radius: 12px; display: flex; flex-direction: column; gap: 11px; }
  .rd-wall-t { margin: 0; font-size: 13.5px; line-height: 1.5; }
  .rd-wall-o { align-self: center; }
  .rd-read-clash {
    margin-top: 6px; padding: 8px 10px; border-radius: 8px;
    border: 1px solid var(--warn-line); background: var(--warn-bg); color: var(--warn);
  }

  /* ── The range-day report sheet (owner-directed 2026-08-21) ── */
  .rdr-sum { font-weight: 650; font-size: 14px; margin-bottom: 4px; font-variant-numeric: tabular-nums; }
  .rdr-head { font-size: 15px; line-height: 1.55; margin: 0 0 14px; }
  /* Who stands behind the read (#251): under the headline, quieter than it —
     provenance, not prose. */
  .rdr-credit { font-size: 12px; line-height: 1.5; margin: -8px 0 14px; }
  .rdr-drill { border-top: 1px solid var(--line-2); padding-top: 11px; margin-top: 11px; }
  .rdr-drill h4 { margin: 0 0 4px; font-size: 14px; font-weight: 700; }
  .rdr-nums { font-size: 13px; font-variant-numeric: tabular-nums; }
  .rdr-t { margin-top: 4px; font-size: 12.5px; color: var(--mut); font-variant-numeric: tabular-nums; }
  .rdr-note { margin-top: 6px; font-size: 13.5px; line-height: 1.55; }
  .rdr-clash {
    margin-top: 7px; padding: 9px 11px; border-radius: 8px; font-size: 12.5px; line-height: 1.5;
    border: 1px solid var(--warn-line); background: var(--warn-bg); color: var(--warn);
  }
  .rdr-pri { border: 1px solid var(--accent); border-radius: 12px; background: var(--accent-soft); padding: 12px 13px; margin-bottom: 10px; }
  .rdr-pri h4 { margin: 0 0 5px; font-size: 14px; font-weight: 700; }
  .rdr-pri p { margin: 0 0 6px; font-size: 13.5px; line-height: 1.55; }
  .rdr-pri .rdr-next { margin: 0; font-size: 13px; }
  .rdr-acts { display: flex; flex-wrap: wrap; gap: 8px; margin: 16px 0 6px; }
  .rdr-acts .btn { flex: 1; min-width: 140px; min-height: 44px; }
  .rdr-gaps { font-size: 12.5px; color: var(--mut); line-height: 1.6; }

  /* ── THE RANGE-DAY REPORT'S FOLDS (2026-09-09, the report-restructure round).
     The report was ~1,235 words in one scroll; it opens at about two hundred
     now, with everything else one tap behind a <details>. Written first as an
     inline <style> beside the dialog by an agent who could not open this file,
     and moved here the next time it was open — tokens only, and both palettes
     are already resolved by :root, so none of it needs a second copy under the
     theme blocks.

     `summary` keeps `list-style: none` plus the ::-webkit-details-marker
     reset, the idiom .scorefold and details.plan-sec already use, and grows
     its own chevron — and it keeps 44px of height, the Web Interface
     Guidelines touch minimum the top bar and the stage bar keep. ── */
  .rdr-verdict { margin-bottom: 8px; }
  /* The headline number. It was set at 14px when it was one line of a summary
     among several; it is the answer to "how did the day go" now, so it is
     sized like one. */
  .rdr-verdict .rdr-sum { font-size: 19px; font-weight: 750; letter-spacing: -.01em; margin-bottom: 2px; }
  .rdr-sub { font-size: 12px; line-height: 1.5; font-variant-numeric: tabular-nums; }
  /* A personal best is the one thing in the head matter that is not about
     today alone, so it is marked rather than set as another muted line. */
  .rdr-best {
    margin: 10px 0 0; font-size: 13.5px; font-weight: 700; line-height: 1.45;
    color: var(--green-dk); font-variant-numeric: tabular-nums;
  }
  .rdr-drill, .rdr-about, .rdr-why { --rdr-chev: '\203A'; }
  .rdr-drill > summary, .rdr-about > summary, .rdr-why > summary {
    cursor: pointer; list-style: none; display: flex; align-items: center; gap: 9px;
    border-radius: 8px;
  }
  .rdr-drill > summary::-webkit-details-marker,
  .rdr-about > summary::-webkit-details-marker,
  .rdr-why > summary::-webkit-details-marker { display: none; }
  .rdr-drill > summary::after, .rdr-about > summary::after, .rdr-why > summary::after {
    content: var(--rdr-chev); margin-left: auto; color: var(--mut); font-size: 19px;
    transform: rotate(90deg); transition: transform .12s;
  }
  .rdr-drill[open] > summary::after, .rdr-about[open] > summary::after,
  .rdr-why[open] > summary::after { transform: rotate(-90deg); }

  .rdr-drill { border-top: 1px solid var(--line-2); }
  .rdr-drill > summary { min-height: 44px; padding: 6px 2px; }
  .rdr-sumtext { min-width: 0; }
  .rdr-dname { display: block; font-size: 14px; font-weight: 700; line-height: 1.3; }
  .rdr-sumline {
    display: block; font-size: 12.5px; color: var(--mut); line-height: 1.4;
    font-variant-numeric: tabular-nums;
  }
  /* The mark is paired with the words "bar met"/"bar missed" on the same line,
     so colour is never the only thing carrying the meaning. */
  .rdr-mark {
    flex: none; width: 20px; text-align: center; font-weight: 700; color: var(--mut);
  }
  .rdr-mark.met { color: var(--green-dk); }
  .rdr-drillbody { padding: 2px 2px 12px 29px; }

  .rdr-why { margin: 0; }
  .rdr-why > summary { min-height: 40px; font-size: 12.5px; font-weight: 600; color: var(--accent-dk); }
  .rdr-why > p { margin: 0 0 4px; font-size: 13px; line-height: 1.55; }

  .rdr-about { margin-top: 16px; border-top: 1px solid var(--line-2); }
  .rdr-about > summary { min-height: 44px; font-size: 13px; font-weight: 600; color: var(--mut); }
  .rdr-aboutbody { padding-bottom: 12px; }
  .rdr-aboutbody h4 { margin: 12px 0 4px; font-size: 12px; font-weight: 700; }
  .rd-blk-nav { display: flex; gap: 8px; margin-top: 14px; }
  .rd-blk-nav .btn.primary { flex: 1; }
  .rd-add { width: 100%; margin-top: 4px; }
  .rd-sess-foot { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-top: 12px; }

  /* ── The wrap: the day's account, and the beaten baselines as cards. ── */
  .rd-wrap-h { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; }
  .rd-wrap-h h3 { margin: 0; }
  .rd-wrap-h .btn, .rd-dateline .btn { padding: 8px 12px; font-size: 13px; white-space: nowrap; }
  .rd-sum { display: flex; gap: 8px; }
  .rd-sum-i { flex: 1; padding: 10px 8px; border: 1px solid var(--line); border-radius: 10px; background: var(--card-2); text-align: center; }
  .rd-sum-i b { display: block; font-size: 22px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
  .rd-sum-i span { font-size: 11.5px; color: var(--mut); }
  /* "Didn't get to" (owner-directed 2026-08-21). Deliberately the QUIETEST
     block on the wrap screen — a hairline card, no accent fill, no colour on
     the pressed chip beyond the app's own accent outline. A tally of what you
     failed to do, painted like a warning at the end of a good range day, is
     the version of this feature nobody would use twice. The chips wrap rather
     than sitting in a 4-column grid: at 320px four 44px targets in one row
     leaves ~66px each, which truncates every label. */
  /* The disclosure keeps the wrap screen's shape: closed, it is one line, so
     Save stays where it was and the day does not end on a list of what you
     missed. It borrows .plan-sec wholesale rather than inventing a second
     disclosure idiom. */
  details.rd-skipsec { border-top: 1px solid var(--line-2); margin: 4px 0 6px; }
  details.rd-skipsec summary h3 { font-size: 15px; color: var(--mut); font-weight: 600; }
  details.rd-skipsec[open] summary h3 { color: var(--ink); }
  .rd-skips { border: 1px solid var(--line); border-radius: 12px; background: var(--card-2); padding: 12px 13px; margin-bottom: 10px; }
  .rd-skips + .rd-skips { margin-top: -2px; }
  .rd-skip-n { font-weight: 600; font-size: 14px; line-height: 1.3; }
  .rd-skip-chips { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 9px; }
  .rd-skip-chips .rd-tog { flex: 0 1 auto; min-width: 0; padding: 10px 12px; }
  .rd-skip-chips .rd-tog[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
  .rd-offer { border: 1px solid var(--accent); border-radius: 12px; background: var(--accent-soft); padding: 13px 14px; margin-bottom: 10px; }
  .rd-offer-n { margin: 5px 0 3px; font-size: 24px; font-weight: 700; font-variant-numeric: tabular-nums; }
  .rd-offer-n s { margin-left: 8px; font-size: 16px; font-weight: 600; color: var(--mut); }
  .rd-dateline {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--line-2); font-size: 13px;
  }
  .rd-save { width: 100%; margin-top: 16px; }

  /* Drill videos (backlog #23, ratified "A + C's label" 2026-08-19): the
     film row on the deck card, and the owner-only admin card on Profile. */
  /* The curated video is EMBEDDED since 2026-09-16 (owner: "embed videos in
     all drills") and draws the same `.rd-own` facade as a credited film; the
     link row (`.rd-film`) and the centred video door (`.rd-video-row`) went
     with it. */
  /* Baked setup card (owner directive, 2026-08-21). The strip on the open
     block is TEXT — the dimensions, which is the thing you actually re-check
     mid-day — and the drawing is one tap behind it. Measured before choosing:
     the diagram inline costs about a third of a 390x844 screen and pushes the
     metric fields and Bar met/missed under the fold, which is the opposite of
     what an open block is for. The strip costs 56px. */
  .rd-setup-row {
    display: flex; align-items: center; gap: 9px; width: 100%; min-height: 44px;
    border: 1px solid var(--accent); background: var(--accent-soft);
    border-radius: 10px; padding: 9px 11px; margin: 10px 0 4px;
    text-align: left; cursor: pointer; color: var(--accent-dk); font: inherit;
  }
  :root[data-resolved-theme="dark"] .rd-setup-row { color: var(--accent); }
  .rd-setup-row .rd-sr-l { display: block; font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; opacity: .72; }
  .rd-setup-row .rd-sr-d { font-size: 13px; font-weight: 650; line-height: 1.35; }
  /* Centred, stacked door (2026-09-07): label line with its glyph, title
     line, text centred. The video row that shared this shape became the
     embedded facade on 2026-09-16. */
  .rd-setup-row {
    flex-direction: column; align-items: center; justify-content: center; gap: 2px; text-align: center; min-height: 52px;
  }
  .rd-setup-row .rd-sr-l { display: inline-flex; align-items: center; gap: 5px; }
  .rd-setup-row .rd-sr-l svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.8; }
  /* The card's media glyphs, icon only, in the sub line. */
  .rd-mg { display: inline-flex; vertical-align: -2px; margin-right: 3px; color: var(--ink); }
  .rd-mg svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.8; }
  /* The diagram is a BACKGROUND IMAGE, not an <img>, so it can follow the
     PINNED theme: <picture media="(prefers-color-scheme: dark)"> tracks the
     system and would disagree with the topbar switch the moment anyone used
     it. Two baked files, two custom properties, one selector — and it
     re-evaluates the instant core.js re-stamps data-resolved-theme, with no
     re-render. */
  .rd-diag {
    width: 100%; aspect-ratio: var(--rd-ar, 340 / 300);
    background-image: var(--rd-l); background-repeat: no-repeat;
    background-position: center; background-size: contain;
    background-color: var(--card); border: 1px solid var(--line); border-radius: 12px;
  }
  :root[data-resolved-theme="dark"] .rd-diag { background-image: var(--rd-d); }
  .rd-card-purpose { color: var(--accent-dk); font-weight: 600; font-size: 14px; margin-bottom: 10px; }
  :root[data-resolved-theme="dark"] .rd-card-purpose { color: var(--accent); }
  .rd-card-sec { margin-top: 14px; }
  .rd-card-sec h3 { font-size: 11.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--mut); margin: 0 0 6px; }
  .rd-card-sec p { margin: 0; font-size: 14px; line-height: 1.55; }
  .rd-kit { display: flex; flex-wrap: wrap; gap: 7px; }
  .rd-kit span { font-size: 12.5px; font-weight: 650; padding: 6px 10px; border-radius: 8px; background: var(--card-2); border: 1px solid var(--line); }
  .rd-card-acts { display: flex; gap: 9px; margin-top: 18px; }
  .rd-card-acts .btn { flex: 1; text-decoration: none; }
  #videos-panel h2 .dv-pill { vertical-align: 3px; margin-left: 6px; }

  /* ── Drill authoring (backlog #48, 2026-08-20) — the curators-only card
     that shares the Admin tab with the video slots. ── */
  #drills-panel h2 .dv-pill { vertical-align: 3px; margin-left: 6px; }
  .dr-ask { border: 1px solid var(--line); border-radius: 12px; background: var(--card-2); padding: 12px 14px; margin: 14px 0 4px; }
  .dr-ask textarea { width: 100%; }
  .dr-ask-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
  .dr-form { border: 1px solid var(--accent); border-radius: 12px; background: var(--card); padding: 12px 14px; margin: 14px 0; }
  .dr-form h3 { margin: 0 0 2px; }
  .dr-form input, .dr-form select, .dr-form textarea { width: 100%; }
  .dr-fs { border: 1px solid var(--line); border-radius: 10px; padding: 8px 12px 12px; margin: 14px 0 0; min-width: 0; }
  .dr-fs legend { font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: var(--mut); padding: 0 4px; }
  .dr-fs .mut { font-size: 12.5px; margin: 4px 0 0; }
  .dr-met { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 8px; margin-top: 10px; align-items: end; }
  /* One column under ~380px: two side-by-side text inputs are unusable on a
     phone, and this card is used at a desk far more often than at the range. */
  @media (max-width: 380px) { .dr-met { grid-template-columns: 1fr; } }
  .dr-shelf-h { margin: 22px 0 6px; }
  .dr-row { border: 1px solid var(--line); border-radius: 12px; background: var(--card); margin-bottom: 8px; padding: 10px 12px; }
  .dr-row.retired { opacity: .62; }
  .dr-row .dr-lbl { font-weight: 650; }
  .dr-row .dr-meta { font-size: 12px; color: var(--mut); margin-top: 2px; }
  .dr-row .dr-acts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
  .dr-row .dr-acts .btn { padding: 6px 12px; font-size: 13px; }
  .dr-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
  .dr-tags span { font-size: 11px; border-radius: 99px; padding: 2px 8px; border: 1px solid var(--line); background: var(--card-2); color: var(--mut); }
  .dr-tags .chal { color: var(--green-dk); border-color: color-mix(in srgb, var(--green-dk) 45%, transparent); background: color-mix(in srgb, var(--green) 14%, var(--card)); font-weight: 650; }
  .dr-tags .check { border-style: dashed; font-weight: 600; }

  /* Shooter assessment — a card of its own on the Admin tab since
     2026-08-20 (it was a divider inside the Profile card until then). */
  .as-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 8px 0; border-bottom: 1px solid var(--line-2); }
  .as-row:last-child { border-bottom: 0; }
  .as-row .as-who { flex: 1 1 auto; min-width: 0; }
  .as-row .mut { font-size: 13px; }
  .as-free { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin: 14px 0 4px; }
  .as-free input { flex: 1 1 150px; min-width: 0; }
  .as-div { margin-top: 16px; }
  .as-div h4 { margin: 0 0 4px; }
  /* Rows stay on one line and the wrapper scrolls — a 6-column run table
     wrapped inside 375px is unreadable. */
  .as-tablewrap { overflow-x: auto; }
  .as-tablewrap th, .as-tablewrap td { white-space: nowrap; }
  .as-note { font-size: 12.5px; color: var(--mut); margin: 6px 0 0; }
  /* Full write-up (owner directive 2026-08-19): the read the app writes
     around the computed numbers, plus the PDF hand-off. */
  .as-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin: 12px 0 0; }
  /* The download is an anchor so the browser handles the save natively; kill
     the link underline so it reads as the button it is styled as. */
  .as-actions a.btn { text-decoration: none; }
  .as-actions .as-note { margin: 0; flex: 1 1 220px; min-width: 0; }
  .as-read { margin-top: 14px; border: 1px solid var(--line); border-radius: 12px; background: var(--card-2); padding: 12px 14px; }
  .as-read h4 { margin: 0 0 8px; }
  .as-read h5 { margin: 14px 0 4px; font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: var(--mut); }
  .as-read p { margin: 0 0 6px; font-size: 14px; }
  .as-list { margin: 0; padding-left: 20px; font-size: 14px; }
  .as-list li { margin-bottom: 6px; }
  .dv-row { border: 1px solid var(--line); border-radius: 12px; background: var(--card); margin-bottom: 8px; padding: 10px 12px; }
  /* Shelf groups and the batch run (2026-09-16: every drill has a slot). */
  .dv-shelf { margin-top: 14px; }
  .dv-shelf-h { font-size: 13px; font-weight: 700; color: var(--ink); margin: 0 0 6px; }
  .dv-shelf-h .mut { font-weight: 500; }
  .dv-batch { margin: 8px 0 4px; }
  .dv-batch .btn { min-height: 44px; }
  .dv-batch p { margin: 6px 0 0; font-size: 12.5px; }
  .dv-rowhead { display: flex; align-items: center; gap: 8px; width: 100%; background: none; border: 0; padding: 0; font: inherit; color: var(--ink); text-align: left; cursor: pointer; }
  .dv-rowhead .dv-lbl { font-weight: 650; font-size: 13.5px; min-width: 0; }
  .dv-pill { flex: none; margin-left: auto; font-size: 11px; font-weight: 650; border-radius: 99px; padding: 2px 9px; border: 1px solid var(--line); background: var(--card-2); color: var(--mut); white-space: nowrap; }
  .dv-pill.ok { color: var(--green-dk); border-color: var(--green-dk); background: transparent; }
  .dv-pill.pend { color: var(--accent-dk); background: var(--accent-soft); border-color: transparent; }
  .dv-detail { margin-top: 10px; border-top: 1px solid var(--line-2); padding-top: 10px; }
  .dv-nom { border: 1px solid var(--line); border-radius: 10px; background: var(--card-2); padding: 10px 12px; margin: 8px 0; }
  .dv-nom .dv-vt { font-weight: 650; font-size: 13.5px; }
  .dv-nom .dv-vc { font-size: 12px; color: var(--mut); margin-top: 1px; }
  .dv-why { font-size: 12.5px; margin: 7px 0; padding: 6px 9px; border-left: 3px solid var(--accent); background: var(--accent-soft); border-radius: 0 8px 8px 0; }
  .dv-clip { display: flex; align-items: center; gap: 8px; margin: 8px 0 2px; font-size: 12.5px; flex-wrap: wrap; }
  .dv-clip label { font-weight: 650; color: var(--mut); font-size: 11.5px; }
  .dv-clip input { font: inherit; font-size: 16px; width: 74px; padding: 4px 6px; border: 1px solid var(--line); border-radius: 7px; background: var(--card); color: var(--ink); font-variant-numeric: tabular-nums; }
  .dv-clipnote { font-size: 11.5px; color: var(--mut); margin: 2px 0 8px; }
  .dv-gate { display: flex; align-items: center; gap: 7px; font-size: 12.5px; margin: 8px 0; cursor: pointer; }
  .dv-gate input { width: 17px; height: 17px; margin: 0; accent-color: var(--accent); flex: none; }
  .dv-btns { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 6px; }
  .dv-cur { font-size: 13px; }
  .dv-cur .dv-vc { font-size: 12px; color: var(--mut); }
  .dv-addform { border-top: 1px solid var(--line-2); margin-top: 10px; padding-top: 10px; }
  .dv-addform .fld { margin-top: 8px; }
  .dv-addform input { width: 100%; font: inherit; font-size: 16px; padding: 6px 8px; border: 1px solid var(--line); border-radius: 8px; background: var(--card); color: var(--ink); }

  /* ── How to — the range card (concept B, ratified 2026-08-19 from the
     how-to mockup round). A bottom sheet, because it is opened one-handed
     between squads; the four range commands group the tasks, so the list
     says WHEN in the loop each one happens. ── */
  #topbar-right { display: inline-flex; align-items: center; gap: 6px; }
  /* The two topbar icon buttons — How to, and the light/dark switch — share
     one shape so the pair reads as a set. They were built the same day on
     separate branches and each arrived with its own wrapper and its own
     sizing; this is the merge (2026-08-20). One rule, so the next icon button
     added up here inherits the set instead of inventing a third look. */
  header.topbar #btn-videos, header.topbar #btn-coach, header.topbar #btn-howto, header.topbar #btn-theme {
    /* 44px is the Web Interface Guidelines touch minimum, and this row is
       tapped one-handed with the phone half out of a range bag (owner,
       2026-08-20). The glyphs scale with the buttons — a 14px "?" adrift in
       a 44px circle reads as a mis-sized control, not a bigger target. */
    width: 44px; height: 44px; padding: 0; flex: none;
    border-radius: 50%; border: 1px solid var(--line);
    background: var(--card-2); color: var(--mut);
    font-size: 17px; font-weight: 700; line-height: 1;
  }
  header.topbar #btn-videos:hover, header.topbar #btn-coach:hover, header.topbar #btn-howto:hover, header.topbar #btn-theme:hover { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
  /* The topbar used to shed parts as it narrowed — the signed-in name below
     420px, the wordmark below 340px — because brand + name + Sign out + two
     44px circles needed 321px at a 320px iPhone SE (measured 2026-08-20).
     Moving the name and Sign out to the Profile tab the same day took ~83px
     out of the row, so both rules are gone: brand + two circles measures
     238px at 320px (measured 2026-08-20), single-line with 82px to spare, so
     the full wordmark now survives to the SE. If a third icon button is ever
     added up here, re-measure at 320px before assuming it still fits. */
  header.topbar #btn-theme { display: inline-flex; align-items: center; justify-content: center; }
  .howto {
    /* Pin the sheet ourselves rather than leaning on the UA's modal box —
       top:0 + bottom:0 + margin-top:auto over height:fit-content, which is
       what "margin: auto auto 0" was riding on. That stack measures right in
       Chrome and in desktop Safari (both checked 2026-08-20) and collapsed on
       the phone: the sheet came up about a header tall, parked at the very
       bottom, its first line already behind the browser toolbar and no way to
       reach the rest (owner, iOS, 2026-08-20). Same shape as nav.tabbar
       above, which has been pinned this way since the shell landed: fixed to
       the bottom edge, centred by margin, height from content. z-index is
       inert while the dialog is in the top layer and is the floor if it ever
       is not. */
    position: fixed; inset: auto 0 0; z-index: 20;
    width: 100%; max-width: 560px; margin: 0 auto;
    height: auto; /* overrides the UA's height: fit-content */
    max-height: min(86vh, 760px);
    /* svh is the viewport with the browser chrome SHOWN, so the sheet can
       never be taller than what the phone is actually displaying. The vh line
       above stays as the fallback for anything without svh — drop one and the
       other still caps it. */
    max-height: min(86svh, 760px);
    padding: 0; border: 0; border-top: 1px solid var(--line);
    border-radius: 18px 18px 0 0; background: var(--bg); color: var(--ink);
    box-shadow: 0 -14px 40px -20px rgba(0, 0, 0, .5);
    overflow: hidden;
  }
  /* A floor, phones only, so a sheet is still a sheet if some engine
     shrink-wraps it to nothing again. The list and all seven task pages are
     taller than this at phone widths, so it binds in the failure case and
     nowhere else — and the failure case becomes a short sheet you can read
     and scroll instead of a strip of chrome you cannot. */
  @media (max-width: 560px) {
    .howto { min-height: min(48svh, 420px); }
  }
  .howto[open] { display: flex; flex-direction: column; }
  .howto::backdrop { background: rgba(10, 14, 20, .45); }
  @media (prefers-reduced-motion: no-preference) {
    .howto[open] { animation: howto-up .18s ease-out; }
  }
  @keyframes howto-up { from { transform: translateY(16px); } to { transform: none; } }
  .howto-grab { width: 36px; height: 4px; border-radius: 2px; background: var(--line); margin: 8px auto 0; flex: none; }
  .howto-head {
    display: flex; align-items: center; gap: 8px; flex: none;
    padding: 8px 12px 10px; border-bottom: 1px solid var(--line);
  }
  .howto-head h2 { flex: 1; margin: 0; font-size: 16.5px; font-weight: 700; min-width: 0; }
  .howto-head h2:focus { outline: none; } /* programmatic focus target, never tabbed to */
  .howto-nav {
    font: inherit; font-size: 13.5px; font-weight: 650; cursor: pointer;
    background: none; border: 0; color: var(--mut); border-radius: 8px;
    padding: 8px 10px; min-height: 40px; min-width: 40px;
  }
  .howto-nav:hover { color: var(--ink); background: var(--line-2); }
  .howto-body { flex: 1; overflow-y: auto; padding: 12px 14px calc(20px + env(safe-area-inset-bottom)); }
  .ht-intro { margin: 0 0 4px; font-size: 13px; }
  .ht-cmd {
    display: flex; align-items: center; gap: 9px; margin: 15px 0 7px;
    font-size: 11.5px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--accent);
  }
  .ht-cmd::after { content: ""; flex: 1; height: 1px; background: var(--line); }
  .ht-task {
    display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
    font: inherit; font-size: 14.5px; font-weight: 650; color: var(--ink); cursor: pointer;
    background: var(--card); border: 1px solid var(--line); border-radius: 12px;
    padding: 11px 12px; margin: 0 0 7px; min-height: 52px; box-shadow: var(--shadow-sm);
  }
  .ht-task:hover { border-color: var(--accent); }
  /* The guide rows are <a>, not <button>: kill the underline, keep the row. */
  a.ht-task { text-decoration: none; }
  .ht-tl { flex: 1; min-width: 0; }
  .ht-tl small { display: block; font-size: 12.5px; font-weight: 500; color: var(--mut); margin-top: 1px; }
  .ht-go { color: var(--mut); font-size: 18px; line-height: 1; flex: none; }
  .ht-guard { margin: 16px 0 0; font-size: 12.5px; color: var(--mut); text-align: center; }
  .ht-before { margin: 0 0 12px; font-size: 13.5px; color: var(--mut); }
  .ht-steps { margin: 0; padding: 0; list-style: none; counter-reset: htstep; }
  .ht-steps li { counter-increment: htstep; position: relative; padding: 0 0 11px 31px; font-size: 14.5px; }
  .ht-steps li::before {
    content: counter(htstep); position: absolute; left: 0; top: 2px;
    width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center;
    background: var(--accent-soft); color: var(--accent-dk); font-size: 11.5px; font-weight: 700;
  }
  .ht-expect {
    display: block; margin: 4px 0 0; padding: 10px 11px; border-radius: 10px; font-size: 13.5px;
    background: color-mix(in srgb, var(--green) 14%, var(--card));
    border: 1px solid color-mix(in srgb, var(--green-dk) 40%, transparent);
  }
  .ht-expect b { color: var(--green-dk); }
  .ht-fix { border-top: 1px solid var(--line); margin-top: 12px; padding-top: 10px; font-size: 13.5px; }
  .ht-fix summary { cursor: pointer; font-weight: 650; color: var(--mut); min-height: 32px; }
  .ht-fix p { margin: 8px 0 0; color: var(--mut); }
  .ht-stamp { margin: 12px 0 0; font-size: 11.5px; color: var(--mut); }
  /* Show me (owner-ratified 2026-09-11): one quiet pill under a step, and
     the still it opens — a dark band of the real screen with a ring drawn
     from the control's captured box. The pill is the drill strip's weight,
     not a button's: the words are the page, the picture is a footnote. */
  .ht-show {
    display: flex; width: fit-content; align-items: center; margin: 6px 0 0; padding: 4px 12px; min-height: 36px;
    font: inherit; font-size: 12.5px; font-weight: 650; color: var(--accent); cursor: pointer;
    background: var(--card); border: 1px solid var(--line); border-radius: 999px;
  }
  .ht-show[aria-expanded="true"] { background: var(--accent-soft); border-color: var(--accent); }
  .ht-still { margin: 8px 0 2px; }
  .ht-fig { position: relative; margin: 0; width: 100%; max-width: 100%; overflow: hidden; border-radius: 12px; border: 1px solid var(--line); background: #10151c; }
  .ht-fig img { display: block; width: 100%; height: auto; }
  .ht-ring { position: absolute; border: 2.5px solid var(--accent); border-radius: 10px; box-shadow: 0 0 0 4px var(--ring); pointer-events: none; }
  @media (prefers-reduced-motion: no-preference) {
    .ht-ring { animation: ht-pulse 1.8s ease-in-out infinite; }
  }
  @keyframes ht-pulse { 50% { box-shadow: 0 0 0 9px var(--ring); } }
  .ht-still-off { margin: 0; font-size: 12.5px; color: var(--mut); }
  /* The price table the credits page alone carries (2026-09-07): the words
     are credits.js's, the rows the hub's shape. */
  .ht-prices { border: 1px solid var(--line); border-radius: 12px; background: var(--card); overflow: hidden; margin: 0 0 14px; }
  .ht-price { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-top: 1px solid var(--line-2); font-size: 13.5px; min-height: 40px; }
  .ht-price:first-child { border-top: 0; }
  .ht-price > span { flex: 1; }
  .ht-price small { display: block; color: var(--mut); font-size: 11.5px; }
  .ht-price b { font-variant-numeric: tabular-nums; white-space: nowrap; }
  .ht-price b.free { color: var(--green-dk); font-weight: 650; }

  @media print {
    header.topbar, nav.tabbar, .segs, #build-sheet, #input-card, #plan-empty, .stage-new, #rangeday-card, #profile-panel, #history-panel, #admin-panel, #videos-panel, #drills-panel, #assess-card, #invites-card, #porch, #porch-foot, .banner.err { display: none !important; }
    .plan-pane[hidden] { display: block !important; } /* every segment prints */
    /* The stage bar prints as the plan's title — pinning it would repeat it. */
    .stage-bar { position: static; margin: 0 0 8px; padding: 0; border: 0; }
    body { background: #fff; color: #000; }
    .card { border: 0; padding: 0; box-shadow: none; }
    main { max-width: none; padding: 0; }
  }

/* Squad Drills (#69). A squad lane is the accent-bordered lane the resume
   row already uses — the exclusivity is the point, so it reads as the
   primary thing on the launcher when a day is live. */
.rd-lane.squad { border-color: var(--accent); box-shadow: 0 0 0 1px var(--ring); }
.rd-lane.squad .rd-cat-name { color: var(--accent); }
.rd-ask { border: 1px solid var(--accent); border-radius: 12px; padding: 12px; margin-bottom: 10px; background: var(--accent-soft); }
.rd-ask h4 { margin: 0 0 4px; font-size: 15px; }
.rd-ask ul { margin: 6px 0 8px; padding-left: 18px; font-size: 13px; line-height: 1.55; }
.rd-ask .rd-ask-btns { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.rd-mrow { display: flex; gap: 10px; align-items: center; padding: 9px 0; border-bottom: 1px solid var(--line-2); }
.rd-mrow:last-child { border-bottom: 0; }
.rd-mrow .rd-av {
  width: 34px; height: 34px; flex: none; border-radius: 99px; display: grid; place-items: center;
  font-weight: 700; font-size: 12.5px; background: var(--card-2); border: 1px solid var(--line); color: var(--mut);
}
.rd-mrow .rd-nm { font-weight: 650; font-size: 14px; }
.rd-mrow .rd-sub { color: var(--mut); font-size: 12px; font-variant-numeric: tabular-nums; }
.rd-mrow .rd-st {
  margin-left: auto; font-size: 11px; padding: 3px 9px; border-radius: 99px; white-space: nowrap;
  border: 1px solid var(--line); color: var(--mut); background: var(--card-2);
}
.rd-mrow .rd-st.on { color: var(--green-dk); border-color: color-mix(in srgb, var(--green-dk) 45%, transparent); background: color-mix(in srgb, var(--green) 14%, var(--card)); font-weight: 650; }
.rd-mrow .rd-st.wait { color: var(--warn); border-color: var(--warn-line); background: var(--warn-bg); font-weight: 650; }
.rd-addrow { display: flex; gap: 8px; margin: 10px 0; }
.rd-addrow input { flex: 1; min-width: 0; text-transform: uppercase; font-variant-numeric: tabular-nums; }
.rd-addrow input::placeholder { text-transform: none; }
.rd-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 20px; letter-spacing: .16em;
  text-align: center; padding: 12px; border-radius: 10px; background: var(--card-2); border: 1px dashed var(--line);
  margin: 8px 0; user-select: all;
}
/* A per-shooter card on the send screen: same drills, their own numbers. */
.rd-pcard { border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; margin-bottom: 8px; background: var(--card); }
.rd-pcard .rd-ph { display: flex; align-items: baseline; gap: 8px; }
.rd-pcard .rd-ph .rd-sub { margin-left: auto; }
.rd-plist { margin: 8px 0 0; padding: 0; list-style: none; font-size: 12.5px; }
.rd-plist li { display: flex; gap: 10px; padding: 4px 0; border-top: 1px dashed var(--line-2); font-variant-numeric: tabular-nums; }
.rd-plist li:first-child { border-top: 0; }
.rd-plist li span:last-child { margin-left: auto; text-align: right; font-weight: 650; }
.rd-plist li.first span:last-child { color: var(--warn); }
.rd-plist li.shared span:last-child { color: var(--mut); font-weight: 600; }
/* The shooter's own bar on the session block — the one number on the card
   that is theirs and not the drill's. */
.rd-mybar {
  margin: 8px 0 0; padding: 8px 10px; border-radius: 9px; font-size: 12.5px; line-height: 1.45;
  background: color-mix(in srgb, var(--green) 12%, var(--card));
  border: 1px solid color-mix(in srgb, var(--green-dk) 35%, transparent); color: var(--green-dk);
  font-variant-numeric: tabular-nums;
}
.rd-mybar.first { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); }

/* ── The MATCH tab (#90, owner-directed 2026-08-22) ──────────────────────────
   Ratified from the Match-tab round (artifact 48dbd88c…). Everything here is
   tokens, so a new surface inherits both themes rather than inventing a look
   (the topbar merge decision, applied down here). Nothing below invents an
   idiom the shell did not already have: the list rows are the History row's
   shape, the segments are .segs, the sheet is #build-sheet's .sheet, and the
   stage bar is the plan view's own. */

/* Stage list. Full-bleed inside the card and paying 8px back as row padding,
   the way #history-list does — the difference between the meta line fitting
   on one row and wrapping on every row. */
.m-legend { margin: 2px 2px 10px; font-size: 11.5px; color: var(--mut); line-height: 1.4; }
#match-view .howlink { margin: 0 0 8px; }
.m-list { margin: 0 calc(-1 * clamp(16px, 3vw, 24px)); padding: 0 8px; }
.m-row { display: flex; align-items: stretch; margin-bottom: 8px; }
.m-item {
  position: relative; overflow: hidden; flex: 1; min-width: 0;
  display: grid; grid-template-columns: 52px 1fr; gap: 11px; align-items: start;
  text-align: left; background: var(--card); color: var(--ink);
  border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px 13px;
  cursor: pointer; font: inherit;
  transition: border-color .12s, background .12s;
}
.m-item:hover { border-color: var(--accent); background: var(--card-2); }
/* A stage with nothing on it reads as an empty slot rather than a record. */
.m-item.blank { border-style: dashed; }
/* Leads with the HIT FACTOR, where .hist-grade leads with the class: History's
   rows are all classifiers, a match is mostly field courses, and points ÷ time
   is the one number every row here actually has. */
.m-grade { display: grid; justify-items: center; gap: 1px; padding-top: 1px; }
.m-grade .hf { font-size: 21px; font-weight: 750; line-height: 1; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.m-grade .lb { font-size: 10.5px; color: var(--mut); line-height: 1.25; text-align: center; font-variant-numeric: tabular-nums; }
.m-grade .lb b { font-weight: 750; color: var(--ink); }
.m-grade.none .hf { color: var(--mut); font-weight: 600; }
.m-title { display: block; font-size: 15px; line-height: 1.25; letter-spacing: -.005em; }
.m-title b { font-weight: 700; font-variant-numeric: tabular-nums; }
.m-title span { color: var(--mut); }
.m-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; font-size: 12px; line-height: 1.35; color: var(--mut); margin-top: 3px; }
.m-meta .dot { opacity: .5; }
.m-meta .tag {
  font-size: 10.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  padding: 1px 6px; border-radius: 99px; border: 1px solid var(--line); color: var(--mut);
}
.m-meta .tag.plan { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.m-meta .tag.first { border-color: var(--green-dk); color: var(--green-dk); }
/* Spans with display:block, never <p> — .m-item is a <button>, whose content
   model is phrasing content only. The range-day log row already learned this. */
.m-quote {
  display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden;
  font-size: 12.5px; line-height: 1.4; color: var(--mut); margin-top: 5px;
}
.m-todo { display: block; font-size: 12.5px; color: var(--accent); font-weight: 650; margin-top: 4px; }
/* The class-band rail, .hist-rail's instrument with its 1px gaps: butted
   together at 5px with no labels the ramp reads as a part-filled progress bar
   instead of a position on the D→GM scale. Drawn ONLY where a published HHF
   exists — an empty scale reads as a zero. */
.m-rail { position: absolute; left: 0; right: 0; bottom: 0; display: flex; gap: 1px; height: 5px; overflow: hidden; }
.m-rail .b1 { width: 40%; background: var(--band-1); }
.m-rail .b2 { width: 20%; background: var(--band-2); }
.m-rail .b3 { width: 15%; background: var(--band-3); }
.m-rail .b4 { width: 10%; background: var(--band-4); }
.m-rail .b5 { width: 10%; background: var(--band-5); }
.m-rail .b6 { width: 5%;  background: var(--band-6); }
.m-rail i { position: absolute; top: 0; bottom: 0; width: 3px; background: var(--accent); transform: translateX(-1.5px); }

/* Row overflow — 44px, the touch floor the top bar and stage bar keep. A
   SIBLING of the row button, never inside it. */
.m-more {
  width: 44px; flex: 0 0 44px; border: 0; background: none; color: var(--mut);
  border-radius: 10px; cursor: pointer; font-size: 17px; line-height: 1;
  transition: background .12s, color .12s;
}
.m-more:hover { background: var(--card-2); color: var(--ink); }
/* The menu ACTS on the tap — no confirm step. NN/g: confirmation is for what
   cannot be undone, and this can. The safety net is .m-undo below. */
.m-menu { margin: -4px 0 10px; display: flex; }
.m-menu-i {
  flex: 1; text-align: left; min-height: 44px; padding: 0 13px;
  border: 1px solid var(--err-line); border-radius: 10px;
  background: var(--err-bg); color: var(--err); cursor: pointer;
  font: inherit; font-size: 13.5px; font-weight: 650;
}
/* The undo row stands WHERE the stage was, in number order. Deliberately not a
   floating toast: this app has no toast anywhere else, so that would be a new
   primitive, and one that can scroll off screen while you are looking away. */
.m-undo {
  display: flex; align-items: center; gap: 10px; margin-bottom: 8px;
  padding: 11px 12px; border-radius: 12px;
  border: 1px dashed var(--line); background: var(--card-2);
  font-size: 12.5px; line-height: 1.35; color: var(--mut);
}
.m-undo span { flex: 1; min-width: 0; }
/* 44px. This is the ONE control that recovers destroyed work — the last place
   in the app to sit under the touch floor. */
.m-undo-b {
  flex: none; min-height: 44px; padding: 0 17px; border-radius: 99px; cursor: pointer;
  border: 1px solid var(--accent); background: var(--accent-soft); color: var(--accent);
  font: inherit; font-size: 13.5px; font-weight: 700;
}

/* Archive over delete (owner-ratified 2026-09-07): the foot of the stage
   list, the History foot's shape. Restore is a WORD in the row's gutter. */
.m-archived { margin-top: 4px; }
.m-archived > summary { cursor: pointer; color: var(--mut); font-size: 13px; font-weight: 600; padding: 8px 2px; list-style: none; }
.m-archived > summary::-webkit-details-marker { display: none; }
.m-archived > summary::before { content: '▸ '; }
.m-archived[open] > summary::before { content: '▾ '; }
.m-row-arch { gap: 8px; }
.m-row-arch > .m-item { flex: 1; min-width: 0; cursor: default; }
/* The row menu's one item is Archive now: not red, because nothing here
   destroys anything. */
.m-menu-i.arch { border-color: var(--line); background: var(--card-2); color: var(--ink); }
.m-restore {
  flex: none; align-self: stretch; padding: 0 10px; border-radius: 10px; cursor: pointer;
  border: 1px solid var(--line); background: var(--card); color: var(--mut);
  font: inherit; font-size: 12.5px; font-weight: 600;
}
.m-restore:hover { border-color: var(--accent); color: var(--ink); }

/* What the day adds up to — and what it deliberately does not. There is no
   percentage and no place here, because scoring a stage needs the match
   winner's hit factor and this app holds no other competitor's numbers. */
.m-foot { margin-top: 14px; padding: 12px 13px; border-radius: 11px; background: var(--card-2); border: 1px solid var(--line); }
.m-foot .r { display: flex; justify-content: space-between; gap: 10px; font-size: 13px; padding: 3px 0; }
.m-foot .r b { font-weight: 650; font-variant-numeric: tabular-nums; }
.m-foot .why { margin: 8px 0 0; font-size: 11.5px; line-height: 1.45; color: var(--mut); }

/* The results column: the evening pass with the sheet in your other hand. */
.res-row {
  display: grid; grid-template-columns: 34px 1fr 1fr 62px; gap: 7px; align-items: center;
  padding: 7px 0; border-bottom: 1px solid var(--line-2);
}
.res-row.head {
  border-bottom: 1px solid var(--line); padding-bottom: 5px;
  font-size: 10px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--mut);
}
.res-row.head span:nth-child(3), .res-row.head span:nth-child(4) { text-align: right; }
.res-n { font-size: 13px; font-weight: 700; color: var(--mut); font-variant-numeric: tabular-nums; }
.res-n small { display: block; font-size: 11px; font-weight: 700; color: var(--green-dk); letter-spacing: .04em; }
.res-row input { padding: 7px 8px; font-size: 16px; text-align: right; border-radius: 8px; font-variant-numeric: tabular-nums; }
.res-hf { text-align: right; font-size: 15px; font-weight: 700; color: var(--mut); font-variant-numeric: tabular-nums; }
.res-hf.on { color: var(--ink); }
.res-hf small { display: block; font-size: 11px; font-weight: 700; color: var(--accent); letter-spacing: .03em; }

/* One stage. The score sits above the fold here, where the review's own
   time/points pair used to sit at 1,193px inside a fifth segment. */
.stage-bar .back {
  flex: none; width: 44px; height: 44px; margin-left: -8px; padding: 0;
  border: 0; background: none; color: var(--mut); border-radius: 10px;
  cursor: pointer; font: inherit; font-size: 22px; line-height: 1;
}
.stage-bar .back:hover { background: var(--card-2); color: var(--ink); }
/* 96px, not 88: the readout's key went to the 11px legibility floor
   (2026-09-09) and "HIT FACTOR" no longer fits 88px on one line. The container
   gave way rather than the type — the two inputs beside it lose 4px each and
   are 16px number fields either way. */
.scorebox { display: grid; grid-template-columns: 1fr 1fr 96px; gap: 9px; align-items: end; margin-top: 12px; }
.scorebox .f { min-width: 0; }
.scorebox input { text-align: right; font-variant-numeric: tabular-nums; }
.hfout { border: 1px solid var(--line); border-radius: 9px; background: var(--card-2); padding: 6px 8px; text-align: center; min-height: 42px; }
.hfout .v { font-size: 17px; font-weight: 750; line-height: 1.15; font-variant-numeric: tabular-nums; }
.hfout .k { font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--mut); font-weight: 700; white-space: nowrap; }
.hfout.on { border-color: var(--accent); background: var(--accent-soft); }
.hfout.on .v { color: var(--accent); }
/* A classifier can say what its hit factor MEANS; a field course cannot, and
   says so rather than leaving a gap for the reader to fill in. */
.classline {
  margin-top: 10px; padding: 9px 11px; border-radius: 9px; font-size: 12.5px; line-height: 1.4;
  background: color-mix(in srgb, var(--green) 12%, var(--card));
  border: 1px solid color-mix(in srgb, var(--green-dk) 40%, transparent); color: var(--green-dk);
}
.noclass { margin-top: 10px; font-size: 11.5px; line-height: 1.45; color: var(--mut); }
.planbox { border: 1px dashed var(--line); border-radius: 12px; padding: 16px 14px; text-align: center; background: var(--card-2); }
.planbox p { margin: 0 0 12px; font-size: 13px; color: var(--mut); line-height: 1.5; }
.lbl-h { font-size: 12.5px; font-weight: 550; color: var(--mut); margin: 12px 0 6px; }
/* One checkbox on its own line — the .checks row's look without the wrapping
   grid, since there is only ever one of these. */
.chk { display: flex; align-items: center; gap: 9px; min-height: 44px; font-size: 13.5px; cursor: pointer; }
.chk input { width: 18px; height: 18px; flex: none; accent-color: var(--accent); }

/* Destructive is an OUTLINE, never a filled block: the app's one filled button
   is the primary action, and a solid red slab reads as the thing to press. */
.btn.danger { background: var(--card); border: 1px solid var(--err-line); color: var(--err); }
.btn.danger:hover { background: var(--err-bg); }
.btn.wide { width: 100%; }

/* ── History's second axis + match rows (#93, owner-directed 2026-08-23) ─────
   The Match tab kept its job (the day in front of you, one tap); History's
   ROWS changed instead — a row is a match, opening to its stages, which is the
   drill-down every comparable app uses. Everything here reuses the log's own
   shapes; nothing invents an idiom. */

/* The axis sits inside the sticky head, above the filter chips. It bleeds to
   the card edge the way .chips does so the two read as one stack. */
#history-axis { margin: 10px calc(-1 * clamp(16px, 3vw, 24px)) 0; }
#history-axis:not(.hidden) { display: flex; }

/* The day's shape, one bar per stage. Height by hit factor against the best of
   THAT DAY — a within-day question ("which stage went wrong"), so deliberately
   not a scale shared across matches. Bars, not a line: seven runs on one
   morning are seven events, and a line between them would draw a trend inside
   a single match that nothing supports. */
.spark { display: flex; align-items: flex-end; gap: 3px; height: 26px; margin-top: 6px; }
.spark i { flex: 1; min-height: 3px; border-radius: 2px 2px 0 0; background: var(--band-5); }
/* A stage with a published HHF — the classifier — is the one worth picking out. */
.spark i.is-clf { background: var(--accent); }
/* Absent, never estimated: an unscored stage is a stub, never a zero-height bar
   that reads as "scored nothing". */
.spark i.none { background: var(--line); min-height: 3px; }

/* A match row's grade block holds the STAGE COUNT, not a class: a match has no
   single class, and most of its stages have none at all. */
.hist-item .hist-grade .cls { font-variant-numeric: tabular-nums; }

/* ── The classifier axis ── */
.clf-card {
  border: 1px solid var(--line); border-radius: 12px; background: var(--card);
  padding: 11px 12px; margin-bottom: 8px;
}
.clf-head { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.clf-head b { font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; flex: none; }
.clf-head span { color: var(--mut); font-size: 13px; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.clf-head .n { flex: none; font-size: 11.5px; font-weight: 650; }
.clf-runs { margin: 6px 0 0; padding: 0; list-style: none; }
.clf-runs li { border-top: 1px dashed var(--line-2); }
.clf-runs li:first-child { border-top: 0; }
/* The row is a button so a run opens the day it was shot — 44px, the touch
   floor the rest of the shell keeps. */
.clf-runs button {
  display: flex; align-items: baseline; gap: 8px; width: 100%; min-height: 44px;
  padding: 4px 2px; border: 0; background: none; color: var(--ink); cursor: pointer;
  font: inherit; font-size: 12.5px; text-align: left; font-variant-numeric: tabular-nums;
  border-radius: 8px;
}
.clf-runs button:hover { background: var(--card-2); }
.clf-runs .when { width: 54px; flex: none; color: var(--mut); }
.clf-runs .cls { width: 26px; flex: none; font-weight: 750; }
.clf-runs .src { color: var(--mut); font-size: 11.5px; }
.clf-runs .pct { margin-left: auto; font-weight: 700; font-size: 13.5px; }
.clf-runs li.best .pct { color: var(--green-dk); }
/* The gate, said out loud rather than by an absent chart: three runs is the
   same bar range-day trends have had since the beginning. */
.clf-gate { margin: 7px 2px 0; font-size: 11px; line-height: 1.45; color: var(--mut); }

/* The stage screen's "last time at this code" (#93). Nearly free — the plan
   already reads that exact run at build time (#71) and throws the number away. */
.priorline {
  margin: 10px 0 0; padding: 8px 11px; border-radius: 9px;
  background: var(--card-2); border: 1px solid var(--line);
  font-size: 12.5px; line-height: 1.4; color: var(--mut); font-variant-numeric: tabular-nums;
}
.priorline b { color: var(--ink); font-weight: 700; }

/* ── The match switcher (backlog #60, earned) ── */
/* The match name becomes a button. It was an inert label, and `latest` was the
   only way into the tab — so a second match made the first unreachable. */
.mswitch {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px;
  padding: 0; border: 0; background: none; color: var(--ink); cursor: pointer;
  text-align: left; font: inherit; border-radius: 8px;
}
.mswitch h2 {
  flex: 0 1 auto; min-width: 0; margin: 0; font-size: 16px; font-weight: 700; letter-spacing: -.015em;
  white-space: nowrap; text-wrap: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mswitch .chev { flex: none; color: var(--mut); font-size: 11px; }
.mswitch:hover h2 { color: var(--accent); }
.mswitch[aria-expanded="true"] .chev { color: var(--accent); }
/* A panel under the bar, not a dialog: this is "which day", a short list of
   recent things, not a task worth a modal. */
.mswitch-list { margin: 0 0 12px; padding: 4px 0 0; }
.mswitch-list .mrow {
  display: block; width: 100%; text-align: left; margin-bottom: 6px;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: 11px;
  background: var(--card); color: var(--ink); cursor: pointer; font: inherit;
}
.mswitch-list .mrow:hover { border-color: var(--accent); background: var(--card-2); }
.mswitch-list .mrow[aria-selected="true"] { border-color: var(--accent); background: var(--accent-soft); }
.mswitch-list .mrow b { display: block; font-size: 14.5px; font-weight: 700; }
.mswitch-list .mrow span { display: block; margin-top: 2px; font-size: 12px; color: var(--mut); font-variant-numeric: tabular-nums; }

/* A plan needs no match, and never did (#97). Both doors to an unattached
   build are QUIET — a ghost button on the empty state and a text link at the
   foot of the stage list — because starting a match is the ordinary act on this
   tab and planning one loose stage is the exception. Deliberately NOT a second
   pill in the stage bar: that bar already collapses its own pill to a 44px
   circle at 320px, so a second one there would push the match name out. */
.loose-plan {
  margin: 14px 2px 0; font-size: 12.5px; line-height: 1.5; color: var(--mut);
}
.loose-plan button {
  display: inline-flex; align-items: center; margin-top: 7px;
  min-height: 44px; padding: 0 15px; border-radius: 9px;
  border: 1px solid var(--line); background: var(--card); cursor: pointer;
  font: inherit; font-size: 13.5px; font-weight: 650; color: var(--accent);
}
.loose-plan button:hover { color: var(--accent-dk); border-color: var(--accent); }
/* ══ HOME (#99, 2026-08-23) ═══════════════════════════════════════════════════
   The landing when there is no live match. Everything here is built from the
   existing tokens — this round added no colour, and the one thing that is not a
   token is the LCD, which is shared with the login card on purpose: a shot
   timer is a physical object and is the same object in both themes.

   Class names are namespaced `home-` except the four the cadence card shares
   with nothing (`.cadence`, `.cad-*`, `.btn-row`) and the two it borrows from
   the front door (`.lcd`, `.lcd-state`). Borrowing is deliberate: one LCD in
   the app, not two that drift.                                              */

#home-panel { display: flex; flex-direction: column; gap: 14px; }

/* The brand as a button — same box as the static label, so the top bar's 320px
   budget is untouched. */
/* THE WORDMARK IS THE ONLY HOME DOOR (owner, 2026-09-08), so it has to LOOK
   like one. It carried a button's semantics and a brand's appearance for a
   day — which was fine while a Home tab sat in the bottom bar and this was a
   quiet second door, and is not fine now that it is the first.
   It borrows the top bar's own control shape rather than inventing a look:
   the same hairline border, --card-2 ground and accent hover the four
   circles wear. Not a circle, because a wordmark is a word — a pill of the
   same 44px height, so the row still reads as one set of controls. */
header.topbar .brand-btn {
  appearance: none; color: inherit;
  font: inherit; font-weight: 700; font-size: 15.5px; letter-spacing: -.01em;
  display: inline-flex; align-items: center; gap: 8px;
  /* 8px, not 12: measured at 320 with the owner's three glyphs beside it,
     12px left only 13px of slack in a row that had 24.4px before this button
     grew a border. The BORDER is what makes it read as pressable; the padding
     was just air. 8px keeps the wordmark unclipped and the row at 21px. */
  min-height: 44px; padding: 0 8px; margin: 0;
  border: 1px solid var(--line); border-radius: 12px;
  background: var(--card-2); cursor: pointer;
}
header.topbar .brand-btn:hover { border-color: var(--accent); background: var(--accent-soft); }
header.topbar .brand-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* On Home the dot is where you already are, so it says so rather than
   pretending to be somewhere to go — and it is the ONE LIT THING on the
   shell, since no tab is pressed on Home (syncTabs returns null there).
   The 2026-09-07 note that removed this accent said "the tab is the lit thing
   now"; that tab is gone, so the rule comes back with it. */
header.topbar .brand-btn[aria-current="page"] {
  /* The BORDER and the ground carry the lit state; the LOGO is left alone
     (owner, 2026-09-08). Setting `color` here recoloured the wordmark itself
     — the letters are `fill="currentColor"` — so Home turned the mark solid
     orange and swallowed the dot, which is the one part of it that is
     supposed to be orange. Light the frame, never the logo. */
  cursor: default; border-color: var(--accent); background: var(--accent-soft);
}

.home-ident { display: flex; align-items: center; gap: 11px; padding: 2px 2px 0; }
.home-avatar {
  width: 42px; height: 42px; border-radius: 50%; flex: none;
  background: var(--accent-soft); color: var(--accent-dk);
  display: grid; place-items: center; border: 1px solid var(--line);
  font: 700 17px/1 "Barlow Condensed", "Arial Narrow", sans-serif; letter-spacing: .02em;
}
.home-ident-txt { min-width: 0; flex: 1 1 auto; }
.home-name { display: block; font: 700 19px/1.15 "Barlow Condensed", "Arial Narrow", sans-serif; }
.home-sub {
  display: block; font-size: 12px; color: var(--mut);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-variant-numeric: tabular-nums;
}

/* ── The cadence card ─────────────────────────────────────────────────────── */
.cadence {
  border-radius: 16px; border: 1px solid var(--line);
  background: var(--card); box-shadow: var(--shadow-sm);
}
/* The LCD rounds its OWN top rather than the card clipping it: overflow:hidden
   here would clip anything that ever needs to sit at the card's corner. */
.home-lcd {
  margin-bottom: 0; border-radius: 15px 15px 0 0;
  padding: 12px 14px 11px; display: flex; align-items: baseline; gap: 10px;
}
/* The command NEVER wraps — it is the one word the strip exists to say, and
   "ARE YOU / READY" over two lines reads as a stutter at 375px. The detail
   beside it is the part allowed to give way. */
.home-lcd .lcd-state { display: inline; font-size: 14px; white-space: nowrap; flex: none; }
.home-lcd.clear .lcd-state { color: #7ee08a; }
.lcd-sub {
  margin-left: auto; min-width: 0; text-align: right;
  font: 500 10px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .12em; text-transform: uppercase; color: #5b6675;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cad-body { padding: 14px; display: flex; flex-direction: column; gap: 11px; }
.cad-head {
  margin: 0; font: 700 25px/1.12 "Barlow Condensed", "Arial Narrow", sans-serif;
  text-wrap: balance;
}
.cad-line { margin: -4px 0 0; font-size: 13.5px; color: var(--mut); }
.btn-row { display: flex; gap: 8px; flex-wrap: wrap; }
/* nowrap + a wrapping ROW: the pair sits side by side while both fit, and
   falls to one full-width button each when they do not. Without nowrap a long
   primary label breaks mid-word at 375px ("Finish the write-/up"). */
.btn-row .btn { width: auto; flex: 1 1 auto; min-height: 46px; white-space: nowrap; }

/* ── Graduation, screen F (2026-09-10) ──────────────────────────────────────
   Its own card above the search, not a variant of the cadence card: that one
   is the next act and this is a receipt. The rows reuse .home-door-row for the
   reason the door reused .home-act's shape — a third list idiom would be a
   third thing to learn. Tokens only. */
.grad {
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  padding: 14px; margin-bottom: 12px;
  display: flex; flex-direction: column; gap: 11px;
}
.grad-h { display: flex; align-items: flex-start; gap: 8px; }
.grad-h h2 {
  flex: 1 1 auto; margin: 0;
  font: 700 23px/1.12 "Barlow Condensed", "Arial Narrow", sans-serif; text-wrap: balance;
}
.grad-x { flex: 0 0 auto; }
.grad-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(64px, 1fr)); gap: 7px; }
.grad-tile {
  display: flex; flex-direction: column; gap: 2px; padding: 8px 6px;
  background: var(--card-2); border: 1px solid var(--line); border-radius: 9px; text-align: center;
}
.grad-tile .n { font-weight: 700; font-size: 15px; font-variant-numeric: tabular-nums; }
.grad-tile .k { font-size: 10.5px; color: var(--mut); line-height: 1.25; }
.grad-line { margin: 0; font-size: 12.5px; color: var(--mut); line-height: 1.5; }
.grad-lbl {
  font: 500 10.5px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .1em; text-transform: uppercase; color: var(--mut);
}
.grad-rows { display: flex; flex-direction: column; gap: 8px; }

/* ── The second door (owner-ratified 2026-09-09) ────────────────────────────
   Inside .cad-body, BELOW the card's own buttons, and set apart by a hairline
   rather than a second card: it is a footnote to the ask above it, not a
   rival to it. Tokens only, so nothing here needs a dark-block entry. The row
   borrows .home-act's shape rather than inventing a third list idiom — the
   two-line label with a chevron is what this shell already means by "a door
   onto something specific". 44px floor per #186. */
#home-door {
  display: flex; flex-direction: column; gap: 10px;
  padding-top: 12px; border-top: 1px solid var(--line);
}
.home-door-h { font-weight: 700; font-size: 15px; }
.home-door-p, .home-door-n { margin: 0; font-size: 12.5px; color: var(--mut); line-height: 1.5; }
.home-door-n { font-size: 11.5px; }
.home-door-rows { display: flex; flex-direction: column; gap: 8px; }
.home-door-row {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: 44px;
  padding: 9px 11px; text-align: left; cursor: pointer;
  background: var(--card-2); color: inherit;
  border: 1px solid var(--line); border-radius: 10px;
}
.home-door-row:hover { border-color: var(--acc); }
.home-door-row .l { flex: 1 1 auto; min-width: 0; }
.home-door-row .l b { display: block; font-weight: 600; font-size: 14px; line-height: 1.2; }
.home-door-row .l small { display: block; font-size: 11.5px; color: var(--mut); line-height: 1.3; }
.home-door-row svg { width: 18px; height: 18px; flex: 0 0 auto; color: var(--mut); }

/* ── The first loop (ratified "A and B" 2026-08-23, artifact 7e650908…) ─────
   Lives inside .cad-body and replaces head/line/actions while a new account's
   loop is open. Tokens only — nothing here needs a dark-block entry. The
   cadence eyebrow borrows .home-act-cmd's face so the two surfaces speak the
   commands in one voice. */
.loop-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.loop-count {
  font: 500 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--mut); white-space: nowrap;
}
.loop-steps { list-style: none; margin: 2px 0 0; padding: 0; }
.loop-step {
  display: flex; align-items: flex-start; gap: 11px;
  padding: 10px 0; border-top: 1px solid var(--line-2);
}
.loop-step:first-child { border-top: none; padding-top: 2px; }
.loop-tick {
  flex: none; width: 22px; height: 22px; margin-top: 1px;
  border: 1.5px solid var(--line); border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; color: var(--card); line-height: 1;
}
.loop-step.done .loop-tick { background: var(--green); border-color: var(--green); }
.loop-step.next .loop-tick { border-color: var(--accent); }
.loop-txt { min-width: 0; flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; }
.loop-cmd {
  font: 600 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .14em; text-transform: uppercase; color: var(--mut);
}
.loop-step.next .loop-cmd { color: var(--accent-dk); }
.loop-t { font-weight: 600; font-size: 14.5px; line-height: 1.25; }
.loop-step.done .loop-t {
  color: var(--mut); font-weight: 500;
  text-decoration: line-through; text-decoration-color: var(--line);
}
.loop-d { font-size: 12.5px; color: var(--mut); line-height: 1.35; }
/* 44px target on the one control per card, the Web Interface Guidelines floor
   the top bar's circles keep. */
.loop-go {
  flex: none; align-self: center; min-height: 44px; padding: 0 12px;
  background: none; border: none; cursor: pointer;
  color: var(--accent); font: 600 14px/1 inherit; font-family: inherit;
  white-space: nowrap; border-radius: 8px;
}
.loop-go:hover { color: var(--accent-dk); }
.loop-foot {
  margin: 0; padding-top: 9px; border-top: 1px solid var(--line-2);
  font-size: 12px; color: var(--mut);
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.loop-notnow.btn-link { width: auto; margin: 0; padding: 8px 4px; flex: none; }

/* ── "How this works" (concept B of the same round) ─────────────────────────
   One quiet link per complex surface, deep-linking the how-to sheet's own
   task page. Muted on purpose: visible to someone lost, invisible to someone
   working. The click is handled globally in howto-page.js off [data-howto]. */
.howlink {
  display: inline-flex; align-items: center; gap: 8px;
  background: none; border: none; padding: 6px 0; cursor: pointer;
  color: var(--mut); font-size: 13px; font-family: inherit; text-align: left;
}
.howlink .q {
  flex: none; width: 20px; height: 20px; border-radius: 50%;
  border: 1px solid var(--line); display: flex; align-items: center;
  justify-content: center; font-size: 11px; line-height: 1;
}
.howlink u { text-decoration-color: var(--line); text-underline-offset: 3px; }
.howlink:hover { color: var(--ink); }
.howlink:hover u { text-decoration-color: var(--mut); }

/* ── Search ───────────────────────────────────────────────────────────────── */
.home-search { position: relative; }
.home-search-ico {
  position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  width: 17px; height: 17px; color: var(--mut); pointer-events: none;
}
#home-q {
  width: 100%; font: 16px/1.2 inherit; /* 16px: iOS zooms the page below it */
  padding: 13px 14px 13px 40px; min-height: 46px;
  border-radius: 11px; border: 1px solid var(--line);
  background: var(--card); color: var(--ink);
}
#home-q:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.home-results { border: 1px solid var(--line); border-radius: 12px; background: var(--card); overflow: hidden; }
.home-res-group {
  font: 600 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .13em; text-transform: uppercase; color: var(--mut);
  padding: 10px 13px 6px; background: var(--card-2);
}
.home-res {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 11px 13px; min-height: 46px; text-align: left; cursor: pointer;
  border: 0; border-top: 1px solid var(--line-2); background: transparent;
  color: var(--ink); font: 14px/1.3 inherit;
}
.home-res:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.home-res-txt { flex: 1 1 auto; min-width: 0; }
.home-res-t { display: block; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.home-res-s { display: block; font-size: 12px; color: var(--mut); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.home-res mark { background: var(--accent-soft); color: var(--accent-dk); border-radius: 3px; padding: 0 1px; }
/* A drill row and its Category button, side by side. The wrap owns the border
   the bare row normally carries, and the inner row drops its own — otherwise
   the hairline draws twice. The button is a sibling of the row (a button in a
   button is invalid), 46px to match the row it rides beside. */
.home-res-wrap { display: flex; align-items: stretch; border-top: 1px solid var(--line-2); }
.home-res-wrap .home-res { border-top: 0; flex: 1 1 auto; min-width: 0; }
.home-res-cat {
  flex: none; align-self: center; min-height: 46px; padding: 0 13px;
  border: 0; background: transparent; cursor: pointer;
  color: var(--accent); font: 600 13px/1 inherit; font-family: inherit;
  white-space: nowrap;
}
.home-res-cat:hover { color: var(--accent-dk); }
.home-res-cat:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.home-res-empty { padding: 16px 13px; font-size: 13.5px; color: var(--mut); }

/* ── Sections ─────────────────────────────────────────────────────────────── */
/* Sections are TITLED CARDS (round three of the shell review, 2026-09-07,
   owner "B as recommended"): the title is the card's first row with a
   hairline under it and the → link in that row, cards 16px apart. The edge
   separates, not whitespace — a header inside its card can never be misread
   as belonging to the section above, which is exactly what the whitespace-
   only labels of that morning did (the space above one was 0px). Colour was
   drawn and refused: an accent title competes with the one lit thing, a
   tinted band reads as a table header and flattens the edge. */
#home-body { display: flex; flex-direction: column; gap: 16px; }
.home-sec {
  display: flex; flex-direction: column; gap: 12px;
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  box-shadow: var(--shadow-sm); padding: 0 0 13px; overflow: hidden;
}
.home-sec-h {
  display: flex; align-items: baseline; gap: 8px; margin: 0;
  padding: 12px 14px 10px; border-bottom: 1px solid var(--line-2);
  font-family: inherit; font-size: 15px; font-weight: 700; line-height: 1.2;
  letter-spacing: -.01em; color: var(--ink);
}
/* The body sits 13px in from the card edge; a rows list is the exception and
   runs edge to edge, its own border gone, so the hairlines line up with the
   header's. */
.home-sec > :not(.home-sec-h) { margin-left: 13px; margin-right: 13px; }
.home-sec > .home-rows { margin-left: 0; margin-right: 0; border: 0; border-radius: 0; }
.home-sec > .home-sec-h + .home-rows { margin-top: -12px; }
.home-sec > .home-rows:last-child { margin-bottom: -13px; }
/* The readiness meter inside a titled card is a row, not a card in a card. */
.home-sec .home-meter { border: 0; padding: 0; margin-bottom: 0; box-shadow: none; background: transparent; }
.home-more {
  margin-left: auto; letter-spacing: 0; text-transform: none;
  font: 500 12.5px/1 inherit; color: var(--accent);
  background: none; border: 0; cursor: pointer; padding: 6px 4px;
}
.home-more:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── Quick actions ────────────────────────────────────────────────────────── */
.home-acts { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
/* A locked quick action's line (#117). coach-page.js inserts the lock line
   as the button's next sibling, which in a two-column grid would take the
   cell beside it and push the next action onto a row of its own. Span the
   row and order it last instead: the line sits under the grid, directly
   beneath the door it names (left column, last row), and the grid's gap is
   its margin. DOM order is unchanged, so aria-describedby still pairs them. */
.home-acts > .lock-why { grid-column: 1 / -1; order: 1; margin: 0; }
.home-act {
  display: flex; flex-direction: row; gap: 10px; align-items: center;
  padding: 11px 12px; min-height: 58px; cursor: pointer; text-align: left;
  border: 1px solid var(--line); border-radius: 12px;
  background: var(--card); color: var(--ink); font: inherit;
}
.home-act:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.home-act > svg { width: 20px; height: 20px; flex: none; color: var(--mut); }
/* These label/sub trios sit inside a flex ITEM, which is blockified — but their
   own children are spans and stay inline unless told otherwise, so the title
   and its one-line description would run together as prose. */
.home-act-t, .home-act-s { display: block; }
/* Under the Between-matches lens the baseline tiles lead "Your numbers" and
   the readiness meter follows; the section is a flex column, so `order` is
   the whole change and the DOM stays where the tests expect it. */
#home-nums-sec[data-lens="between"] .home-meter { order: 2; }
/* A locked Home action's line (coach-page.js LOCKS) lands inside the grid;
   span it so it does not become a stray cell. */
.home-acts > .lock-why { grid-column: 1 / -1; margin: -2px 2px 0; }
/* The same line under the cadence card's locked secondary (#119): the row is
   a wrapping flex, so the note takes the whole width beneath the pair rather
   than becoming a third item beside them. */
#home-cad-actions > .lock-why { flex-basis: 100%; margin: 0 2px; }
/* The one accented tile (2026-09-07): a coach with a shooter waiting on
   their plan — the Coach dot's state, said where the thumb rests. */
.home-act.waiting { border-color: var(--accent); }
.home-act.waiting > svg { color: var(--accent); }
.home-act.is-locked { border-style: dashed; color: var(--mut); cursor: default; }
/* #122: a locked card spans the grid while locked, so its line (the next
   sibling `<p>`) reads as part of the card rather than a stray row under the
   pair; a small lock glyph sits top-right, drawn as a mask so it takes the
   card's muted colour in both themes. Measured at 320 in the walk. */
.home-act.is-locked { grid-column: 1 / -1; position: relative; padding-right: 34px; }
.home-act.is-locked::after {
  content: ''; position: absolute; top: 10px; right: 10px; width: 14px; height: 14px;
  background: currentColor; opacity: .75;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2'%3E%3Crect x='5' y='11' width='14' height='10' rx='2'/%3E%3Cpath d='M8 11V8a4 4 0 0 1 8 0v3'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2'%3E%3Crect x='5' y='11' width='14' height='10' rx='2'/%3E%3Cpath d='M8 11V8a4 4 0 0 1 8 0v3'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* The student tag beside the identity block, the hub's chip shape. */
.home-tag { flex: none; align-self: center; }
/* Program rows (#122): a category heading inside the rows box and the
   Logged / Not yet badge, component-qualified like every Home modifier. */
.home-subgrp {
  padding: 9px 13px 3px; font: 600 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .12em; text-transform: uppercase; color: var(--mut); border-top: 1px solid var(--line-2);
}
.home-subgrp:first-child { border-top: 0; }
.home-badge {
  flex: none; font-size: 11.5px; font-weight: 650; padding: 4px 9px; border-radius: 99px;
  border: 1px solid var(--line); background: var(--card); color: var(--mut); white-space: nowrap;
}
.home-badge.good { border-color: var(--green-dk); color: var(--green-dk); background: transparent; }
/* A grid item's min-width is `auto`, so a nowrap line inside a tile widened its
   track past the phone (walked at 375: the second tile ran off the right
   edge). min-width: 0 on the tile is what lets the ellipsis happen. */
.home-tiles .home-tile { min-width: 0; }
.home-tile-tag.home-tile-tag-bar { display: block; width: 100%; color: var(--mut); text-transform: none; letter-spacing: 0; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.home-act-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.home-act-t { font-weight: 600; font-size: 14px; line-height: 1.2; }
.home-act-s { font-size: 11.5px; color: var(--mut); line-height: 1.3; }

/* ── Your numbers ─────────────────────────────────────────────────────────── */
/* The entry shell's not-yet line (2026-09-09). Hair type, muted, no border and
   no icon: it is a fact about the app, not a warning about the shooter, and
   anything that framed it as a notice would make an absence feel like a
   penalty. 12px is the house's smallest body size, not a new tier. */
/* Screen E's chips, on the wrap and on the curator's row (2026-09-10). The
   toggle shape is `rd-tog`'s, already the wrap's vocabulary for "pick one of
   these" — the keep/not-this-time offer uses it three inches above. */
.rd-fb { margin: 8px 0 2px; }
.rd-fb .rd-togs { flex-wrap: wrap; }
.dr-fb { color: var(--mut); }

/* ── A drill's OWN film (2026-09-10) ───────────────────────────────────────
   `rd-own-`, not `rd-film-`. The first cut used the second and collided: the
   curated third-party strip has owned `.rd-film`, `.rd-film-lbl` and
   `.rd-film-seg` since it was written, so a second `.rd-film { }` sat 900
   lines below the first and both applied to our markup — a border and a
   padding nobody asked for, from a component with a different job. The two
   films are two claims and they get two prefixes.
   A FACADE: the still and a play control, sized 16:9 so the player that
   replaces it lands in the same box and the sheet does not jump under the
   thumb. `object-fit: cover` because YouTube's hqdefault is 4:3 with black
   bars baked in — cropping to 16:9 is what removes them. */
.rd-own { margin: 10px 0 2px; }
.rd-own-face {
  position: relative; display: block; width: 100%; aspect-ratio: 16 / 9; padding: 0;
  border: 1px solid var(--line); border-radius: 12px; overflow: hidden; background: #0d1117; cursor: pointer;
}
.rd-own-face[disabled] { cursor: default; opacity: .72; }
.rd-own-still { width: 100%; height: 100%; object-fit: cover; display: block; }
.rd-own-go {
  position: absolute; inset: 0; margin: auto; width: 54px; height: 54px; border-radius: 50%;
  background: rgba(0,0,0,.45); border: 1.5px solid rgba(255,255,255,.55); color: #fff;
  display: grid; place-items: center;
}
.rd-own-go svg { width: 20px; height: 20px; margin-left: 3px; }
.rd-own-face[disabled] .rd-own-go { background: rgba(0,0,0,.6); border-style: dashed; }
.rd-own-len {
  position: absolute; right: 8px; bottom: 8px; padding: 2px 6px; border-radius: 4px;
  background: rgba(0,0,0,.72); color: #e6edf3; font: 600 11px/1.4 ui-monospace, Menlo, monospace;
  font-variant-numeric: tabular-nums;
}
.rd-own-player { aspect-ratio: 16 / 9; border: 1px solid var(--line); border-radius: 12px; overflow: hidden; background: #0d1117; }
.rd-own-player iframe { width: 100%; height: 100%; border: 0; display: block; }

/* The porch's own line about the meter (2026-09-09). Sits under the three
   steps as a coda rather than a fourth step — it is the condition on the loop
   above it, not another part of the loop. */
.porch-steps-note { margin: 18px auto 0; max-width: 64ch; text-align: center; }
.home-notyet { margin: 10px 2px 0; font-size: 12px; line-height: 1.45; color: var(--mut); }
.home-meter { padding: 13px; }
.home-meter-row { display: flex; align-items: center; gap: 10px; }
.home-meter-n {
  font: 700 26px/1 "Barlow Condensed", "Arial Narrow", sans-serif;
  font-variant-numeric: tabular-nums; flex: none;
}
.home-meter-l { font-size: 12.5px; color: var(--mut); line-height: 1.3; }
.home-meter-bar { display: flex; gap: 3px; margin-top: 9px; }
.home-meter-bar i { flex: 1 1 0; height: 5px; border-radius: 3px; background: var(--line); }
.home-meter-bar i.on { background: var(--green-dk); }

.home-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
/* The five baselines in ONE row (shell review 2026-09-07): two columns left
   Reload orphaned on a third row and spent 200px saying five numbers. The
   provenance line ("Typed · Sep 5") does not fit a 66px tile and is not
   drawn here — the Profile hub's tiles carry it, one tap away, and the
   section's "Profile →" is that tap. An untimed tile still says so on its
   own face. */
#home-tiles { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 5px; }
.home-sec > #home-tiles { margin-left: 8px; margin-right: 8px; }
#home-tiles .home-tile { padding: 9px 6px 10px; gap: 1px; }
#home-tiles .home-tile-v { font-size: 19px; }
#home-tiles .home-tile-l { font-size: 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 8px was the smallest type in the app, and it was on the one word that says
   a baseline is not real yet ("Not timed"). At the 11px floor it does not fit a
   fifth of the row on one line — so the LINE gave way, not the type: it wraps
   to two, every tile in the grid row grows together, and the strip is still one
   row of five (2026-09-09). */
#home-tiles .home-tile-tag { font-size: 11px; letter-spacing: 0; line-height: 1.15; max-width: 100%; }
/* At 320 a fifth of the row is 53px and "Transition" does not fit; three
   tiles over two, all the same height, with no orphan (measured 2026-09-07). */
@media (max-width: 359px) {
  #home-tiles { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  #home-tiles .home-tile { grid-column: span 2; }
  #home-tiles .home-tile:nth-child(4), #home-tiles .home-tile:nth-child(5) { grid-column: span 3; }
}
.home-tile {
  border: 1px solid var(--line); border-radius: 12px; background: var(--card);
  padding: 10px 11px; text-align: left; cursor: pointer; color: var(--ink);
  font: inherit; display: flex; flex-direction: column; gap: 2px;
}
.home-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.home-tile-v {
  font: 700 22px/1 "Barlow Condensed", "Arial Narrow", sans-serif;
  font-variant-numeric: tabular-nums;
}
.home-tile-v small { font-size: 12px; font-weight: 500; color: var(--mut); margin-left: 1px; }
.home-tile-l { font-size: 11.5px; color: var(--mut); line-height: 1.25; }
.home-tile-tag {
  font: 600 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .08em; text-transform: uppercase; margin-top: 3px; color: var(--warn);
}

/* ── Where you left off ───────────────────────────────────────────────────── */
.home-rows { border: 1px solid var(--line); border-radius: 14px; background: var(--card); overflow: hidden; }
.home-row {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 11px 13px; min-height: 58px; cursor: pointer; text-align: left;
  border: 0; border-top: 1px solid var(--line-2); background: transparent;
  color: var(--ink); font: inherit;
}
.home-row:first-child { border-top: 0; }
.home-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.home-grade {
  width: 34px; height: 34px; flex: none; border-radius: 9px; display: grid; place-items: center;
  background: var(--card-2); border: 1px solid var(--line);
  font: 700 15px/1 "Barlow Condensed", "Arial Narrow", sans-serif;
}
/* Filled for a run that actually happened, hollow for a prediction (#63). */
.home-grade.real { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-dk); }
.home-row-txt { flex: 1 1 auto; min-width: 0; }
.home-row-t { display: block; font-weight: 600; font-size: 14.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.home-row-s {
  display: block; font-size: 12px; color: var(--mut); font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.home-row-end { flex: none; font-size: 12px; color: var(--mut); font-variant-numeric: tabular-nums; }
.home-empty { padding: 16px 13px; font-size: 13.5px; color: var(--mut); }
/* #121: the last report's priorities are STATIC rows in a list — same row
   shape, no pointer, and the title may wrap because a priority is a sentence
   and truncating one leaves "Tighten the second shot on the far…". */
.home-static { list-style: none; margin: 0; padding: 0; }
.home-row.home-row-static { cursor: default; }
.home-row-t.home-row-wrap-t { white-space: normal; overflow: visible; text-overflow: clip; line-height: 1.3; }
/* A one-line note under a section heading (the stale mark). Component-
   qualified — a bare `.st-stale` already colours the report-state chip. */
.home-sec-note { margin: -4px 0 8px; font-size: 12px; color: var(--mut); line-height: 1.4; }
/* The read's date beside the section heading — a fact, set at the heading's
   weight but muted, so it reads as a qualifier and not a second title. */
.home-sec-when { color: var(--mut); font-weight: 500; letter-spacing: 0; text-transform: none; }
.home-sec-note.st-stale { color: var(--warn); }

/* ── The tab directory ────────────────────────────────────────────────────── */
.home-dir { border: 1px solid var(--line); border-radius: 14px; background: var(--card); overflow: hidden; }
.home-dir-row {
  display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
  padding: 11px 13px; min-height: 58px; cursor: pointer;
  border: 0; border-top: 1px solid var(--line-2); background: transparent;
  color: var(--ink); font: inherit;
}
.home-dir-row:first-child, .home-dir-row.hidden + .home-dir-row { border-top: 0; }
.home-dir-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.home-dir-row > svg { width: 19px; height: 19px; flex: none; color: var(--mut); }
.home-dir-row { position: relative; }
.home-dir-row .tab-dot { top: 15px; left: 25px; }
.home-dir-txt { flex: 1 1 auto; min-width: 0; }
.home-dir-t { display: block; font-weight: 600; font-size: 14.5px; }
.home-dir-s { display: block; font-size: 12px; color: var(--mut); line-height: 1.35; }
.home-chev { width: 15px; height: 15px; flex: none; color: var(--mut); }

@media (max-width: 340px) {
  .home-acts, .home-tiles { grid-template-columns: 1fr; }
  /* One column has no cell to steal, so the lock line (#117) keeps DOM
     order and follows its own door instead of the grid's last card. */
  .home-acts > .lock-why { order: 0; }
}

  /* The relocated Admin glyph (Coach/Admin round, 2026-08-23). It inherits the
     44px circle from the shape rule above — one look for the whole row, so the
     third icon button joined the set instead of inventing a third style — and
     only needs its SVG sized, which the other two do through font-size. */
  header.topbar #btn-videos svg, header.topbar #btn-coach svg { width: 22px; height: 22px; }
  header.topbar #btn-videos[aria-current="page"], header.topbar #btn-coach[aria-current="page"] {
    color: var(--accent); border-color: var(--accent); background: var(--accent-soft);
  }
  /* The Coach glyph's waiting dot (#115) on a 44px circle: top-right of the
     circle rather than of a 22px tab icon. */
  header.topbar #btn-coach { position: relative; }
  header.topbar #btn-coach .tab-dot { top: 3px; left: auto; right: 3px; }

  /* ── The Coach tab (owner-ratified 2026-08-22/23) ────────────────────────
     Rows, categories and the undo strip. Everything here reuses the app's
     tokens; nothing hardcodes a colour, so both themes follow for free. */
  #coach-panel .co-sub { font-size: 13px; margin: 2px 0 12px; }
  #coach-panel .co-note { font-size: 12.5px; line-height: 1.5; margin-top: 12px; }
  .co-row {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    width: 100%; text-align: left; padding: 11px 12px; margin-bottom: 8px;
    background: var(--card); border: 1px solid var(--line); border-radius: 10px;
    font: inherit; color: inherit; cursor: pointer; min-height: 44px;
  }
  .co-row:hover { border-color: var(--accent); }
  .co-row:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .co-row.co-flat { cursor: default; background: var(--card-2); }
  .co-row.co-flat:hover { border-color: var(--line); }
  .co-t { display: block; font-size: 14.5px; font-weight: 600; line-height: 1.25; }
  .co-s { display: block; font-size: 12px; color: var(--mut); margin-top: 2px; line-height: 1.35; }
  .co-chev { color: var(--mut); font-size: 16px; flex: none; }
  /* The roster row's last-day line (spend-surface #9, 2026-09-07): ink, not
     muted — it is the number the coach decides on. */
  .co-last { display: block; font-size: 12px; color: var(--ink); margin-top: 2px; line-height: 1.35; font-variant-numeric: tabular-nums; }
  /* The first-loop chip (concept B, 2026-08-24). Monochrome by default; the
     accent marks exactly one state — waiting on your plan — because it is
     the one the person reading the roster can fix. Green marks a close. */
  .co-loop {
    flex: none; align-self: center; white-space: nowrap;
    font: 500 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
    letter-spacing: .06em; color: var(--mut);
    border: 1px solid var(--line); border-radius: 999px; padding: 4px 9px;
  }
  .co-loop.you { color: var(--accent); border-color: var(--accent); }
  .co-loop.closed { color: var(--green-dk); border-color: var(--green); }
  .co-wait { display: block; font-size: 11.5px; color: var(--accent); margin-top: 2px; }
  .co-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: var(--mut); }
  .co-tag.co-tag-mine { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
  .co-tag {
    display: inline-block; font-size: 10px; font-weight: 600; letter-spacing: .04em;
    text-transform: uppercase; vertical-align: 1px; border: 1px solid var(--line);
    border-radius: 3px; padding: 1px 4px; color: var(--mut); margin-left: 5px;
  }
  .co-pills { margin: 0 0 12px; display: flex; gap: 5px; flex-wrap: wrap; }
  .co-pill {
    font-size: 11px; border: 1px solid var(--line); border-radius: 20px;
    padding: 2px 8px; color: var(--mut);
  }
  .co-pill.on { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
  .co-back {
    border: 0; background: none; padding: 0 0 10px; font: inherit; font-size: 13px;
    color: var(--mut); cursor: pointer; display: block; min-height: 32px;
  }
  .co-back:hover { color: var(--accent); }
  .co-btns { display: flex; gap: 8px; flex-wrap: wrap; margin: 12px 0 0; }
  .co-in {
    width: 100%; font: inherit; font-size: 16px; padding: 9px 10px;
    border: 1px solid var(--line); border-radius: 9px;
    background: var(--card); color: var(--ink); margin-bottom: 9px;
  }
  .co-code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 20px;
    font-weight: 600; letter-spacing: .06em; color: var(--accent);
    background: var(--accent-soft); border: 1px dashed var(--accent);
    border-radius: 9px; padding: 12px; text-align: center; margin: 4px 0 9px;
  }
  .co-cat { border: 1px solid var(--line); border-radius: 10px; background: var(--card); margin-bottom: 9px; overflow: hidden; }
  .co-cat-h {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    padding: 9px 11px; background: var(--card-2); border-bottom: 1px solid var(--line-2);
  }
  .co-cat-n { font-size: 13px; font-weight: 600; }
  .co-drill {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    padding: 8px 11px; border-bottom: 1px solid var(--line-2);
  }
  .co-drill:last-child { border-bottom: 0; }
  .co-dn { display: block; font-size: 13px; line-height: 1.3; }
  .co-db { display: block; font-size: 11px; color: var(--mut); margin-top: 2px; line-height: 1.3; }
  /* The hit area reaches 44x44 through a pseudo-element while the drawn box
     stays 34 (2026-09-09). Growing the box would grow every row of a coach's
     program — the rows are ~50px of two-line text, and a 44px glyph would put
     them at 60 and push a twelve-drill category below the fold. The ⋮ is the
     only control in the row, so the 5px it takes on each side lands on the
     row's own padding and steals nothing. */
  .co-kebab {
    position: relative;
    border: 1px solid var(--line); background: var(--card-2); color: var(--mut);
    width: 34px; height: 34px; flex: none; border-radius: 8px; cursor: pointer;
    font: inherit; font-size: 15px; line-height: 1;
  }
  .co-kebab::after { content: ""; position: absolute; inset: -5px; }
  .co-kebab:hover { color: var(--accent); border-color: var(--accent); }
  .co-empty { padding: 9px 11px; font-size: 12px; color: var(--mut); font-style: italic; }
  .co-catfoot { padding: 7px 11px; border-top: 1px solid var(--line-2); }
  .co-lnk { border: 0; background: none; padding: 0; font: inherit; font-size: 12px; color: var(--mut); cursor: pointer; text-decoration: underline; min-height: 32px; }
  .co-lnk:hover { color: var(--accent); }
  .co-menu { border-top: 1px solid var(--line-2); background: var(--card-2); padding: 8px 10px; }
  .co-mh { font-size: 11px; color: var(--mut); margin: 4px 0 5px; }
  .co-menu button {
    display: block; width: 100%; text-align: left; border: 0; background: none;
    font: inherit; font-size: 13px; padding: 8px; border-radius: 7px;
    cursor: pointer; color: var(--ink); min-height: 38px;
  }
  .co-menu button:hover { background: var(--accent-soft); color: var(--accent); }
  .co-menu button[disabled] { color: var(--mut); cursor: default; }
  .co-menu button[disabled]:hover { background: none; color: var(--mut); }
  .co-barin { margin-bottom: 6px; }
  /* Undo sits WHERE the row was — the app has no toast anywhere, so one would
     be a new primitive that can also scroll off screen (ratified 2026-08-22). */
  .co-undo {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    border: 1px solid var(--line); border-radius: 10px; background: var(--card-2);
    padding: 8px 10px; margin-bottom: 9px; font-size: 12px; color: var(--mut);
  }
  .co-undo button {
    border: 1px solid var(--accent); background: transparent; color: var(--accent);
    border-radius: 8px; padding: 6px 12px; font: inherit; font-size: 12.5px;
    font-weight: 600; cursor: pointer; flex: none; min-height: 36px;
  }
  /* Admin acts on a person's screen, fenced so it reads as a different power
     rather than more coaching. */
  .co-adminbox, .co-fundbox {
    border: 1px solid var(--line); border-left: 3px solid var(--mut);
    border-radius: 0 10px 10px 0; padding: 9px 10px 2px; margin: 10px 0 4px;
    background: var(--card-2);
  }
  /* Carrying somebody is a standing commitment to spend, so the box says so
     at a glance rather than only in the row's own words. */
  .co-fundbox.on { border-left-color: var(--accent); }
  .co-xfer { max-width: 110px; }
  .co-ah {
    font-size: 10px; letter-spacing: .12em; text-transform: uppercase;
    color: var(--mut); margin-bottom: 7px; font-weight: 600;
  }
  /* #coach-mine renders INSIDE the Profile hub's Account .rows (2026-09-07):
     its rows are the hub's own .row shape. The wrapper is not a .row, so the
     first-child rule cannot see through it — restated here. */
  #coach-mine .row:first-child { border-top: 0; }
  #coach-mine.hidden + .row { border-top: 0; }
  #coach-mine .co-coachnote { display: block; white-space: normal; margin-top: 3px; }
/* What your coach changed (#251, the shooter's side). Inside the Profile
   hub's Account rows, so it borrows the row shape and adds only the list. */
#coach-mine .co-mydoc-row .rv { white-space: normal; }
#coach-mine .co-mydoc { list-style: none; margin: 8px 0 0; padding: 0; }
#coach-mine .co-mydoc > li {
  border-top: 1px solid var(--line-2); padding: 7px 0; display: block;
}
#coach-mine .co-mydoc > li:first-child { border-top: 0; }
#coach-mine .co-mydoc-w { display: block; font-size: 12.5px; line-height: 1.4; color: var(--mut); }
#coach-mine .co-mydoc-v { display: block; font-size: 13px; margin-top: 3px; font-variant-numeric: tabular-nums; }
#coach-mine .co-mydoc-o { margin: 3px 0 0; padding-left: 18px; font-size: 12.5px; line-height: 1.45; }
#coach-mine .co-mydoc-o li { margin: 1px 0; }
  #coach-mine .co-ask-btns { display: flex; gap: 8px; flex: none; }
  #coach-mine .co-ask-btns .btn { min-height: 40px; padding: 0 12px; }
  #coach-mine .row.co-ask-row .rv { white-space: normal; }
  .co-ask { border: 1px solid var(--accent); border-radius: 10px; padding: 12px; background: var(--accent-soft); margin-bottom: 10px; }
  .co-ask p { margin: 0 0 6px; }
  .co-endbtn { flex: none; }

  /* A coach's category, as a headed group inside "Your program" (2026-08-23).
     Quieter than .rd-grp, which titles the whole aisle. */
  .rd-subgrp {
    font-size: 11.5px; font-weight: 650; letter-spacing: .04em; text-transform: uppercase;
    color: var(--mut); margin: 12px 0 6px; padding-bottom: 4px;
    border-bottom: 1px solid var(--line-2);
  }

  /* A locked control — still on screen, still named, plainly not pressable.
     Locked, not hidden (owner 2026-08-23): the lock IS the invitation. */
  .is-locked { opacity: .5; cursor: not-allowed; }
  .lock-why {
    font-size: 12px; line-height: 1.45; margin: 6px 0 0;
    padding-left: 10px; border-left: 2px solid var(--line);
  }
  .lock-why-flash { border-left-color: var(--accent); color: var(--accent); }
/* Reports (#102). Reuses .home-row wholesale — a report row and a recent row
   are the same object with a different second line, and inventing a second
   row style for it would be two things to keep in step. The state chip is the
   only addition: written / not yet / out of date, encoded in form as well as
   words so it reads at a glance. */
/* The row and its mail button are SIBLINGS in a flex wrapper — a button nested
   inside a button is invalid and unclickable, and the row is a button. */
.home-row-wrap { display: flex; align-items: stretch; border-top: 1px solid var(--line-2); }
.home-row-wrap:first-child { border-top: 0; }
.home-row-wrap .home-row { border-top: 0; flex: 1 1 auto; min-width: 0; }
.home-mail {
  flex: none; width: 48px; min-height: 44px; cursor: pointer;
  display: grid; place-items: center;
  border: 0; border-left: 1px solid var(--line-2); background: transparent; color: var(--mut);
}
.home-mail svg { width: 18px; height: 18px; }
/* The Reports tab's Debrief door (2026-09-16): a text button in the envelope's
   slot, the price under the word. */
.home-mail.rep-debrief { width: auto; padding: 0 8px; color: var(--accent); font: 600 13px/1.15 inherit; font-family: inherit; }
.home-mail.rep-debrief small { display: block; font-size: 10.5px; font-weight: 500; color: var(--mut); margin-top: 2px; }
.home-mail.rep-debrief:disabled, .home-mail.rep-debrief.is-locked { color: var(--mut); }
.rep-group-h { margin: 14px 0 8px; font: 700 13px/1 inherit; font-family: inherit; color: var(--ink); display: flex; align-items: center; gap: 8px; }
.rep-group-h:first-child { margin-top: 0; }
.rep-group-n { font-weight: 600; color: var(--mut); font-variant-numeric: tabular-nums; }
.m-price { color: var(--accent); font-weight: 600; }
.home-mail:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.home-mail:disabled { cursor: default; }
.home-mail.sending { opacity: .5; }
/* Sent and failed are carried by SHAPE as well as colour — the icon swaps to a
   tick or a bang — because a colour-only state answers nobody who cannot see
   the difference, and the title/aria-label say it in words either way. */
.home-mail.sent { color: var(--green-dk); }
/* Archive over delete (owner-ratified 2026-09-07) on the Reports card — the
   History foot's shape, on Home's row idiom. Restore is a WORD in the
   envelope's slot, so it sizes to its text. */
.home-restore { width: auto; padding: 0 12px; font: inherit; font-size: 12.5px; font-weight: 600; color: var(--mut); }
.home-restore:hover { color: var(--ink); }
.home-undo { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; margin: 0 0 8px; }
.home-undo .btn-link { display: inline; width: auto; margin: 0; padding: 4px 6px; font-weight: 600; color: inherit; text-decoration: underline; }
.home-archived { border-top: 1px solid var(--line-2); }
.home-archived > summary { cursor: pointer; color: var(--mut); font-size: 13px; font-weight: 600; padding: 10px 0 6px; list-style: none; }
.home-archived > summary::-webkit-details-marker { display: none; }
.home-archived > summary::before { content: '▸ '; }
.home-archived[open] > summary::before { content: '▾ '; }
.home-mail.sent svg { display: none; }
.home-mail.sent::after { content: '✓'; font: 700 16px/1 inherit; }
.home-mail.failed { color: var(--err); }
.home-mail.failed svg { display: none; }
.home-mail.failed::after { content: '!'; font: 700 16px/1 inherit; }

.home-report-state {
  display: inline-block; margin-top: 3px;
  font: 600 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .08em; text-transform: uppercase;
}
/* Namespaced modifiers, NOT `.ready`/`.none`/`.stale`. A bare `.ready` already
   exists in this sheet (the profile readiness meter) with a border-top and
   padding, and a bare `.none` in four more places — an unprefixed modifier
   here inherited a hairline rule under the row's title and shipped a stray
   underline. One stylesheet, one namespace per component. */
.home-report-state.st-ready { color: var(--green-dk); }
.home-report-state.st-none { color: var(--mut); }
.home-report-state.st-stale { color: var(--warn); }

  /* "They already have a USPSA number" — the uncommon path, folded away so
     the ordinary one (an email) is the whole form (#104, 2026-08-23). */
  .co-det { margin: 2px 0 10px; }
  .co-det summary { font-size: 12.5px; color: var(--mut); cursor: pointer; padding: 6px 0; min-height: 32px; }
  .co-det summary:hover { color: var(--accent); }

/* The saved write-up sheet (#107) — the email, shown as the email. The frame
   carries its own light palette by design (it IS an email preview), so it sits
   on a plain white ground in both themes rather than being tinted to match the
   app and misrepresenting what arrives. */
.asr-frame {
  width: 100%; height: 62vh; min-height: 320px;
  border: 1px solid var(--line); border-radius: 12px; background: #f5f6f8;
  margin-top: 12px;
}
.asr-note { margin: 0 0 12px; font-size: 12.5px; color: var(--mut); line-height: 1.55; }
.asr-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.asr-actions .btn { width: auto; flex: 0 1 auto; }
.asr-del { color: var(--err); }
.asr-del:hover { border-color: var(--err); }

/* ── The student's view (#125) and the coach note (#126), 2026-09-05 ─────────
   The Plans tab's rows wear History's own classes (.hist-row/.hist-item), so
   the tab invents no idiom; the two rules below are the only new ones. */
/* The coach's one-line note above a plan's verdict: "<coach>: <text>". A
   read, so the serif; replaced not threaded, so no badge and no unread mark. */
.coach-note {
  margin: 0 0 12px; padding: 10px 12px; border-radius: 10px; font-size: 14px; line-height: 1.5;
  background: var(--accent-soft); border: 1px solid var(--accent); color: var(--ink);
}
.coach-note b { color: var(--accent-dk); font-weight: 650; }
/* In the stage bar (2026-09-09): the note stays ABOVE the verdict, and the
   verdict moved into the bar, so the note came with it. Trimmed, because
   everything in that bar is paid for out of the pane under it — and its own
   card border would be a second box inside a box. */
.stage-bar .coach-note {
  margin: 8px 0 0; padding: 8px 10px; font-size: 13px; border-radius: 9px;
}
/* The roster's "not read yet" line (#125 D4): a state in the muted colour, not
   the accent — the accent stays with the one loop state the coach can fix. */
#coach-panel .co-unread { color: var(--mut); }
/* The coach note fields on the shooter screen (#126) and the note under a
   coach's name on the shooter's own card. */
#coach-panel .co-notebox { margin: 8px 0 10px; }
#coach-panel .co-notebox label { display: block; margin: 0 0 4px; }
#coach-panel .co-note { width: 100%; resize: vertical; min-height: 44px; font-size: 16px; line-height: 1.4; }
#coach-panel .co-note-when { margin: 3px 2px 0; font-size: 11.5px; }
#coach-panel .co-plan .co-notebox { margin: 6px 0 2px; }
#coach-panel .co-planopen { font: inherit; font-weight: 600; color: var(--ink); background: none; border: 0; padding: 0; cursor: pointer; text-align: left; }
#coach-panel .co-planopen:hover { color: var(--accent); }

/* The doctrine editor (#251). One card per entry: what it moves, the control,
   and — only when this coach has actually moved it — the way back. The moved
   ones carry the accent on their left edge, so "what do I do differently"
   reads down the page without opening anything. */
#coach-panel .co-docah { margin: 16px 0 8px; }
#coach-panel .co-doc {
  border: 1px solid var(--line); border-left: 3px solid var(--line);
  border-radius: 0 10px 10px 0; padding: 10px 11px; margin-bottom: 9px;
  background: var(--card);
}
#coach-panel .co-doc.moved { border-left-color: var(--accent); background: var(--card-2); }
/* Declared overridable and not yet read by anything (2026-09-13). Shown, and
   shown as inert — the row is a record of intent, not a control. */
#coach-panel .co-doc.pending { border-left-style: dashed; background: transparent; }
#coach-panel .co-doc.pending .co-t { color: var(--mut); }
#coach-panel .co-docpend { margin: 6px 0 0; font-size: 12px; color: var(--mut); line-height: 1.45; }
#coach-panel .co-doc.bad { border-left-color: var(--err-line); }
#coach-panel .co-doc-h { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
#coach-panel .co-doc-h .co-t { font-size: 13.5px; font-weight: 600; }
#coach-panel .co-doc-h .co-pill { flex: none; }
#coach-panel .co-doc .co-s { margin: 3px 0 8px; }
#coach-panel .co-docnum { display: flex; align-items: center; gap: 8px; margin: 0; }
#coach-panel .co-docnum .co-in { max-width: 120px; margin-bottom: 0; }
#coach-panel .co-docu { font-size: 12px; color: var(--mut); }
#coach-panel .co-doclist { list-style: none; margin: 0; padding: 0; }
#coach-panel .co-doclist li {
  display: flex; align-items: flex-start; gap: 8px; padding: 6px 0;
  border-top: 1px solid var(--line-2);
}
#coach-panel .co-doclist li:first-child { border-top: 0; }
#coach-panel .co-docn {
  flex: none; width: 20px; text-align: center; font-size: 11px; font-weight: 700;
  color: var(--mut); font-variant-numeric: tabular-nums; padding-top: 10px;
}
#coach-panel .co-doct { flex: 1; font-size: 13px; padding-top: 8px; }
#coach-panel .co-doctext { margin-bottom: 0; font-size: 13.5px; line-height: 1.4; resize: vertical; }
#coach-panel .co-docmv { flex: none; display: flex; gap: 2px; }
#coach-panel .co-docmv button {
  min-width: 36px; min-height: 36px; font: inherit; font-size: 12px; line-height: 1;
  border: 1px solid var(--line); border-radius: 8px; background: var(--card);
  color: var(--ink); cursor: pointer;
}
#coach-panel .co-docmv button:disabled { color: var(--line); cursor: default; }
#coach-panel .co-docadd, #coach-panel .co-docreset {
  margin-top: 8px; font: inherit; font-size: 12px; color: var(--mut);
  background: none; border: 0; padding: 6px 0; min-height: 36px; cursor: pointer; text-decoration: underline;
}
#coach-panel .co-docadd:hover, #coach-panel .co-docreset:hover { color: var(--accent); }
#coach-panel .co-dockeys { display: grid; gap: 8px; }
#coach-panel .co-dockey { display: flex; align-items: center; gap: 10px; }
#coach-panel .co-dockn {
  flex: none; min-width: 62px; font-size: 11px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--mut);
}
#coach-panel .co-dockey .co-in { margin-bottom: 0; }
#coach-panel .co-dockey input.co-in { max-width: 110px; }
#coach-panel .co-docbad { margin: 6px 0 0; font-size: 12px; color: var(--err); }
#coach-panel .co-docerr {
  border: 1px solid var(--err-line); background: var(--err-bg); border-radius: 10px; padding: 10px 11px;
  margin: 0 0 12px; font-size: 12.5px;
}
#coach-panel .co-docerr p { margin: 0 0 6px; font-weight: 600; color: var(--err); }
#coach-panel .co-docerr ul { margin: 0; padding-left: 18px; }
#coach-panel .co-docerr li { margin: 2px 0; color: var(--err); }
#coach-mine .co-coachnote { margin-top: 5px; color: var(--ink); }
#coach-mine .co-coachnote b { color: var(--accent-dk); font-weight: 650; }
/* What a shooter's Home leads with (#123): a mono line under the roster
   row's second line — a command and one word of why, nothing else. */
#coach-panel .co-leads { display: block; margin-top: 3px; font-size: 11px; letter-spacing: .04em; }

/* ── Dry fire (#127, owner-ratified 2026-09-06) ────────────────────────────
   The Range day tab's second lane. `.rd-segs` is the plan view's `.segs`
   with the launcher's margin; `.rd-lcd` borrows the front door's LCD — one
   LCD in the app, and a timer is a physical object, so its black-and-amber
   is deliberately not a token (the login strip's rule). Everything else is
   tokens, so nothing here needs a dark-block entry. */
.rd-segs { margin: 0 0 12px; }
.rd-lcd {
  background: #0b0e12; border: 1px solid #1f2732; border-radius: 15px 15px 0 0;
  padding: 12px 14px 11px; box-shadow: inset 0 2px 8px rgba(0, 0, 0, .6);
  display: flex; align-items: baseline; gap: 10px;
}
.rd-lcd .lcd-state {
  font: 700 14px/1.2 ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  letter-spacing: .1em; color: #ffb424; text-transform: uppercase; white-space: nowrap; flex: none;
}
.rd-lcd.clear .lcd-state { color: #7ee08a; }
.rd-dry { border: 1px solid var(--line); border-radius: 16px; background: var(--card); box-shadow: var(--shadow-sm); margin-bottom: 4px; }
.rd-dry-body { padding: 12px 14px 14px; display: flex; flex-direction: column; gap: 8px; }
.rd-dry-parrow { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 10px; }
.rd-dry-parlbl { font-size: 12px; font-weight: 700; color: var(--mut); text-transform: uppercase; letter-spacing: .06em; }
.rd-dry-parin {
  width: 108px; min-height: 44px; padding: 6px 10px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--card-2); color: var(--ink); font: 700 26px/1 "Barlow Condensed", "Arial Narrow", sans-serif;
  font-variant-numeric: tabular-nums; text-align: center;
}
.rd-dry-parin[readonly] { background: var(--bg); color: var(--mut); }
.rd-dry-parin:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
.rd-dry-parrow .rd-why { flex: 1 1 140px; margin: 0; }
.rd-dry-nopar, .rd-dry-hint { margin: 0; font-size: 13px; color: var(--mut); }
.rd-pips { display: flex; gap: 5px; }
.rd-pips i { flex: 1 1 0; height: 34px; border-radius: 7px; background: var(--line-2); border: 1px solid var(--line); }
.rd-pips i.on { background: var(--green-dk); border-color: var(--green-dk); }
.rd-pips i.miss { background: var(--warn-bg); border-color: var(--warn-line); }
.rd-pips i.now { background: var(--card); border: 2px solid var(--accent); }
.rd-dry-calls { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
/* 64px, not 44: the phone is on the floor and the hand is coming off a
   holster. Colour is never the only carrier — the words say which is which. */
.rd-call {
  min-height: 64px; border-radius: 10px; font: inherit; font-size: 16px; font-weight: 700; cursor: pointer;
  border: 1px solid var(--line); background: var(--card-2); color: var(--ink);
}
.rd-call.made { border-color: var(--green-dk); color: var(--green-dk); background: color-mix(in srgb, var(--green) 14%, var(--card)); }
.rd-call.miss { border-color: var(--warn-line); color: var(--warn); background: var(--warn-bg); }
.rd-call:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
.rd-call:active { transform: translateY(1px); }
.rd-dry-row { display: flex; flex-wrap: wrap; gap: 8px; }
.rd-dry-row .btn { flex: 1 1 auto; }
.rd-dry-sets { display: flex; flex-wrap: wrap; gap: 6px 12px; font-size: 12.5px; color: var(--mut); font-variant-numeric: tabular-nums; }
.rd-watch { border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; background: var(--card); }
.rd-watch-list { margin: 6px 0 0; padding-left: 18px; font-size: 13px; line-height: 1.45; }
.rd-watch-list li + li { margin-top: 4px; }
.rd-flags { display: flex; flex-direction: column; gap: 8px; }
.rd-flag {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; background: var(--card); margin-bottom: 8px;
}
.rd-flag > div { min-width: 0; flex: 1 1 auto; }
.rd-flag .rd-chips { margin: 0; flex: none; }
.rd-tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin-bottom: 4px; }
.rd-tile { border: 1px solid var(--line); border-radius: 12px; background: var(--card); padding: 10px 11px; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rd-tl { font-size: 11.5px; color: var(--mut); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rd-tv { font: 700 25px/1.1 "Barlow Condensed", "Arial Narrow", sans-serif; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.rd-tv u { text-decoration: none; font-size: 13px; font-weight: 600; color: var(--mut); margin-left: 2px; }
.rd-ts { font-size: 10.5px; color: var(--mut); }

/* ── The what-if sheet (owner-directed 2026-09-07; whatif-page.js) ──────────
   Every value is a token; the shapes are the hub's rows, the profile's tiles
   and the verdict's band — nothing invented here. */
.wi-verdict { border: 1px solid var(--line); border-radius: 12px; background: var(--card); padding: 11px 13px; display: flex; gap: 10px; align-items: center; }
.wi-verdict.accent { border-color: var(--accent); }
.wi-verdict .wi-band { width: 38px; height: 38px; border-radius: 9px; background: var(--mut); display: grid; place-items: center; font: 700 18px/1 "Barlow Condensed", "Arial Narrow", sans-serif; color: var(--card); flex: none; }
.wi-verdict.accent .wi-band { background: var(--accent); color: var(--on-fill); }
.wi-verdict > span:last-child { font-size: 12.5px; color: var(--mut); font-variant-numeric: tabular-nums; min-width: 0; }
.wi-verdict b { display: block; font-size: 14px; color: var(--ink); }
.wi-verdict.accent b { color: var(--accent); }
.wi-arrow { text-align: center; color: var(--mut); font-size: 18px; margin: 6px 0; line-height: 1; }
.wi-sech { display: flex; align-items: baseline; gap: 8px; margin: 16px 0 8px; font: 600 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .13em; text-transform: uppercase; color: var(--mut); }
.wi-sech .free { margin-left: auto; letter-spacing: .06em; color: var(--green-dk); }
.wi-sech .paid { margin-left: auto; letter-spacing: .06em; color: var(--accent); }
.wi-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.wi-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.wi-field > span { font-size: 11.5px; color: var(--mut); font-weight: 600; }
.wi-field input { width: 100%; min-height: 44px; }
.wi-rows .wi-row { gap: 10px; }
.wi-row .wi-in { width: 92px; flex: none; min-height: 44px; text-align: right; }
.wi-drill { border: 1px solid var(--line); border-radius: 12px; background: var(--card); padding: 11px 13px; margin-bottom: 8px; }
.wi-drill.changed { border-color: var(--accent); }
.wi-drill > b { display: block; font-size: 14px; margin-bottom: 4px; }
.wi-ln { display: flex; gap: 8px; align-items: flex-end; margin-top: 6px; }
.wi-logged { flex: 1; min-width: 0; border: 1px solid var(--line); border-radius: 9px; background: var(--card-2); min-height: 44px; padding: 6px 10px; display: flex; flex-direction: column; justify-content: center; }
.wi-logged small { font-size: 10.5px; color: var(--mut); }
.wi-logged b { font-size: 15px; font-variant-numeric: tabular-nums; }
.wi-ln .wi-field { flex: 1; }
.wi-arrow-h { color: var(--mut); font-size: 13px; flex: none; padding-bottom: 14px; }
.wi-bar { font-size: 12px; margin: 8px 0 0; color: var(--mut); font-variant-numeric: tabular-nums; }
.wi-bar b { color: var(--green-dk); }
.wi-words { border-top: 1px solid var(--line); margin-top: 16px; padding-top: 4px; }
.wi-headline { font-size: 14.5px; line-height: 1.5; margin: 0 0 10px; }
.wi-pri { margin: 0 0 10px; padding-left: 20px; }
.wi-pri li { margin-bottom: 8px; font-size: 13.5px; }
.wi-pri li b, .wi-pri li span { display: block; }
.wi-note { font-size: 13px; margin: 0 0 6px; color: var(--mut); }
.wi-note b { color: var(--ink); }
.wi-foot { border-top: 1px solid var(--line-2); margin-top: 16px; padding-top: 12px; }
.hist-legend-line { margin: 0 0 10px; font-size: 12.5px; }

/* ── Coach grants (#164): one editor drawer between rows ─────────────────────
   The drawer is a SIBLING of its row inside `.rows`, never nested in the row
   button — a control inside a button is invalid and unclickable, the rule the
   drill-card ⤢ button already pays for. `.libtog` is reused rather than a
   second checkbox look invented; its first `margin-top` is dropped so the
   drawer opens flush under the row it belongs to. */
.gr-editor { padding: 2px 13px 14px; border-top: 1px solid var(--line-2); background: var(--card-2); }
/* The drawer hides with the `hidden` ATTRIBUTE, not the `.hidden` class, because
   it pairs with the row's aria-expanded. Stated explicitly for the reason
   `.tab-dot[hidden]` is: a rule that sets `display` on the element silently
   beats the attribute, and this file is one namespace with no build step. */
.gr-editor[hidden] { display: none; }
.gr-editor .libtog:first-of-type { margin-top: 12px; }
.gr-editor .libtog { background: var(--card); }
.gr-editor .fld { margin-top: 14px; }

/* ── Standards (#172) ────────────────────────────────────────────────────────
   The margin is the headline, so it is the one thing on an attempt row that
   carries colour. Green for slack, accent for short — NOT red: falling short
   of a published standard is a measurement, not an error, and the app's red
   is reserved for something being wrong. */
.st-card { margin: 0 0 14px; }

/* ── Admin > Stages (2026-09-14) ──────────────────────────────────────────
   `sg-`, not `st-`: Standards already owns eleven .st-* rules, and two
   unrelated features sharing a prefix is how a change to one silently
   restyles the other.

   The card exists to make a DISAGREEMENT visible — two independent reads of
   one classifier page that cannot both be right — so the disagreement is the
   only thing that gets colour. Everything else stays quiet. */
.sg-card { border: 1px solid var(--line-2); border-radius: 10px; padding: 12px; margin: 0 0 12px; }
.sg-card:last-child { margin-bottom: 0; }
.sg-card h3 { margin: 0 0 2px; font-size: 15px; }
.sg-sub { margin: 0 0 8px; font-size: 12.5px; }
.sg-one { font-size: 12.5px; margin: 0 0 8px; }
/* Amber, and the whole card edged, because a card that disagrees is the one
   thing on this page anybody came to find. */
.sg-card-clash { border-color: var(--warn-line); background: var(--warn-bg); }
.sg-clash { color: var(--warn); }
.sg-row-clash td, .sg-row-clash th { background: var(--warn-bg); }

/* Tables scroll rather than squeezing: a card with six reads is wider than a
   phone, and the page must never scroll sideways as a whole. */
.sg-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; margin: 0 0 8px; }
.sg-table { border-collapse: collapse; font-size: 12.5px; min-width: 100%; }
.sg-table th, .sg-table td { padding: 5px 9px; text-align: left; white-space: nowrap; border-bottom: 1px solid var(--line-2); }
.sg-table thead th { font-weight: 600; color: var(--mut); font-size: 11.5px; }
.sg-table tbody th { font-weight: 650; }
.sg-table td { font-variant-numeric: tabular-nums; }

.sg-flags { margin: 6px 0 0; padding-left: 18px; font-size: 12.5px; }
.sg-flags li { margin: 2px 0; }
/* The app's own working, folded away: it is the reason the audit trail is here
   and not on the bay card, so it should not shout on this one either. */
.sg-basis { margin-top: 8px; font-size: 12.5px; }
.sg-basis summary { cursor: pointer; color: var(--mut); }
.sg-basis p { margin: 6px 0 0; }
.st-card:last-child { margin-bottom: 0; }
.st-attempt .rt b { font-size: 17px; font-variant-numeric: tabular-nums; }
.st-margin {
  flex: none; font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums;
  padding: 4px 9px; border-radius: 8px; border: 1px solid var(--line); background: var(--card-2);
}
.st-margin.st-clear { color: var(--green-dk); border-color: var(--green-dk); }
.st-margin.st-short { color: var(--accent); border-color: var(--accent); }
.st-margin.st-edge { color: var(--mut); }
.st-what { margin-top: 11px; }
.st-what > summary { cursor: pointer; font-size: 13px; font-weight: 600; color: var(--mut); }
.st-what p { font-size: 13px; line-height: 1.5; margin: 9px 0 0; }
.st-what .st-src { font-size: 12px; }

/* ── The Standards SHELL (owner "build it", 2026-09-16) ──────────────────
   The plan and range-day shells' shapes on a test. Where a shape already
   has a rule — the strip, the folds, the tiles, the echo, the whole-screen
   layer — the Standards selector is ADDED TO THAT RULE below rather than
   copied here, so one rule draws both and neither drifts; only what is the
   shelf's own (the rows) lives in this block. `st-`, as before. */
.st-strip { border: 1px solid var(--line); background: var(--card-2); border-radius: 12px; padding: 9px 12px; margin: 0 0 10px; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.st-strip b { font: 700 17px/1.15 "Barlow Condensed", "Arial Narrow", sans-serif; letter-spacing: .01em; font-variant-numeric: tabular-nums; color: var(--ink); }
.st-strip b .st-clear { color: var(--green-dk); }
.st-strip b .st-short { color: var(--accent); }
.st-strip span { font-size: 12.5px; color: var(--mut); line-height: 1.4; }
/* The shelf: one row per standard, a button the width of the card. */
.st-rows { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: 14px; background: var(--card); overflow: hidden; }
.st-row {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: 64px; padding: 12px 14px; text-align: left;
  border: 0; border-top: 1px solid var(--line-2); background: none; color: var(--ink); font: inherit; cursor: pointer;
}
.st-row:first-child { border-top: 0; }
.st-row:hover { background: var(--card-2); }
.st-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.st-row-l { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.st-row-t { font-size: 15px; font-weight: 700; }
.st-row-s { font-size: 12.5px; color: var(--mut); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.st-row-r { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 3px; }
.st-row-d { font-size: 11.5px; color: var(--mut); white-space: nowrap; }
.st-row-go { flex: none; width: 18px; height: 18px; color: var(--mut); }
.st-foot { margin: 10px 0 0; font-size: 12.5px; line-height: 1.45; }
/* The card's body scrolls under the slim bar; the strip and the segments
   lead it. */
.st-full-body { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 12px 14px calc(14px + env(safe-area-inset-bottom)); }
/* The slim bar's title sits beside the back button, one line, clipped
   before it wraps — centred it wrapped to two lines at 390 (measured). */
.st-full .rd-full-mid { text-align: left; }
.st-full .rd-full-mid b { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.st-full-spacer { flex: none; width: 0; }
.st-strip-pinned { margin-bottom: 10px; }
.segs.st-segs { margin: 0 0 10px; }
.st-purpose { margin: 0 0 10px; font-size: 12.5px; }
/* The picture on the card: the course of fire as a distance ladder, its
   facts as pills laid over (the diagram labels' idiom, aria-hidden because
   the strip and the bar read them). */
.st-pic { position: relative; border: 1px solid var(--line); border-radius: 12px; background: var(--card); padding: 34px 8px 6px; margin: 0 0 10px; }
.st-pic-pills { position: absolute; top: 8px; left: 10px; right: 10px; display: flex; gap: 6px; flex-wrap: wrap; }
.st-pic-pills .dv-pill { margin-left: 0; }
.st-ladder { display: block; width: 100%; height: auto; }
.st-setup { margin: 0 0 10px; font-size: 13.5px; line-height: 1.5; }
.st-bar { display: flex; align-items: center; gap: 10px; border: 1px solid var(--line); border-radius: 12px; background: var(--card); padding: 10px 12px; margin: 0 0 10px; }
.st-bar-l { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.st-bar-l b { font-size: 14px; }
.st-bar-l .mut { font-size: 12.5px; }
.st-bar .dv-pill { margin-left: 0; }
.st-strings .row .rt b { font-variant-numeric: tabular-nums; }
.st-src { margin: 0; padding: 0 12px 10px; font-size: 13px; line-height: 1.5; }
.st-fold .rd-own, .st-fold .rows { margin: 0 10px 10px; }
/* Shoot: the ritual, then the door. */
.st-sec { margin: 0 0 10px; }
.st-sec h3 { margin: 0 0 4px; font-size: 15px; }
.st-sec > .mut { margin: 0 0 10px; font-size: 12.5px; line-height: 1.45; }
.st-sec .rtags { flex-direction: column; align-items: stretch; }
.st-tick { justify-content: flex-start; text-align: left; font: inherit; font-size: 13.5px; font-weight: 600; border-radius: 999px; }
.st-tick svg { width: 18px; height: 18px; flex: none; }
.st-tick[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.st-shoot { margin: 0 0 8px; }
/* Record: four tiles, the echo, the rows — each row a door. */
.st-attempt { width: 100%; text-align: left; border-left: 0; border-right: 0; border-bottom: 0; background: none; color: var(--ink); font: inherit; cursor: pointer; }
.st-attempt:first-child { border-top: 0; }
.st-attempt:hover { background: var(--card-2); }
.st-attempt:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* A test's session card says why there is no Add run, rather than just not
   having one — a control that vanishes silently reads as a bug (#172). */
.rd-stdcap { font-size: 12px; color: var(--mut); padding: 4px 2px; display: block; }
/* The cold ask, at the bench. A checkbox and not a toggle: it is an answer
   to a question, the same shape as the bar tick beside it. */
.rd-cold {
  display: flex; gap: 9px; align-items: flex-start; margin-top: 10px;
  padding: 10px 11px; border: 1px solid var(--line); border-radius: 10px; background: var(--card);
}
.rd-cold input { width: auto; margin: 2px 0 0; flex: none; }
.rd-cold b { display: block; font-size: 13px; }
.rd-cold small { display: block; color: var(--mut); font-size: 11.5px; margin-top: 2px; line-height: 1.45; }
/* A test's own facts on its picker row (#172). Two chips, not one: the
   attempts rule and the score are different kinds of fact, and the score is
   the longer sentence. */
.rd-std { border: 1px solid var(--accent); color: var(--accent); border-radius: 20px; padding: 2px 8px; font-size: 11px; font-weight: 600; }
.rd-std-bar { color: var(--mut); font-size: 11px; }
/* The passing score is one fact in two fields — a number and which metric it
   is a number OF — so they sit on one line rather than reading as two asks. */
.dr-std-pass { display: flex; gap: 8px; align-items: center; }
.dr-std-pass input { flex: 0 0 96px; }
.dr-std-pass select { flex: 1 1 auto; min-width: 0; }
/* A student's standard on the coach's screen: the MARGIN is the figure, so it
   is what carries colour. Same green/accent pair the Standards tab uses, so
   one number means one thing in both places. */
.co-std {
  flex: none; font: 700 13px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  font-variant-numeric: tabular-nums; padding: 5px 9px; border-radius: 7px;
  border: 1px solid var(--line); background: var(--card-2); color: var(--mut);
}
.co-std.met { color: var(--green-dk); border-color: var(--green-dk); }
.co-std.short { color: var(--accent); border-color: var(--accent); }
/* A standard on the curator's shelf list (#172). The accent, because this is
   the tag that changes how the drill is recorded — the challenge tags say
   what a drill feeds, this one says what KIND of thing it is. */
.dr-tags .std { border-color: var(--accent); color: var(--accent); }

/* ── The card view (owner-approved 2026-09-10, "Range Day Card Deck") ───────
   List ⇄ Cards under the day's header, then one drill per screen in a CSS
   scroll-snap column — the browser's own swipe, the keyboard's own arrows.
   The page is a little shorter than the deck so the next card PEEKS, which is
   what makes a swipe discoverable without a hint that has to be dismissed.
   Tokens only, so nothing here needs a dark-block entry. */
/* ── The day's strip and the drill's (range day & drills shell, 2026-09-16) ──
   One shape on the bench, the card, Record it and the wrap: a line of figures
   over a line of words, both the figures module's. Card ground and a
   hairline, the plan's own pinned strip made small; tokens only. */
.rd-strip { border: 1px solid var(--line); background: var(--card-2); border-radius: 12px; padding: 9px 12px; margin: 0 0 10px; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rd-strip b { font: 700 17px/1.15 "Barlow Condensed", "Arial Narrow", sans-serif; letter-spacing: .01em; font-variant-numeric: tabular-nums; color: var(--ink); }
.rd-strip span { font-size: 12.5px; color: var(--mut); line-height: 1.4; }
.rd-dcard .rd-strip { margin: 8px 0 10px; }
/* What a plan's drill is worth (owner, 2026-09-16 evening): one accent line
   at the foot of the card's strip — the plan's own priced figure, credited. */
.rd-strip .rd-strip-worth { color: var(--accent); font-weight: 600; }
.sheet.rd-rec .rd-strip { margin: 6px 8px 8px; }
/* Why this drill is on the list — the review's own sentence, surviving past
   Start as one accent line under the card's head. */
.rd-pick { margin: 0 0 8px; font-size: 12.5px; line-height: 1.4; color: var(--accent); }
/* The Purpose fold on a drill's words: the prose one tap down, its length on
   the summary so leaving it shut is a choice. */
.rd-fold, .st-fold { border: 1px solid var(--line); border-radius: 10px; background: var(--card-2); margin: 0 0 10px; }
.rd-fold > summary, .st-fold > summary { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 44px; padding: 0 12px; font-size: 13.5px; font-weight: 600; color: var(--ink); cursor: pointer; list-style: none; }
.rd-fold > summary::-webkit-details-marker, .st-fold > summary::-webkit-details-marker { display: none; }
.rd-fold > summary em, .st-fold > summary em { font-style: normal; font-weight: 500; color: var(--mut); font-size: 12.5px; }
.rd-fold > summary em::after, .st-fold > summary em::after { content: " ›"; }
.rd-fold[open] > summary em::after, .st-fold[open] > summary em::after { content: " ˅"; }
.rd-fold > .rd-why { padding: 0 12px 10px; margin: 0; }
/* The media folds (owner, 2026-09-16 evening): the doors and the film sit
   inside, inset like the purpose. */
.rd-fold-diag > .rd-setup-row, .rd-fold-watch > .rd-own { margin-left: 10px; margin-right: 10px; }
.rd-fold-diag > .rd-setup-row, .rd-fold-watch > .rd-own { margin-bottom: 10px; }
/* How it went: the six words, the plan review's chip shape (.rtag). */
.rd-tags { margin: 0 0 10px; }
/* Which rep the words are for (2026-09-16 evening): one small pip per rep,
   the tagged ones marked, the current one pressed. */
.rd-tagreps { display: inline-flex; gap: 6px; margin: 0 0 8px; }
.rd-tagrep { min-width: 34px; min-height: 34px; border: 1px solid var(--line); border-radius: 8px; background: var(--card-2); color: var(--mut); font: 600 13px/1 var(--font, system-ui); cursor: pointer; }
.rd-tagrep.has { color: var(--ink); }
.rd-tagrep[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); }
.rd-r-tags td { padding-top: 0; font-size: 12px; }
/* Setup ticks on the bench (owner, 2026-09-16 evening): this phone's ritual,
   the plan review's chip shape, under the drills and above the aisles door. */
.rd-setup-ticks { margin: 4px 0 12px; }
.rd-tags-l { display: block; font-size: 12px; color: var(--mut); margin: 0 0 6px; }
/* The wrap: four tiles in the dry wrap's tile shape, and the echo under them
   — an accent rule, the plan review's quote idiom. */
.rd-glance, .st-glance { margin: 0 0 10px; }
.rd-glance .rd-tile { padding: 8px 11px 7px; gap: 1px; }
.rd-glance .rd-tv { font-size: 21px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rd-glance .rd-ts { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The Standards card's four (2026-09-16): the same tile, the margin's colour
   on the Newest value — green for slack, accent for short, never red. */
.st-glance { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.st-tile { border: 1px solid var(--line); border-radius: 12px; background: var(--card); padding: 8px 11px 7px; display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.st-tl { font-size: 11.5px; color: var(--mut); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.st-tv { font: 700 21px/1.1 "Barlow Condensed", "Arial Narrow", sans-serif; letter-spacing: -.02em; font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.st-tv u { text-decoration: none; font-size: 12px; font-weight: 600; color: var(--mut); margin-left: 3px; }
.st-tv.st-clear { color: var(--green-dk); }
.st-tv.st-short { color: var(--accent); }
.st-ts { font-size: 10.5px; color: var(--mut); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* One row of four at 360 and up (owner, 2026-09-16 evening: the strip came
   back onto the wrap, and its 69px are paid for here rather than by Save
   sliding under the fold at 390 — the walk's line). Home's five-up strip
   is the precedent; the value drops to 17px to fit "0 of 1 met" in a 77px
   tile (measured: 19px clipped it), and the sub clips on the name, never
   on the figure. */
@media (min-width: 360px) {
  .rd-glance, .st-glance { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }
  .rd-glance .rd-tile, .st-tile { padding: 8px 6px 7px; }
  .rd-glance .rd-tv, .st-tv { font-size: 17px; }
}
.rd-echo, .st-echo { margin: 0 0 10px; padding: 7px 10px; border-left: 3px solid var(--accent); background: var(--card-2); border-radius: 0 8px 8px 0; font-size: 13.5px; line-height: 1.45; color: var(--ink); }
/* The Shoot door's answer when the Range day tab refuses it mid-day (shell
   review 2026-09-19). The echo's shape in the warn colour — it is a refusal,
   not a reading — and it renders directly under the button it answers, so it
   is qualified by its component rather than reusing a bare modifier (the
   `.home-report-state.ready` collision's rule). */
.st-refused { margin: 8px 0 0; padding: 7px 10px; border-left: 3px solid var(--warn); background: var(--card-2); border-radius: 0 8px 8px 0; font-size: 13.5px; line-height: 1.45; color: var(--ink); }
/* The report sheet's study: the picture, then the sentence. */
.rdr-study { margin: 0 0 12px; }
.rdr-study .rd-shot { margin: 0 0 6px; }
.rd-viewrow { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: -2px 0 10px; }
.rd-viewmeta { font-size: 12.5px; color: var(--mut); font-variant-numeric: tabular-nums; }
.segs.rd-viewsegs { margin: 0; flex: 0 0 auto; width: 184px; }
/* THE WHOLE SCREEN (owner, 2026-09-10). A fixed layer over the top bar and
   the tab bar — a screen of its own with one slim bar — because inside the
   page a long card had ~550px at 390x844 and here it has ~770. The top bar
   and tab bar are made inert and the page is locked behind it by the page
   module (`rd-full-on`). Tokens only; the column stays phone-width on a wide
   screen, where a 1200px card would be a poster, not a card. */
html.rd-full-on, html.rd-full-on body { overflow: hidden; }
/* The Standards card is the same layer (2026-09-16): `.st-full` rides every .rd-full rule. */
.rd-full {
  position: fixed; inset: 0; z-index: 50; display: flex; flex-direction: column;
  background: var(--bg); padding-top: env(safe-area-inset-top);
}
.rd-full > * { width: 100%; max-width: 640px; margin-inline: auto; }
.rd-full-bar {
  flex: none; display: flex; align-items: center; gap: 8px;
  padding: 8px max(12px, env(safe-area-inset-right)) 8px max(8px, env(safe-area-inset-left));
  border-bottom: 1px solid var(--line); background: var(--card);
}
.rd-full-back {
  flex: none; display: inline-flex; align-items: center; gap: 2px; min-height: 44px; padding: 0 10px 0 4px;
  border: 0; background: none; color: var(--accent); font: inherit; font-size: 15px; font-weight: 650; cursor: pointer; border-radius: 10px;
}
.rd-full-back svg { width: 20px; height: 20px; }
.rd-full-back:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.rd-full-mid { flex: 1; min-width: 0; text-align: center; line-height: 1.25; }
.rd-full-mid b { display: block; font-size: 15px; }
.rd-full-mid span { display: block; font-size: 12px; color: var(--mut); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rd-full-fin { flex: none; min-height: 44px; padding: 0 16px; }
.rd-full-msg {
  flex: none; width: calc(100% - 28px); max-width: 612px; margin: 8px auto 0; padding: 8px 12px; border-radius: 9px; font-size: 13px;
  background: var(--err-bg); border: 1px solid var(--err-line); color: var(--err);
}
.rd-full .rd-deckw { flex: 1; min-height: 0; padding: 10px 14px calc(6px + env(safe-area-inset-bottom)); }
.rd-deckw { position: relative; }
.rd-deck {
  position: relative; height: 100%; overflow-y: auto;
  scroll-snap-type: y mandatory; overscroll-behavior: contain; scrollbar-width: none;
  margin: 0 -2px; padding: 0 2px; border-radius: 16px;
}
.rd-deck::-webkit-scrollbar { display: none; }
.rd-deck:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A card taller than the deck is still one snap: the browser scrolls inside
   an oversized snap area before it snaps on, so a long setup is read, not
   clipped. */
.rd-page { min-height: calc(var(--rd-deck-h, 70vh) - 44px); scroll-snap-align: start; scroll-snap-stop: always; padding: 0 0 10px; display: flex; }
.rd-deck-tail { height: 44px; }
.rd-dcard {
  flex: 1; display: flex; flex-direction: column; min-width: 0;
  border: 1px solid var(--line); border-radius: 16px; background: var(--card);
  padding: 13px 15px 0; margin-right: 12px;
}
.rd-dcard.done { border-color: var(--green-dk); }
.rd-dc-kick { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; font-size: 11.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--mut); }
.rd-dc-kick .rd-dlogged { margin-left: 0; letter-spacing: 0; text-transform: none; font-size: 12.5px; }
.rd-dc-t { margin: 4px 0 2px; font-size: 21px; line-height: 1.15; letter-spacing: -.01em; text-wrap: balance; }
.rd-dc-body { flex: 1; }
/* Picture ⇄ Steps on the card (owner, 2026-09-16 evening). The segs are the
   view row's own width; the picture pane is the setup sheet's diagram at
   card width with the drill's own setup line under it — the words are the
   document, the drawing never replaces the sentence. */
.segs.rd-cpanes { margin: 0 0 10px; width: 184px; }
.rd-dc-pane { flex: 1; min-width: 0; }
/* The free what-if row on the card (owner, 2026-09-16 evening): a question
   with a number in it, the route's answer under it, the full sheet behind a
   text door. Card-2 ground like the strip; nothing here is a figure of its
   own. */
.rd-wi { border: 1px solid var(--line); background: var(--card-2); border-radius: 12px; padding: 9px 12px; margin: 0 0 10px; }
.rd-wi-ask { display: flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 600; color: var(--ink); }
.rd-wi-ask input { width: 84px; min-height: 40px; font-size: 16px; text-align: center; }
.rd-wi-out { margin: 6px 0 0; font-size: 12.5px; line-height: 1.45; color: var(--mut); min-height: 18px; }
.rd-wi-open { display: inline-block; margin-top: 6px; padding: 0; min-height: 44px; border: 0; background: none; font: inherit; font-size: 13px; font-weight: 600; color: var(--accent); cursor: pointer; }
.rd-dc-pic .rd-diag { margin: 0 0 10px; }
/* The numbers on the diagram (owner, 2026-09-16 evening): small pills in the
   drawing's top-left corner over the picture, the dry par top-right, on the
   card ground so they read over any line of the drawing. Tokens only; the
   green is the readable-on-background one, the warn is the app's. */
.rd-diag-wrap { position: relative; }
.rd-diag-nums { position: absolute; top: 8px; left: 8px; right: 8px; display: flex; flex-direction: column; align-items: flex-start; gap: 3px; pointer-events: none; }
.rd-dn { display: inline-block; font: 700 12.5px/1.3 "Barlow Condensed", "Arial Narrow", sans-serif; letter-spacing: .02em; padding: 1px 7px; border-radius: 6px; background: var(--card-2); border: 1px solid var(--line); color: var(--ink); font-variant-numeric: tabular-nums; }
.rd-dn-best { color: var(--green-dk); }
.rd-dn-last { color: var(--warn); }
.rd-dn-dry { position: absolute; top: 0; right: 0; color: var(--mut); }
.rd-dc-pic .rd-setup { font-size: 14px; line-height: 1.55; }
.rd-dc-body .rd-setup { font-size: 14px; line-height: 1.55; }
/* The film's still is a door, not the film: a wider crop on the card keeps
   the setup and Record it on the same screen as it. The player that replaces
   it on a tap is its own 16:9. */
.rd-dc-body .rd-own-face { aspect-ratio: 2 / 1; }
/* The one button, pinned to the bottom of its card while the card is on
   screen — sticky inside the deck's own scroller, so a long setup never puts
   Record it below the fold. */
.rd-dc-go {
  position: sticky; bottom: 0; margin: 12px -15px 0; padding: 10px 15px 13px;
  background: var(--card); border-top: 1px solid var(--line-2); border-radius: 0 0 16px 16px;
}
.rd-dc-go .btn { width: 100%; min-height: 48px; }
.rd-dots { position: absolute; right: 2px; top: 50%; transform: translateY(-50%); display: flex; flex-direction: column; gap: 4px; }
/* 24px tap target around a 6px mark: a dot is a jump, and a jump is a tap. */
.rd-dots button { width: 10px; height: 24px; padding: 0; border: 0; background: none; cursor: pointer; display: grid; place-items: center; }
.rd-dots button::before { content: ""; width: 6px; height: 6px; border-radius: 3px; background: var(--line); transition: height .2s ease, background .2s ease; }
.rd-dots button.lg::before { background: var(--green-dk); }
.rd-dots button[aria-current="true"]::before { height: 18px; background: var(--accent); }
.rd-dots button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-radius: 4px; }
@media (prefers-reduced-motion: reduce) { .rd-dots button::before { transition: none; } }

/* Record it: the .sheet idiom as a BOTTOM sheet over the card it came from —
   the card stays visible above it, which is the point of recording "on" it. */
.sheet.rd-rec {
  inset: auto 0 0 0; height: auto; max-height: 88%; border-radius: 18px 18px 0 0;
  background: var(--card); box-shadow: 0 -12px 34px rgba(0, 0, 0, .28);
}
.sheet.rd-rec .sheet-head { border-radius: 18px 18px 0 0; padding-top: 10px; }
.rd-rec-h { flex: 1; min-width: 0; text-align: center; }
.rd-rec-h h2 { margin: 0; font-size: 16.5px; font-weight: 700; }
.rd-rec-h h2:focus { outline: none; }
.rd-rec-sub { font-size: 12px; color: var(--mut); margin-top: 1px; }
.sheet.rd-rec .sheet-body { padding: 4px 4px 0; }
/* The card inside its sheet is the sheet's body, not a card in a card. */
.rd-drill.rd-drill-rec { border: 0; margin: 0; background: none; border-radius: 0; }
.rd-drill.rd-drill-rec .rd-dhead { padding-top: 6px; }
.rd-rec-foot {
  position: sticky; bottom: 0; padding: 10px 12px calc(12px + env(safe-area-inset-bottom));
  background: var(--card); border-top: 1px solid var(--line-2);
}
.rd-rec-foot .btn { width: 100%; min-height: 48px; }

/* ── Build my range day (owner-ratified 2026-09-10) ── the sheet drawn from the
   Home payload's `review`. Tones are the app's semantic ones: err = work in
   progress, warn = a number to trust less, acc = a pointer already paid for,
   ok = nothing asking. */
#review-sheet .rv { display: grid; gap: 12px; }
#review-sheet .rv-head { display: grid; gap: 4px; }
#review-sheet .rv-title {
  margin: 0; font-family: "Barlow Condensed", "Arial Narrow", -apple-system, sans-serif;
  font-weight: 700; font-size: 30px; line-height: 1.02; letter-spacing: .01em; text-wrap: balance;
}
#review-sheet .rv-head p { margin: 0; font-size: 13px; }
#review-sheet .rv-lbl { font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--mut); }
#review-sheet .rv-five { background: var(--card); border: 1px solid var(--line); border-radius: 14px; }
#review-sheet .rv-row { display: grid; grid-template-columns: 1fr auto; gap: 2px 10px; padding: 9px 12px; border-top: 1px solid var(--line-2); }
#review-sheet .rv-row:first-child { border-top: 0; }
#review-sheet .rv-nm { font-size: 14px; font-weight: 600; }
#review-sheet .rv-val { margin-left: 6px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-variant-numeric: tabular-nums; }
#review-sheet .rv-fact { grid-column: 1 / -1; font-size: 12px; line-height: 1.35; color: var(--mut); }
#review-sheet .rv-chip { align-self: center; font-size: 11px; font-weight: 700; letter-spacing: .03em; border-radius: 999px; padding: 2px 8px; border: 1px solid transparent; white-space: nowrap; }
#review-sheet .rv-ok { color: var(--green-dk); border-color: var(--line); }
#review-sheet .rv-warn { color: var(--warn); background: var(--warn-bg); border-color: var(--warn-line); }
#review-sheet .rv-err { color: var(--err); background: var(--err-bg); border-color: var(--err-line); }
#review-sheet .rv-acc { color: var(--accent-dk); background: var(--accent-soft); }
#review-sheet .rv-drills { display: grid; gap: 8px; }
#review-sheet .rv-drill { background: var(--card); border: 1px solid var(--line); border-radius: 14px; padding: 11px 12px; display: grid; gap: 4px; }
#review-sheet .rv-drill-h { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
#review-sheet .rv-drill-h b { font-size: 14.5px; line-height: 1.25; }
#review-sheet .rv-drill-h span { font-size: 12px; color: var(--mut); white-space: nowrap; }
#review-sheet .rv-why { margin: 0; font-size: 13px; line-height: 1.4; }
#review-sheet .rv-quiet { margin: 0; padding: 14px; font-size: 13px; line-height: 1.45; color: var(--mut); background: var(--card); border: 1px dashed var(--line); border-radius: 14px; }
#review-sheet .rv-aside { display: grid; gap: 4px; font-size: 12.5px; line-height: 1.4; color: var(--mut); }
#review-sheet .rv-aside b { color: var(--ink); font-weight: 600; }
#review-sheet .rv-cta { display: grid; gap: 8px; margin-top: 4px; }
#review-sheet .rv-cta .btn { width: 100%; }

/* Hold and Review (owner-ratified 2026-09-11): the Coach tab's "Targets to
   check". The overlay colours are FIXED, not tokens, for the shot-timer LCD's
   reason: they are drawn over a photograph of tan cardboard and black holes,
   which is the same object in both themes, and a ring has to read against
   that picture rather than against the page. Cyan rings, a yellow outline and
   a magenta A zone are the three hues farthest from cardboard and from each
   other. */
.co-reads { margin: 0 0 18px; padding: 0 0 14px; border-bottom: 1px solid var(--line); }
.co-reads h3 { font-size: 15px; font-weight: 700; margin: 0 0 6px; display: flex; gap: 8px; align-items: baseline; }
.tr-photo { margin: 10px 0 8px; max-width: 100%; }
/* The SVG covers the IMAGE, never the caption under it: an overlay sized to
   the figure stretched every ring down by the caption's height (caught in the
   first browser check, 2026-09-11). */
.tr-frame { position: relative; line-height: 0; }
.tr-frame img { display: block; width: 100%; height: auto; border-radius: 10px; background: var(--card-2); }
.tr-svg { position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; }
.tr-hit { fill: none; stroke: #00e5ff; }
.tr-body { fill: none; stroke: #ffd400; }
.tr-azone { fill: none; stroke: #ff3df5; }
.tr-photo figcaption { font-size: 12px; line-height: 1.5; margin-top: 6px; }
.tr-key { display: inline-block; width: 10px; height: 10px; border-radius: 50%; border: 2px solid #00e5ff; vertical-align: -1px; }
.tr-key-body { border-radius: 2px; border-color: #ffd400; }
.tr-key-az { border-radius: 2px; border-style: dashed; border-color: #ff3df5; }
.tr-doubt { color: var(--warn); background: var(--warn-bg); border: 1px solid var(--warn-line); border-radius: 8px; padding: 8px 10px; }
.rd-read-held { font-variant-numeric: normal; }
.rd-read-held b { color: var(--ink); font-weight: 600; }
.rd-read-checked { color: var(--green-dk); }
.rdr-wait { margin: 8px 0 0; }

/* Admin > Stages: the incomplete-source banner. Louder than a flag list and
   above the verdict, because an entry holding page 1 of 4 makes every distance
   under it an estimate — the reads disagreeing is then the expected result,
   not the finding, and fixing the upload comes before reading the spread. */
.sg-source {
  margin: 0 0 .6rem;
  padding: .55rem .7rem;
  border-left: 3px solid var(--warn, #b45309);
  background: color-mix(in srgb, var(--warn, #b45309) 8%, transparent);
  border-radius: 0 4px 4px 0;
  line-height: 1.45;
}
.sg-source b { color: var(--warn, #b45309); }

/* A banner that is INFORMATION rather than a warning: what a plan could not
   know, because the document that would settle it does not exist for a match
   stage. Deliberately quieter than .warn — a shooter who sees amber on every
   plan stops reading amber. */
.banner.note {
  background: var(--card, #f6f7f9);
  border: 1px solid var(--line, #dcdfe4);
  color: var(--mut, #555);
}

/* Admin › Stages — the confirmed layout and its confirm form (2026-09-14). */
.sg-layout { margin-top: 10px; padding-top: 8px; border-top: 1px dashed var(--line-2); }
.sg-layout h4 { margin: 0 0 4px; font-size: 13.5px; }
.sg-nolay { margin: 0 0 8px; font-size: 12.5px; color: var(--warn); }
.sg-lay .sg-row { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 6px; align-items: center; }
.sg-lay .sg-row input[type="text"] { flex: 1 1 10em; min-width: 8em; }
.sg-lay .sg-trow { border: 1px solid var(--line-2); border-radius: 8px; padding: 6px 8px; margin: 0 0 8px; }
/* The footprint picture (2026-09-14): drawn from the record's own numbers,
   redrawn on every keystroke of the form above the rows it is drawn from. */
.sg-fp { margin: 0 0 8px; border: 1px solid var(--line-2); border-radius: 8px; overflow: hidden; background: var(--fp-bg, #f6f5f0); }
.sg-fp svg { display: block; width: 100%; height: auto; }
.sg-sight { font-size: 12px; }

/* ── Admin › Stages, the four screens (2026-09-15) ──────────────────────────
   One container, four screens, all drawn by stages-page.js. The list is the
   triage: three count tiles that double as filters, search, chips, a row per
   stage with a status dot and the finding as pills. Detail, editor and review
   share the same top bar (.sg-bar) and the editor and review share the same
   pinned bottom bar (.sg-pinbar), sitting just above the tab bar. */
.sg-head h2 { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
.sg-head h2 .count { color: var(--mut); font-size: 13px; font-weight: 500; font-variant-numeric: tabular-nums; }
.sg-head h2 .dv-pill { margin-left: auto; }
.sg-head p { margin: 0 0 14px; }
.sg-tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; margin: 0 0 10px; }
.sg-tile {
  display: flex; flex-direction: column; align-items: flex-start; gap: 1px; min-height: 52px; min-width: 0;
  padding: 7px 9px; border: 1px solid var(--line); border-radius: 10px; background: var(--card-2);
  font: inherit; color: var(--ink); cursor: pointer; text-align: left;
}
.sg-tile b { font-size: 20px; line-height: 1.1; font-variant-numeric: tabular-nums; }
.sg-tile span { font-size: 10.5px; font-weight: 600; color: var(--mut); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.sg-tile-disagree { border-color: transparent; background: var(--accent-soft); }
.sg-tile-disagree b, .sg-tile-disagree span { color: var(--accent-dk); }
.sg-tile-incomplete { border-color: var(--warn-line); background: var(--warn-bg); }
.sg-tile-incomplete b, .sg-tile-incomplete span { color: var(--warn); }
.sg-refile { display: block; width: 100%; text-align: left; font: inherit; cursor: pointer; margin: 0 0 12px; }
.sg-refile b { display: block; margin-bottom: 2px; }
.sg-pills { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; margin-top: 4px; }
.sg-pills .dv-pill { margin-left: 0; }
.dv-pill.clash { color: var(--accent-dk); background: var(--accent-soft); border-color: transparent; }
.sg-tile-confirmed b { color: var(--green-dk); }
.sg-tile[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--ring); border-color: var(--accent); }
.sg-search { position: relative; margin: 0 0 8px; }
.sg-search svg { position: absolute; left: 11px; top: 50%; width: 16px; height: 16px; margin-top: -8px; color: var(--mut); pointer-events: none; }
.sg-search input { padding-left: 34px; }
.sg-chips { display: flex; gap: 6px; overflow-x: auto; -webkit-overflow-scrolling: touch; padding-bottom: 4px; margin: 0 0 4px; scrollbar-width: none; }
.sg-chips::-webkit-scrollbar { display: none; }
.sg-chips .chip { min-height: 36px; flex: none; }
.sg-items { display: flex; flex-direction: column; }
.sg-item {
  display: flex; align-items: flex-start; gap: 12px; width: 100%; min-height: 56px; padding: 10px 0;
  border: 0; border-bottom: 1px solid var(--line-2); background: none; font: inherit; color: var(--ink);
  text-align: left; cursor: pointer;
}
.sg-item:last-child { border-bottom: 0; }
.sg-item:hover .sg-item-title b { color: var(--accent); }
.sg-dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 7px; flex: none; background: var(--line); }
.sg-dot-disagree { background: var(--accent); }
.sg-dot-incomplete { background: var(--warn); }
.sg-dot-confirmed { background: var(--green-dk); }
.sg-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.sg-item-title { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
.sg-item-title b { font-size: 14.5px; }
.sg-badges { display: flex; gap: 6px; flex-wrap: wrap; }
.sg-badges .dv-pill { margin-left: 0; }
.sg-item-meta { font-size: 12px; }
.sg-chev { width: 18px; height: 18px; margin-top: 8px; color: var(--mut); flex: none; }
.sg-chev svg { width: 18px; height: 18px; }
.sg-empty { padding: 18px 0; text-align: center; }
.dv-pill.warn { color: var(--warn); background: var(--warn-bg); border-color: var(--warn-line); }

/* The top bar every inner screen wears: a way back on the left, the name of
   the screen in the middle, the state on the right. */
.sg-bar { display: flex; align-items: center; gap: 8px; margin: -4px 0 10px; min-height: 44px; }
.sg-bar .dv-pill { margin-left: auto; }
.sg-bar-title { flex: 1; text-align: center; line-height: 1.2; display: flex; flex-direction: column; }
.sg-bar-title b { font-size: 14.5px; }
.sg-bar-title .mut { font-size: 12px; }
.sg-back {
  display: inline-flex; align-items: center; gap: 4px; min-height: 44px; padding: 6px 10px 6px 4px;
  border: 0; background: none; font: inherit; font-size: 13.5px; font-weight: 600; color: var(--accent); cursor: pointer;
}
.sg-back svg { width: 18px; height: 18px; }
.sg-title { display: flex; align-items: flex-start; gap: 10px; }
.sg-title > div { flex: 1; min-width: 0; }
.sg-title h2 { margin: 0; font-size: 20px; }
.sg-title .sg-name { font-weight: 500; color: var(--mut); }
.sg-title p { margin: 4px 0 0; }

.sg-actions { display: flex; gap: 8px; margin: 14px 0 0; }
.sg-actions .btn { flex: 1; gap: 6px; padding: 10px 12px; font-size: 13.5px; white-space: nowrap; }
.sg-actions .btn svg { width: 18px; height: 18px; }
.sg-segs { margin: 14px 0 12px; }
.sg-body { min-height: 80px; }
.sg-h3 { margin: 14px 0 6px; font-size: 15px; font-weight: 650; }
.sg-tblhead { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: 0 10px; }
.sg-tblhead .sg-h3 { white-space: nowrap; }
.sg-tblhead > span { font-size: 12px; }
.sg-tblhead .mut { font-size: 12px; }
.sg-foot { margin: 8px 0 0; font-size: 12px; }
.sg-center { text-align: center; margin-top: 16px; }
.sg-agree { color: var(--green-dk); }
.sg-settled { white-space: nowrap; }
.sg-banner { display: flex; gap: 10px; align-items: flex-start; }
.sg-banner svg { width: 18px; height: 18px; flex: none; margin-top: 2px; }
.sg-reads { list-style: none; margin: 0; padding: 0; }
.sg-read { padding: 10px 0; border-bottom: 1px solid var(--line-2); }
.sg-read:last-child { border-bottom: 0; }
.sg-read-h { display: flex; align-items: center; gap: 8px; }
.sg-read-h b { font-size: 13.5px; }
.sg-read-h .mut { font-size: 12px; }
.sg-read-h .dv-pill { margin-left: auto; }
.sg-read p { margin: 4px 0 0; font-size: 13px; color: var(--mut); }

/* The editor: the picture pinned under the top bar while the rows under it
   are typed, sections that fold, grid rows with headers, a pinned bar. */
.sg-note { margin: 0 0 10px; font-size: 12.5px; color: var(--accent-dk); background: var(--accent-soft); border-radius: 8px; padding: 8px 10px; }
.sg-pin { position: sticky; top: var(--topbar-h, 65px); z-index: 4; background: var(--card); padding: 0 0 8px; margin: 0 0 10px; border-bottom: 1px solid var(--line-2); }
.sg-pin .sg-fp { margin: 0; }
/* Pinned, so it must leave the rows room: a square drawing at full width
   would take the whole phone. Capped to a third of the viewport, centred. */
.sg-pin .sg-fp svg { width: auto; max-width: 100%; max-height: 34vh; margin: 0 auto; }
/* Dragging on the picture: no scroll while a finger is on a grab point. */
.sg-fp-drag { touch-action: none; user-select: none; -webkit-user-select: none; }
.sg-fp-drag .fp-handle:hover circle:first-child { stroke: var(--accent); stroke-dasharray: none; }
.sg-dragging { cursor: grabbing; }
.sg-dragging .fp-handle { cursor: grabbing; }

/* THE EDITOR ON A WIDE SCREEN (owner, 2026-09-15): main's 920px cap is right
   for reading a plan and wrong for a drawing with rows beside it. While the
   editor is open the page widens, the picture takes the left column at up to
   three quarters of the viewport, pinned, and the rows scroll on the right.
   Under 900px it is the phone layout above, unchanged. */
main.sg-wide { max-width: 1480px; }
@media (min-width: 900px) {
  main.sg-wide .sg-lay { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(360px, 1fr); gap: 0 24px; align-items: start; }
  main.sg-wide .sg-lay > .sg-bar, main.sg-wide .sg-lay > .sg-note, main.sg-wide .sg-lay > .attrib,
  main.sg-wide .sg-lay > [data-err], main.sg-wide .sg-lay > .sg-pinbar { grid-column: 1 / -1; }
  main.sg-wide .sg-lay > .sg-pin { grid-column: 1; border-bottom: 0; padding-bottom: 0; }
  main.sg-wide .sg-lay > .sg-lay-rows { grid-column: 2; min-width: 0; }
  main.sg-wide .sg-pin .sg-fp svg { max-height: 66vh; width: 100%; height: auto; }
  main.sg-wide .sg-pin .sg-fp { display: flex; justify-content: center; }
  main.sg-wide .sg-pin .sg-fp svg { width: auto; max-width: 100%; }
}
.sg-chip-sm { min-height: 28px; padding: 3px 9px; font-size: 11px; margin-right: 6px; vertical-align: middle; }
.sg-pin-foot { margin: 6px 0 0; font-size: 11.5px; }
.sg-sec { border: 1px solid var(--line); border-radius: 12px; background: var(--card); margin: 0 0 10px; overflow: hidden; }
.sg-sec-h { display: flex; align-items: center; gap: 8px; padding: 12px 14px; min-height: 48px; cursor: pointer; list-style: none; }
.sg-sec-h::-webkit-details-marker { display: none; }
.sg-sec-h b { font-size: 14px; white-space: nowrap; }
.sg-sec-h .mut { font-size: 12px; }
details.sg-sec > .sg-sec-h::after {
  content: ''; margin-left: auto; width: 8px; height: 8px; border-right: 2px solid var(--mut); border-bottom: 2px solid var(--mut);
  transform: rotate(45deg); transition: transform .12s; flex: none;
}
details.sg-sec[open] > .sg-sec-h::after { transform: rotate(-135deg); }
.sg-sec-b { padding: 0 14px 8px; border-top: 1px solid var(--line-2); }
.sg-sec-open .sg-sec-h { cursor: default; }
.sg-sec-open .sg-sec-h .sg-link { margin-left: auto; }
.sg-sec-warn { border-color: var(--warn-line); background: var(--warn-bg); }
.sg-sec-warn .sg-sec-h b, .sg-sec-warn .sg-flags { color: var(--warn); }
.sg-sec-src .sg-sec-h { cursor: default; }
.sg-src { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; }
.sg-src input { width: 52px; padding: 7px 8px; font-size: 13.5px; text-align: center; }
.sg-grid { display: grid; gap: 6px; align-items: center; padding: 6px 0; }
.sg-grid input { padding: 7px 10px; font-size: 13.5px; min-width: 0; }
.sg-grid-h { padding: 8px 0 0; font-size: 11px; font-weight: 600; color: var(--mut); }
.sg-grid-corner { grid-template-columns: 20px minmax(0, 1fr) minmax(0, 1fr) 36px; }
.sg-wall { padding: 6px 0; border-bottom: 1px solid var(--line-2); }
.sg-wall:last-child { border-bottom: 0; }
.sg-wall .sg-grid { padding: 2px 0; }
.sg-grid-wall1 { grid-template-columns: 56px minmax(0, 1fr) 36px; }
.sg-grid-wall2 { grid-template-columns: 34px minmax(0, 1fr) minmax(0, 1fr) 22px minmax(0, 1fr) minmax(0, 1fr); }
.sg-grid-wall2 .mut { font-size: 11.5px; text-align: right; }
/* The layout tab's targets, one sentence each. */
.sg-tgts { list-style: none; margin: 8px 0 0; padding: 0; }
.sg-tgt { display: grid; grid-template-columns: 40px minmax(0, 1fr); gap: 8px; padding: 8px 0; border-bottom: 1px solid var(--line-2); font-size: 13px; line-height: 1.45; }
.sg-tgt:last-child { border-bottom: 0; }
.sg-tgt-id { font-size: 14px; }
.sg-tgt-body { min-width: 0; overflow-wrap: anywhere; }
.sg-tgt-where { display: block; color: var(--mut); font-size: 12.5px; }
.sg-diff-f { display: block; }
.sg-diff-f .sg-old { font-size: 12.5px; }
.sg-grid-pos { grid-template-columns: minmax(0, 1.2fr) 52px 52px minmax(0, 1.4fr) 36px; }
.sg-grid-fact { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) 36px; }
.sg-del {
  width: 36px; height: 36px; border-radius: 8px; border: 0; background: transparent; color: var(--mut);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer; justify-self: end;
}
.sg-del:hover { color: var(--err); background: var(--err-bg); }
.sg-del svg { width: 18px; height: 18px; }
.sg-addrow { margin: 4px 0 0; }
.sg-add {
  display: inline-flex; align-items: center; gap: 6px; min-height: 40px; padding: 6px 8px; border: 0; background: transparent;
  color: var(--accent); font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
}
.sg-add svg { width: 16px; height: 16px; }
.sg-lay .sg-trow { border: 0; border-top: 1px solid var(--line-2); border-radius: 0; padding: 12px 0; margin: 0; }
.sg-lay .sg-trow:first-child { border-top: 0; }
.sg-trow-h { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
.sg-trow-h .sg-tid { width: 64px; font-weight: 700; padding: 7px 10px; }
.sg-trow-h .dv-pill { margin-left: 0; }
.sg-trow-h .sg-del { margin-left: auto; }
.sg-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.sg-fields label, .sg-fields > div, .sg-field { display: block; min-width: 0; }
.sg-field { margin-top: 10px; }
.sg-lay .lbl { display: block; font-size: 11.5px; font-weight: 600; color: var(--mut); margin: 0 0 4px; }
.sg-lay .lbl em { font-style: normal; color: var(--accent); font-weight: 500; }
.sg-lay .sg-fields input, .sg-lay .sg-fields select, .sg-lay .sg-field input { padding: 7px 10px; font-size: 13.5px; }
.sg-xy { display: flex; gap: 6px; }
.sg-lay input[aria-invalid="true"] { border-color: var(--err); box-shadow: 0 0 0 3px var(--err-bg); }
.sg-prob { margin: 6px 0 0; font-size: 12px; color: var(--err); }
.sg-lay .attrib { margin: 8px 0 0; font-size: 11.5px; color: var(--mut); }
.sg-pinbar {
  position: sticky; bottom: calc(64px + env(safe-area-inset-bottom)); z-index: 4;
  display: flex; align-items: center; gap: 10px; margin: 12px calc(-1 * clamp(16px, 3vw, 24px)) calc(-1 * clamp(16px, 3vw, 24px));
  padding: 10px clamp(16px, 3vw, 24px) 14px; background: color-mix(in srgb, var(--card) 94%, transparent);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); border-top: 1px solid var(--line);
  border-radius: 0 0 14px 14px;
}
.sg-pinbar .mut { flex: 1; font-size: 12px; }
@media (max-width: 359px) { .sg-pinbar { flex-wrap: wrap; } .sg-pinbar .mut { flex-basis: 100%; } }

/* The read review: one change per row, the confirmed value struck through
   beside the proposed one, a box to accept it. */
.sg-diffs { padding-top: 0; }
.sg-diff { display: grid; grid-template-columns: 72px minmax(0, 1fr) 28px; gap: 8px; align-items: center; padding: 10px 0; border-bottom: 1px solid var(--line-2); font-size: 13px; cursor: pointer; }
.sg-diff:last-child { border-bottom: 0; }
.sg-diff-l { display: flex; flex-direction: column; gap: 2px; }
.sg-diff-l .mut { font-size: 11px; }
.sg-diff-v { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sg-old { color: var(--mut); text-decoration: line-through; font-size: 12.5px; overflow-wrap: anywhere; }
.sg-diff-added .sg-old { display: none; }
.sg-new { font-weight: 600; overflow-wrap: anywhere; }
.sg-diff-removed .sg-new { color: var(--mut); font-weight: 500; }
.sg-diff input[type="checkbox"] { width: 22px; height: 22px; margin: 0; accent-color: var(--accent); justify-self: end; }
.sg-link { border: 0; background: none; font: inherit; font-size: 12.5px; font-weight: 600; color: var(--accent); cursor: pointer; padding: 6px 0; }

/* Managing the record (2026-09-15): the file door, the Record segment, the
   delete box. Readiness is a sentence under the title, green only when a
   shooter can actually select the stage. */
.sg-select { margin: 8px 0 0; font-size: 12.5px; font-weight: 600; color: var(--mut); }
.sg-select.ok { color: var(--green-dk); }
.sg-notice { margin: 12px 0 0; }
.sg-ready { border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; font-size: 13px; color: var(--mut); margin: 0 0 4px; }
.sg-ready b { color: var(--ink); }
.sg-ready.ok { border-color: var(--green-dk); }
.sg-ready.ok b { color: var(--green-dk); }
.sg-doc { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 13px; margin: 0 0 4px; }
.sg-doc .dv-pill { margin-left: 0; }
.sg-doc b { overflow-wrap: anywhere; }
.sg-kvs { display: flex; flex-direction: column; }
.sg-kv { display: grid; grid-template-columns: 120px minmax(0, 1fr); gap: 10px; padding: 6px 0; border-bottom: 1px solid var(--line-2); font-size: 13px; }
.sg-kv:last-child { border-bottom: 0; }
.sg-kv .mut { font-size: 12px; }
.sg-fields-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.sg-rec .sg-fields, .sg-rec .sg-field { margin-top: 10px; }
.sg-rec .lbl { display: block; font-size: 11.5px; font-weight: 600; color: var(--mut); margin: 0 0 4px; }
.sg-rec input, .sg-rec select { padding: 7px 10px; font-size: 13.5px; }
.sg-rec input[readonly] { color: var(--mut); background: var(--card-2); }
.sg-rec .rowbtns { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 8px 0 0; }
.sg-rec .rowbtns .btn svg { width: 18px; height: 18px; margin-right: 6px; }
.sg-rec .attrib { margin: 8px 0 0; font-size: 11.5px; color: var(--mut); }
.sg-body .rowbtns { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 8px 0 4px; }
.sg-body .rowbtns .btn svg { width: 18px; height: 18px; margin-right: 6px; }
.sg-head .rowbtns { margin: 0 0 14px; }
.sg-head .rowbtns .btn svg { width: 18px; height: 18px; margin-right: 6px; }
/* Destructive stays an OUTLINE on a screen (controls.test.js: the filled red
   lives only inside the confirm sheet). The app's own .btn.danger is that
   outline; the door and the final button both wear it. */
.sg-danger svg { width: 18px; height: 18px; margin-right: 6px; }
.sg-delbox { font-size: 13px; }
.sg-delbox .sg-fields { grid-template-columns: minmax(0, 1fr) auto; align-items: end; }
.sg-delbox input { border-color: var(--err-line); }

/* ── Your order on the build sheet (2026-09-15) ───────────────────────────── */
.order-sec { margin-top: 14px; }
.order-fp { border: 1px solid var(--line-2); border-radius: 10px; overflow: hidden; background: var(--fp-bg, #f6f5f0); }
.order-fp svg { display: block; width: 100%; height: auto; }
.order-fp .fp-hit:focus { outline: none; }
.order-fp .fp-hit:focus-visible circle { stroke: var(--accent); stroke-width: 3; }
.order-seq { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 8px; min-height: 30px; font-size: 13px; }
.ord-chip { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--line); background: var(--card-2); border-radius: 999px; padding: 4px 10px 4px 5px; font-size: 13.5px; font-weight: 600; font-variant-numeric: tabular-nums; }
.ord-chip .n { display: inline-grid; place-items: center; width: 20px; height: 20px; border-radius: 50%; background: var(--accent); color: #fff; font-size: 11.5px; font-weight: 700; }
.ord-chip .pos { color: var(--mut); font-weight: 500; font-size: 12px; }
.ord-chip.warn { border-color: var(--warn-line); background: var(--warn-bg); }
.ord-arrow { color: var(--mut); font-size: 12px; }
.ord-readout:empty { display: none; }
.ord-readout { margin-top: 10px; border: 1px solid var(--line-2); background: var(--card-2); border-radius: 10px; padding: 10px 12px; font-size: 13.5px; }
.ord-readout .route { font-family: "Barlow Condensed", "Arial Narrow", sans-serif; font-weight: 700; font-size: 22px; line-height: 1.1; display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.ord-readout .route .ft { font-family: inherit; font-size: 13.5px; font-weight: 600; color: var(--mut); }
.ord-readout .verdict { margin-top: 4px; font-weight: 600; }
.ord-readout.warn { border-color: var(--warn-line); background: var(--warn-bg); }
.ord-readout.warn .verdict { color: var(--warn); }
.ord-readout.ok .verdict { color: var(--ok, #2f7a3d); }


/* ── WALK, REMINDERS AND THE LOG (the plan & review shell round, 2026-09-15)
   ────────────────────────────────────────────────────────────────────────────
   Four intent segments, and three of them needed surfaces the plan view did
   not have: a picture with a walk drawn on it, one position per screen, a
   checklist, and three numbers typed once. Everything below is tokens — the
   drawing's own colours live in footprint.js, which renders for the admin
   page and a document as well as for this one. */
.walk-top { display: flex; align-items: center; gap: 8px; margin: 12px 0 10px; }
.walk-toggle { flex: 1; margin: 0; max-width: 200px; }
.walk-toggle button { min-height: 38px; font-size: 12.5px; }
.chip-btn {
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--ink);
  background: var(--card-2); border: 1px solid var(--line); border-radius: 999px;
  padding: 0 12px; min-height: 38px; cursor: pointer;
}
.chip-btn:hover { border-color: var(--accent); color: var(--accent); }
.fp-wrap { border: 1px solid var(--line-2); border-radius: 10px; overflow: hidden; background: var(--fp-bg, #f6f5f0); }
.fp-wrap svg { display: block; width: 100%; height: auto; }
.fp-wrap .fp-hit:focus { outline: none; }
.fp-wrap .fp-hit:focus-visible circle { stroke: var(--accent); stroke-width: 3; }
.fp-small { max-height: 190px; }
.fp-small svg { max-height: 190px; object-fit: contain; }
.hitpick { font-size: 12px; color: var(--mut); text-align: center; margin: 6px 0 10px; }
/* The position strip is a row of BUTTONS, so every one is a 44px target and
   lands on that position's own screen — the picture and Steps ask the same
   question and must not need two different taps to answer it. */
.pos-strip { display: flex; gap: 6px; flex-wrap: wrap; }
.pos-chip {
  flex: 1 1 92px; min-width: 0; text-align: left; font: inherit; cursor: pointer;
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  padding: 8px 10px; min-height: 44px; color: var(--mut); font-size: 12px;
}
.pos-chip b { display: block; color: var(--ink); font-size: 13.5px; margin-bottom: 2px; }
.pos-chip span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pos-chip.now { border-color: var(--accent); }
.pos-chip:hover { border-color: var(--accent); }
.wdots { display: flex; gap: 6px; justify-content: center; margin: 4px 0 10px; }
.wdot {
  width: 22px; height: 22px; padding: 0; border: 0; background: none; cursor: pointer;
  display: grid; place-items: center;
}
.wdot::after { content: ''; width: 7px; height: 7px; border-radius: 50%; background: var(--line); }
.wdot.done::after { background: var(--ok, #2f7a3d); }
.wdot.on::after { background: var(--accent); width: 9px; height: 9px; }
.walk-card { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 12px 14px; box-shadow: var(--shadow-sm); }
.walk-card h3 { margin: 10px 0 2px; font-size: 18px; }
.walk-card h3 .mut { font-size: 13px; font-weight: 400; }
.walk-rds { margin: 0 0 8px; color: var(--mut); font-size: 12.5px; font-variant-numeric: tabular-nums; }
.walk-card .row { display: flex; justify-content: space-between; gap: 10px; padding: 7px 0; border-top: 1px solid var(--line-2); font-size: 13px; }
.walk-card .row span { color: var(--mut); flex: none; }
.walk-card .row b { text-align: right; font-weight: 600; }
.walk-nav { display: flex; gap: 8px; margin-top: 10px; }
.walk-nav .btn { flex: 1; min-height: 44px; }
.walk-caveat { margin: 10px 2px 0; font-size: 12.5px; }
/* The side caveat (G1 b2, 2026-09-22): a muted caveat with the accent on its
   edge, because it is the one line on these panes that asks the shooter to
   check something on the stage. Tokens only — no dark twin needed. */
.side-caveat { border-left: 3px solid var(--accent); padding-left: 9px; }
.card-rows { margin-top: 10px; border: 1px solid var(--line-2); background: var(--card-2); border-radius: 10px; padding: 4px 12px; }
.card-rows .row { display: flex; justify-content: space-between; gap: 10px; padding: 8px 0; border-top: 1px solid var(--line-2); font-size: 13px; }
.card-rows .row:first-child { border-top: 0; }
.card-rows .row span { color: var(--mut); flex: none; }
.card-rows .row b { text-align: right; font-weight: 600; }
.eng tr.is-mine { background: var(--card-2); }
/* Reminders: a checklist, because a walkthrough is a rehearsal. 44px rows. */
.card-block { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 12px 14px; box-shadow: var(--shadow-sm); margin-bottom: 12px; }
.card-block h3 { margin: 0 0 6px; font-size: 15px; }
.wcheck { display: flex; gap: 10px; align-items: flex-start; padding: 9px 0; border-top: 1px solid var(--line-2); font-size: 13.5px; cursor: pointer; min-height: 44px; }
.wcheck:first-of-type { border-top: 0; }
.wcheck input { width: 20px; height: 20px; flex: none; margin-top: 2px; accent-color: var(--accent); }
.wcheck input:checked + span { color: var(--mut); text-decoration: line-through; }
/* The log: three numbers in one row, typed once. They stay side by side to
   320px — at 13px the labels are Time / Points / Penalties and the fields are
   the widest thing in the cell. */
.rb-log { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin: 6px 0 0; }
.rb-cell { background: var(--card-2); border: 1px solid var(--line); border-radius: 10px; padding: 8px 9px; min-width: 0; }
.rb-cell .fld { margin: 0 0 4px; font-size: 11.5px; }
.rb-cell input {
  width: 100%; min-width: 0; font: inherit; font-size: 16px; font-weight: 700;
  font-variant-numeric: tabular-nums; padding: 6px 4px; min-height: 44px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--card); color: var(--ink);
}
.rb-unit { display: block; margin-top: 3px; font-size: 11px; color: var(--mut); }
.rtags { display: flex; flex-wrap: wrap; gap: 6px; }
.rtag {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  border: 1px solid var(--line); background: var(--card-2); border-radius: 10px;
  padding: 0 11px; min-height: 44px; font-size: 12.5px; color: var(--mut);
}
.rtag input { width: 18px; height: 18px; flex: none; accent-color: var(--accent); }
.rtag:has(input:checked) { border-color: var(--accent); color: var(--ink); font-weight: 600; }
.tag-echo { margin-top: 10px; border-left: 2px solid var(--line); padding-left: 10px; font-size: 13px; }
.tag-echo p { margin: 4px 0; }
/* Planned vs ran (round two of the plan & review shell, 2026-09-15): four
   tiles in the strip's units, the sentences under them, and the run drawn on
   the plan's own picture one toggle away. Tokens only — the marks' red lives
   in footprint.js with the rest of the drawing's colours. Two columns hold
   to 320px because a tile is a label, one number and one short line. */
.rv-glance { margin: 0 0 18px; padding-bottom: 14px; border-bottom: 1px solid var(--line-2); }
.rv-glance .walk-top { margin: 6px 0 10px; }
.gl-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.gl-tile { background: var(--card-2); border: 1px solid var(--line); border-radius: 10px; padding: 9px 10px; min-width: 0; }
.gl-tile .gl-l { display: block; font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--mut); }
.gl-tile b { display: block; margin-top: 2px; font-size: 18px; font-weight: 750; line-height: 1.2; letter-spacing: -.01em; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.gl-tile.gl-said b { font-size: 13.5px; font-weight: 650; }
.gl-tile .gl-s { display: block; margin-top: 3px; font-size: 12px; color: var(--mut); }
.gl-s.gl-slower { color: var(--warn); font-weight: 600; }
.gl-s.gl-faster, .gl-s.gl-inside { color: var(--green-dk); font-weight: 600; }
.gl-says { margin: 12px 2px 0; font-size: 13.5px; line-height: 1.5; }
.gl-says p { margin: 0 0 6px; }
.gl-next { margin: 8px 2px 0; font-size: 13px; color: var(--mut); }
.gl-doors { margin: 12px 0 0 !important; }
.gl-mark { color: var(--err); }
/* Measured at 320: "12.8–13.7 s" at 18px wraps inside a 125px tile (43px
   tall against 22 for its neighbours); at 16px it holds one line. */
@media (max-width: 359px) { .gl-tile b { font-size: 16px; } }


/* ── YOUR STANDING (owner-ratified 2026-09-17; the look is the second pass,
   taken from the mockup's artboards the same day) ────────────────────────────
   Its own namespace, `sd-`, deliberately: `st-` already belongs to the
   Standards tab (.st-row, .st-tile, .st-fold, .st-short, .st-src all exist),
   and one stylesheet with no build step is ONE namespace — the stray hairline
   under Home's first report row came from exactly this, a bare modifier
   colliding with one defined 700 lines away. Tokens only, no hex.

   THE DOCUMENT IS A DECK OF CARDS, not a flat scroll: every block is a
   rounded card with an uppercase eyebrow over it, so a reader meets one
   finding at a time instead of a wall of headed prose. */

/* ── THE METERS (decisions 1A and 6A, owner 2026-09-18) ──────────────────────
   One segment bar, three jobs: the five LEVELS, the nine countable SOURCES, and level
   0's answers. Which segments are lit is the module's answer (`meterOf`) and
   never a count made here; these rules choose a height and a colour.

   The twelve-chip strip came off Home with the round: measured, it wrapped to
   five rows at 390 and six at 320, and it was the largest thing in the section
   by area while being the least actionable thing in it. Its words are not lost
   — they are `sources[].chip`, which the What it read screen still prints. */
.sd-meter { display: flex; gap: 3px; width: 100%; }
.sd-meter .sd-seg { flex: 1 1 0; min-width: 0; height: 6px; border-radius: 3px; background: var(--line-2); }
.sd-meter .sd-seg.sd-on { background: var(--accent); }
/* THE COVERAGE METER READS AS "5 OF 9" FIRST — nine, because the two rows that
   are absent BY RULE left the count on 2026-09-19 and a meter that scored them
   could never fill. `coverageOf` counts FULL rows,
   so a thin one is not read for this meter's purposes and is grey like an
   absent one — what tells the two apart is the border, and what tells the
   reader WHY is the row on the What it read screen, which has space for it. */
.sd-meter.sd-cov-m { gap: 2px; }
.sd-meter.sd-cov-m .sd-seg { height: 15px; border-radius: 2px; }
.sd-meter.sd-cov-m .sd-seg.sd-sg-full { background: var(--accent); }
.sd-meter.sd-cov-m .sd-seg.sd-sg-thin { border: 1px solid var(--line); }
.sd-meter.sd-cov-m .sd-seg.sd-sg-absent { border: 1px dashed var(--line); }
.sd-meter.sd-ans-m .sd-seg { height: 7px; }

/* Home's standing section (owner, 2026-09-19: the drift signal and one slot).

   THE HEAD: one of two things leads, and standing.js says which. With a read
   bought, `.sd-subonly` drops the name to the muted line under the meter and
   the verdict below is the largest thing in the section; without one, the
   level's NAME is the lead and the level line sits beside it. */
#home-standing-sec .sd-name { font-size: 15px; font-weight: 700; line-height: 1.3; }
#home-standing-sec .sd-headrow { display: flex; align-items: baseline; gap: 8px; margin-top: 8px; }
#home-standing-sec .sd-headrow .sd-name { flex: 1 1 auto; min-width: 0; }
#home-standing-sec .sd-headrow .sd-lvl { flex: none; font-size: 11.5px; color: var(--mut); }
#home-standing-sec .sd-headrow.sd-subonly { margin-top: 6px; }
/* The empty lead slot is REMOVED rather than left at zero width: two flex
   items at `1 1 auto` split the row, which put the sub line in the middle. */
#home-standing-sec .sd-headrow.sd-subonly .sd-name { display: none; }
#home-standing-sec .sd-headrow.sd-subonly .sd-lvl { flex: 1 1 auto; min-width: 0; }
/* Air above the meter; the 13px side inset stays the card's, not overridden. */
#home-standing-sec .sd-meterwrap { margin-top: 4px; }

/* THE KEPT READ. A plain block door, not a bordered row: the bordered things
   in this section are the ones that carry a state (what has landed since, and
   the slot), and giving the lead a box of its own would put three boxes in a
   card that has one thing to say. The clamp is for the FALLBACK path — a read
   written before the verdict field leads with its paragraph, which runs to
   twelve hundred characters. */
#home-standing-sec .sd-kept { display: block; width: 100%; text-align: left; margin: 11px 0 0;
  background: none; border: 0; padding: 0; color: inherit; cursor: pointer; }
#home-standing-sec .sd-kept-lead { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden; font-size: 15px; font-weight: 650; line-height: 1.4; }
#home-standing-sec .sd-kept-meta { display: flex; align-items: center; gap: 6px; margin-top: 6px; }
#home-standing-sec .sd-kept-w { font-size: 11px; color: var(--mut); }
/* The age, in `agoWords`' vocabulary. 11px and not the mockup's 9.5:
   `test/controls.test.js` bans 9.5 outright and 11px is this app's floor. */
#home-standing-sec .sd-age { font-size: 11px; color: var(--mut); border: 1px solid var(--line);
  border-radius: 999px; padding: 1px 7px; white-space: nowrap; }
.home-sec-note.sd-new { color: var(--warn); }

/* WHAT HAS LANDED SINCE, a row of its own. The dot is the only mark on it:
   this row states a count and never a verdict, so it carries the accent
   without carrying the alert's colour. */
/* NO `width: 100%` ON A DIRECT CHILD OF `.home-sec`. That card insets every
   child 13px a side, so a box told to be 100% wide overflows it by 26 — which
   is what the level meter and the coverage row had been doing since
   2026-09-18, on every persona, with nothing measuring it (found 2026-09-19 by
   driving the seeded app; act 13 asserts against it now). These are
   block-level flex boxes and fill the line without being told to. */
#home-standing-sec .sd-meter { width: auto; }
.sd-sincerow, .sd-slot { display: flex; align-items: center; gap: 9px; text-align: left;
  background: var(--card-2); border: 1px solid var(--line); border-radius: 11px;
  padding: 10px 11px; margin: 11px 0 0; color: inherit; cursor: pointer; min-height: 44px; }
.sd-since-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
.sd-since-t { flex: 1 1 auto; min-width: 0; font-size: 11.5px; line-height: 1.4; }

/* THE SLOT. One row, three drawn states, and the state is the document's.
   `align-items` differs for the alert because its sentence runs to two lines
   beside a mark that belongs at the top of them. */
.sd-slot.sd-slot-alert { align-items: flex-start; border-color: var(--warn-line); background: var(--warn-bg); color: var(--warn); }
.sd-slot-mark { flex: none; display: flex; margin-top: 1px; }
.sd-slot-b { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 7px; }
.sd-slot-t { font-size: 11.5px; line-height: 1.45; }
.sd-slot-p { display: flex; align-items: center; gap: 8px; }
.sd-slot-pl { flex: 1 1 auto; min-width: 0; font-size: 11px; color: var(--mut); }
.sd-meter.sd-gate-m { flex: 0 0 54px; width: 54px; gap: 3px; }
.sd-meter.sd-gate-m .sd-seg { height: 5px; }
.sd-slot-go { flex: none; font-size: 11.5px; color: var(--accent); white-space: nowrap; }
.sd-slot.sd-slot-alert .sd-slot-go { color: var(--warn); }

/* The inventory state keeps the 2026-09-18 row exactly: the meter first, its
   own sentence beside it, and the door named because "What it read" is not a
   screen a chevron could stand for. `.sd-covrow` is the sheet's copy of the
   same row and is untouched. */
.sd-covrow { display: flex; align-items: center; gap: 9px; text-align: left;
  background: var(--card-2); border: 1px solid var(--line); border-radius: 11px;
  padding: 10px 11px; margin: 8px 0; color: inherit; cursor: pointer; min-height: 44px; }
.sd-covrow .sd-meter, .sd-slot .sd-meter.sd-cov-m { flex: 0 0 106px; width: 106px; }
.sd-covtext { flex: 1 1 auto; min-width: 0; font-size: 11.5px; line-height: 1.4; }
.sd-covgo { flex: none; font-size: 11.5px; color: var(--accent); white-space: nowrap; }
/* At 320 the meter gives up a third of itself before the sentence beside it
   wraps to three lines — measured, not guessed. */
@media (max-width: 359px) {
  .sd-covrow, .sd-sincerow, .sd-slot { gap: 7px; padding: 10px 9px; }
  .sd-covrow .sd-meter, .sd-slot .sd-meter.sd-cov-m { flex: 0 0 72px; width: 72px; }
  .sd-covtext, .sd-covgo, .sd-since-t, .sd-slot-t, .sd-slot-go { font-size: 11px; }
}

/* The sheet's bar: the level travels with the title. */
.sd-pill { font-size: 11px; font-weight: 650; border-radius: 999px; padding: 3px 9px; flex: none;
  border: 1px solid var(--line); color: var(--mut); }
.sd-pill.sd-pill-on { background: var(--accent); border-color: transparent; color: var(--on-fill); }

/* The headline is the level's NAME; the meta line counts what it read. */
#standing-body .sd-lead { padding: 2px 0 12px; }
#standing-body .sd-name { font-size: 19px; font-weight: 650; line-height: 1.25; }
#standing-body .sd-meta { font-size: 12px; margin-top: 4px; line-height: 1.4; }
#standing-body .sd-intro { font-size: 13px; line-height: 1.5; margin: 0 0 10px; }
#standing-body .sd-clash { border: 1px solid var(--warn); border-radius: 10px; padding: 9px 11px; margin: 0 0 12px; font-size: 13px; color: var(--warn); }
#standing-body .sd-acts { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 4px; }
#standing-body .sd-foot { font-size: 11px; line-height: 1.5; text-align: center; margin: 16px 0 4px; }
#standing-body .sd-loose { margin: 14px 0 8px; }

/* THE DOORS OUT (decisions 11B + 13B, 2026-09-17). Below the sections, because
   taking a document away is what you do AFTER reading it — and at every level,
   so a shooter at Level 1 meets the same block a shooter at 5 does.
   `sd-sent` is the one thing the sheet says back about an act it performed;
   it borrows the accent rather than the warn colour, because a message that
   arrived is not a problem. */
#standing-body .sd-sent { border: 1px solid var(--accent); border-radius: 10px; padding: 9px 11px; margin: 0 0 12px; font-size: 13px; color: var(--accent); }
/* `sd-doorthin`, not `sd-thin`: `.sd-chip.sd-thin` already means a source chip
   in a thin state 40 lines up, and one stylesheet with no build step is one
   namespace — the bare-modifier collision the house rule names. */
#standing-body .sd-c-doors .sd-doorthin,
#standing-body .sd-c-doors .sd-stored { margin: 8px 0 0; font-size: 12.5px; line-height: 1.5; }
#standing-body .sd-c-doors .sd-doorline { margin: 2px 0 0; font-size: 11.5px; line-height: 1.45; }
#standing-body .sd-c-doors .sd-acts { margin: 12px 0 6px; }

/* ONE CARD SHAPE, and an eyebrow over it. */
#standing-body .sd-card {
  background: var(--card-2); border: 1px solid var(--line); border-radius: 14px;
  padding: 13px; margin: 0 0 10px;
}
#standing-body .sd-eyebrow { font-size: 11px; font-weight: 650; letter-spacing: .06em; text-transform: uppercase; color: var(--mut); margin-bottom: 8px; }
#standing-body .sd-card.sd-c-table { padding: 0; }
#standing-body .sd-c-table .sd-eyebrow { padding: 13px 13px 0; margin-bottom: 4px; }
#standing-body .sd-p { font-size: 13px; line-height: 1.55; margin: 0 0 7px; }
#standing-body .sd-card > .sd-p:last-child { margin-bottom: 0; }
#standing-body .sd-cfoot { font-size: 11.5px; line-height: 1.45; margin: 6px 0 0; }
#standing-body .sd-c-lead .sd-name { margin-bottom: 8px; }
#standing-body .sd-c-lead p { font-size: 13px; line-height: 1.5; margin: 0; }

/* The app moved under a file somebody already holds. Marked, not alarmed:
   re-opening it is still free, and most deploys change nothing it prints. */
#standing-body .sd-outdated { color: #e2b862; }

/* A sentence the shooter wrote, with what was measured under it. The rule is
   in the layout: the quote is set apart and attributed, the measurement sits
   below in the muted voice, and nothing on the page joins the two. */
#standing-body .sd-said { margin: 0 0 12px; }
#standing-body .sd-said:last-child { margin-bottom: 0; }
#standing-body .sd-said-k { font-size: 11px; font-weight: 650; letter-spacing: .04em; text-transform: uppercase; color: var(--mut); margin-bottom: 4px; }
#standing-body .sd-said-q {
  margin: 0 0 5px; padding: 0 0 0 10px; border-left: 2px solid var(--line);
  font-size: 13px; line-height: 1.5; font-style: italic;
}
#standing-body .sd-said-m { font-size: 12px; line-height: 1.5; margin: 0; }

/* The dashed caveat: what this level will not say, and why. */
#standing-body .sd-c-caveat { background: var(--card-2); border-style: dashed; }
#standing-body .sd-c-caveat p { font-size: 12.5px; line-height: 1.5; margin: 0 0 6px; }
#standing-body .sd-c-caveat p:last-child { margin-bottom: 0; }

/* The one alert this document raises: a profile number no clock has read. */
#standing-body .sd-c-alert { border-color: var(--err-line); background: var(--err-bg); }
#standing-body .sd-c-alert p { font-size: 12.5px; line-height: 1.5; margin: 0; color: var(--err); }

/* Tiles: the class split, a reading's values in order, projected vs scored. */
#standing-body .sd-tiles { display: flex; gap: 8px; margin: 2px 0 8px; }
#standing-body .sd-tile {
  flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 2px;
  background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 8px 10px;
}
#standing-body .sd-tile-k { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--mut); }
#standing-body .sd-tile-v { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 17px; font-weight: 700; font-variant-numeric: tabular-nums; }
#standing-body .sd-tile-u { font-size: 10.5px; color: var(--mut); }
#standing-body .sd-tile.sd-newest { border-color: var(--accent); }
#standing-body .sd-tile.sd-newest .sd-tile-v, #standing-body .sd-tile.sd-newest .sd-tile-k { color: var(--accent); }
#standing-body .sd-tile.sd-issued .sd-tile-v { font-size: 26px; }
#standing-body .sd-tile.sd-issued .sd-tile-k { color: var(--green-dk); }
#standing-body .sd-tiles.sd-split .sd-tile { background: transparent; border: 0; border-right: 1px solid var(--line); border-radius: 0; padding: 4px 10px 4px 0; }
#standing-body .sd-tiles.sd-split .sd-tile:last-child { border-right: 0; }
#standing-body .sd-tile.sd-projected .sd-tile-k { color: var(--warn); }
#standing-body .sd-tile.sd-scored .sd-tile-k { color: var(--green-dk); }

/* A direction: its name, its count, its unit and polarity, its readings. */
#standing-body .sd-rhead { display: flex; align-items: baseline; gap: 8px; font-size: 10.5px; }
#standing-body .sd-rname { flex: 1 1 auto; min-width: 0; font-size: 14px; font-weight: 650; }
#standing-body .sd-axis { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10px; font-weight: 700; letter-spacing: .04em; color: var(--mut); margin: 7px 0 6px; }

/* The baseline table, and the reconciliation table beside it. */
#standing-body .sd-brows { display: flex; flex-direction: column; }
#standing-body .sd-brow { display: flex; align-items: baseline; gap: 10px; padding: 10px 13px; border-top: 1px solid var(--line); font-size: 13px; }
#standing-body .sd-brow:first-child { border-top: 0; }
#standing-body .sd-brow.sd-head { font-size: 10.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--mut); }
#standing-body .sd-brow-t { flex: 1 1 auto; min-width: 0; }
#standing-body .sd-brow-v, #standing-body .sd-brow-m {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 700; font-variant-numeric: tabular-nums;
  width: 62px; text-align: right; flex: none;
}
#standing-body .sd-brow.sd-head .sd-brow-v, #standing-body .sd-brow.sd-head .sd-brow-m { font-family: inherit; font-weight: 700; }
#standing-body .sd-s-optimistic { color: var(--err); }
#standing-body .sd-s-better { color: var(--green-dk); }
#standing-body .sd-mark { font-size: 10px; font-weight: 650; border-radius: 5px; padding: 2px 6px; width: 64px; text-align: center; flex: none;
  border: 1px solid var(--line); color: var(--mut); }
#standing-body .sd-mark.sd-m-typed { color: var(--warn); border-color: var(--warn-line); background: var(--warn-bg); }
#standing-body .sd-mark.sd-m-measured { color: var(--green-dk); }

/* Plain rows: the classifiers under the class you hold, the bars you missed. */
#standing-body .sd-rows { display: flex; flex-direction: column; margin: 4px 0 0; }
#standing-body .sd-row { display: grid; grid-template-columns: 1fr auto; gap: 2px 10px; padding: 8px 0; border-top: 1px solid var(--line); font-size: 13px; }
#standing-body .sd-row-t { min-width: 0; }
#standing-body .sd-row-v { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 700; }
#standing-body .sd-row-s { grid-column: 1 / -1; font-size: 12px; }

/* A ratio column: "5 of 5" beside the rule, "Shot it" beside the priority. */
#standing-body .sd-mrow { display: flex; gap: 10px; align-items: baseline; padding: 5px 0; }
#standing-body .sd-ratio { flex: none; width: 58px; font-size: 10.5px; font-weight: 650; letter-spacing: .02em; color: var(--mut); }
#standing-body .sd-ratio.sd-r-all { color: var(--green-dk); }
#standing-body .sd-ratio.sd-r-some { color: var(--warn); }
#standing-body .sd-ratio.sd-r-none { color: var(--mut); }
#standing-body .sd-mtext { flex: 1 1 auto; min-width: 0; font-size: 12.5px; line-height: 1.45; }

/* How well it has predicted for you — amber while it is provisional. */
#standing-body .sd-c-cover.sd-prov { border-color: var(--warn-line); background: var(--warn-bg); }
#standing-body .sd-cover-h { display: flex; align-items: baseline; gap: 8px; }
#standing-body .sd-c-cover.sd-prov .sd-cover-h { color: var(--warn); }
/* 10px, not the mockup's 9.5: the app has an 11px legibility floor with
   uppercase labels at 10–10.5, and test/controls.test.js bans 9.5 outright. */
#standing-body .sd-prov-tag { margin-left: auto; font-size: 10px; border: 1px solid var(--warn-line); border-radius: 5px; padding: 1px 6px; color: var(--warn); }
#standing-body .sd-cnote { font-size: 12px; line-height: 1.5; margin: 0; color: var(--warn); }
#standing-body .sd-c-cover:not(.sd-prov) .sd-cnote { color: var(--mut); }

#standing-body .sd-rules { margin: 2px 0; padding-left: 20px; font-size: 13px; line-height: 1.55; }
#standing-body .sd-qs { margin: 2px 0; padding-left: 20px; font-size: 13px; line-height: 1.55; }
#standing-body .sd-qs li, #standing-body .sd-rules li { margin-bottom: 6px; }

/* The gate table: level, what it needs, what it may then say. */
#standing-body .sd-ladder { border: 1px solid var(--line); border-radius: 14px; overflow: hidden; margin: 0 0 10px; }
#standing-body .sd-gate { display: grid; grid-template-columns: 26px 1fr; gap: 3px 10px; padding: 11px 13px; border-top: 1px solid var(--line); }
#standing-body .sd-gate:first-child { border-top: 0; }
#standing-body .sd-gate-n { font-size: 15px; font-weight: 700; color: var(--mut); }
#standing-body .sd-gate.sd-met .sd-gate-n { color: var(--accent); }
#standing-body .sd-gate-needs { font-size: 12.5px; line-height: 1.45; }
#standing-body .sd-gate-opens { grid-column: 2; font-size: 12.5px; line-height: 1.45; color: var(--mut); }
#standing-body .sd-gate.sd-gate-head { font-size: 10.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--mut); }
#standing-body .sd-gate.sd-gate-head .sd-gate-n, #standing-body .sd-gate.sd-gate-head .sd-gate-needs, #standing-body .sd-gate.sd-gate-head .sd-gate-opens { font-size: 10.5px; color: var(--mut); }
#standing-body .sd-gate.sd-never { opacity: .7; }

/* What it read: every store, its counts, and the state it is in. */
#standing-body .sd-sources { margin: 0 0 10px; }
#standing-body .sd-line { font-size: 13px; line-height: 1.5; margin: 0 0 8px; }
#standing-body .sd-srows { display: flex; flex-direction: column; }
#standing-body .sd-srow { display: flex; align-items: center; gap: 10px; padding: 10px 13px; border-top: 1px solid var(--line); }
#standing-body .sd-srow:first-child { border-top: 0; }
#standing-body .sd-srow-b { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
#standing-body .sd-src-t { font-size: 13px; font-weight: 600; }
#standing-body .sd-src-d { font-size: 11.5px; line-height: 1.4; }
#standing-body .sd-state { flex: none; font-size: 10px; font-weight: 650; border: 1px solid var(--line); border-radius: 5px; padding: 2px 7px; color: var(--mut); }
#standing-body .sd-state.sd-st-full { color: var(--green-dk); }
#standing-body .sd-state.sd-st-thin { color: var(--warn); border-color: var(--warn-line); }

/* The bought words, and the section headings between the card decks. */
#standing-body .sd-h { font-size: 15px; font-weight: 700; margin: 22px 0 8px; display: flex; align-items: baseline; gap: 8px; }
#standing-body .sd-lv { font-size: 10px; font-weight: 700; color: var(--mut); letter-spacing: .06em; text-transform: uppercase; }
#standing-body .sd-short { font-size: 13px; line-height: 1.5; margin: 0 0 10px; }
/* The verdict: one sentence about the shooter, and since 2026-09-19 it is
   what LEADS the sheet — it stands where the level's name stands when no
   words have been bought, so it takes the HubV2 artboard's own type (17px,
   650, 1.3) rather than the larger size it wore inside the read's block.
   One lit thing at the top, and one of the two, never both. */
#standing-body .sd-lead .sd-verdict { font-size: 17px; font-weight: 650; line-height: 1.3; letter-spacing: -.01em; margin: 0; }
#standing-body .sd-verdict { font-size: 18px; font-weight: 600; line-height: 1.35; letter-spacing: -.01em; margin: 0 0 9px; }
#standing-body .sd-standing { font-size: 15px; line-height: 1.6; margin: 0 0 10px; }
#standing-body .sd-pat { margin: 0 0 10px; }
#standing-body .sd-pat:last-child { margin-bottom: 0; }
#standing-body .sd-pat h4 { font-size: 13px; margin: 0 0 3px; }
#standing-body .sd-pat p { font-size: 12.5px; line-height: 1.5; margin: 0; }
#standing-body .sd-fold { border-top: 1px solid var(--line); margin-top: 14px; }
#standing-body .sd-fold > summary { cursor: pointer; color: var(--mut); font-size: 13px; font-weight: 600; padding: 10px 0 6px; }
#standing-body .sd-refused { margin: 4px 0 8px; padding-left: 20px; font-size: 13px; line-height: 1.55; color: var(--mut); }

/* Level 0: the answers that are missing, with what each one opens. */
#standing-body .sd-arows { display: flex; flex-direction: column; }
#standing-body .sd-arow { display: flex; gap: 12px; align-items: flex-start; padding: 12px 13px; border-top: 1px solid var(--line); }
#standing-body .sd-arow:first-child { border-top: 0; }
#standing-body .sd-dot { flex: none; width: 6px; height: 6px; border-radius: 999px; background: var(--accent); margin-top: 6px; }
#standing-body .sd-abody { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
#standing-body .sd-atitle { font-size: 14px; font-weight: 600; }
#standing-body .sd-abody .mut { font-size: 12px; line-height: 1.45; }

/* An earlier standing: its date, the level it was written at, its opening
   sentence, and the archive door the store already has. */
#standing-body .sd-old { padding: 9px 0; border-top: 1px solid var(--line); }
#standing-body .sd-old-h { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 13px; }
#standing-body .sd-old-act { margin-left: auto; min-height: 36px; padding: 6px 12px; font-size: 12px; }
#standing-body .sd-old p { font-size: 12px; line-height: 1.45; margin: 4px 0 0; }

/* ── YOUR STANDING, SECOND LOOK (owner, 2026-09-18, all seven decisions A —
   artifact 62F9LHLn3o1rB5AW6cqMid) ──────────────────────────────────────────
   The hub, the section screens, and the eight marks. Tokens only, no hex, and
   `sd-` throughout — `st-` is the Standards tab, and one stylesheet with no
   build step is ONE namespace. */

/* The hub's level meter and the line under it. */
#standing-body .sd-meterrow { margin: 2px 0 12px; }
#standing-body .sd-meterline { display: flex; align-items: baseline; gap: 8px; margin-top: 7px; font-size: 11.5px; }
#standing-body .sd-meterline > span { flex: 1 1 auto; min-width: 0; }
#standing-body .sd-link { background: none; border: 0; padding: 0; font-size: 11.5px; color: var(--accent); cursor: pointer; flex: none; text-align: right; }

/* THE GLANCE ROW: three tiles, and a tile whose gate is shut is ABSENT rather
   than greyed — at level 3 the reader sees one tile, not three placeholders. */
#standing-body .sd-glance { display: flex; gap: 8px; margin: 0 0 10px; }
#standing-body .sd-gtile { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 2px;
  background: var(--card-2); border: 1px solid var(--line); border-radius: 11px; padding: 8px; }
/* Two lines and no third. The tile is a GLANCE at a thing whose own screen is
   one tap away, so a label or a note that ran on would be the hub growing a
   section rather than a door to one. */
#standing-body .sd-gtile .sd-tile-k, #standing-body .sd-gnote {
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
#standing-body .sd-gv { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 18px; font-weight: 700;
  font-variant-numeric: tabular-nums; line-height: 1.1; display: flex; align-items: baseline; gap: 4px; }
#standing-body .sd-g-pair .sd-gv { font-family: inherit; font-size: 13.5px; }
#standing-body .sd-gnote { font-size: 10.5px; line-height: 1.35; color: var(--mut); }
#standing-body .sd-gtile.sd-better .sd-gnote { color: var(--green-dk); }
#standing-body .sd-gtile .sd-track, #standing-body .sd-gtile .sd-band, #standing-body .sd-gtile .sd-spark { margin-top: 4px; }

/* MARK 3 · the class track. The fill is the issued percent; the tick is the
   next class's floor. A percent over the ceiling fills the rail and says so
   through the tile beside it — the figure itself is never clamped. */
#standing-body .sd-track { display: block; position: relative; height: 10px; border-radius: 5px; background: var(--line-2); }
#standing-body .sd-track-fill { position: absolute; left: 0; top: 0; bottom: 0; border-radius: 5px; background: var(--accent); }
#standing-body .sd-track-fill.sd-over { background: var(--green-dk); }
#standing-body .sd-track-tick { position: absolute; top: -3px; bottom: -3px; width: 2px; background: var(--mut); }
#standing-body .sd-tracknote { font-size: 11px; margin: 5px 0 0; }

/* MARK 5 · the projected band against the scored run, with the 100%-of-HHF
   reference between them. NEVER a band collapsed to a point. */
#standing-body .sd-band { display: block; position: relative; height: 14px; margin: 4px 0 0;
  background: linear-gradient(var(--line-2), var(--line-2)) left center / 100% 6px no-repeat; border-radius: 3px; }
#standing-body .sd-band-box { position: absolute; top: 2px; height: 10px; min-width: 4px; border-radius: 3px; background: var(--mut); }
#standing-body .sd-band-ref { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--mut); opacity: .7; }
#standing-body .sd-band-score { position: absolute; top: -2px; bottom: -2px; width: 3px; border-radius: 2px; background: var(--accent); }
/* The reference LABEL sits under its own tick, not in the middle of the rail:
   centred, it read as a caption for the whole bar and put "100% of HHF"
   nowhere near where 100% of HHF actually is. */
#standing-body .sd-bandkeys { position: relative; height: 13px; font-size: 10px; color: var(--mut); margin: 2px 0 7px; }
#standing-body .sd-bandkeys span { position: absolute; top: 0; transform: translateX(-50%); white-space: nowrap; }

/* A section title is a sentence — "What the app predicted, and what happened"
   — so the sheet's bar gives it two lines and no third. */
#standing-sheet .sheet-head h2 {
  /* `display: -webkit-box` drops `flex: 1`, which shrank the title to its own
     content and wrapped "Your standing" onto two lines at 320 — found by
     driving it, not by reading it. The flex sizing is restored alongside. */
  flex: 1 1 auto; min-width: 0;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  line-height: 1.25;
}

/* MARK 4 · the direction sparkline. The line is an SVG stretched to the box;
   the newest point is a positioned dot over it, so a wide card never stretches
   it into an ellipse. Nothing is drawn past the last reading. */
#standing-body .sd-spark { display: block; position: relative; height: 26px; margin: 2px 0 6px; color: var(--mut); }
#standing-body .sd-spark svg { display: block; width: 100%; height: 100%; overflow: visible; }
#standing-body .sd-spark-dot { position: absolute; width: 6px; height: 6px; border-radius: 999px;
  background: var(--accent); transform: translate(-50%, -50%); }
#standing-body .sd-gtile .sd-spark { height: 18px; }

/* MARK 6 · one box per scored stage, the ones inside the band filled. */
#standing-body .sd-dots { display: flex; gap: 4px; margin: 0 0 8px; }
#standing-body .sd-dot-b { flex: 1 1 0; min-width: 0; height: 9px; border-radius: 3px; border: 1px solid var(--line); }
#standing-body .sd-dot-b.sd-on { background: var(--accent); border-color: transparent; }

/* MARK 7 · on file against measured. Amber ONLY on `optimistic` — the profile
   faster than anything a clock has read, which is the number every plan since
   was built on and the one alert this document raises. */
#standing-body .sd-brow.sd-brow-mk { flex-wrap: wrap; }
#standing-body .sd-dumb { flex: 1 0 100%; position: relative; height: 16px; margin-top: 6px;
  background: linear-gradient(var(--line-2), var(--line-2)) left center / 100% 2px no-repeat; }
#standing-body .sd-dumb-span { position: absolute; top: 7px; height: 2px; background: var(--mut); }
#standing-body .sd-dumb-file, #standing-body .sd-dumb-meas { position: absolute; top: 3px;
  width: 10px; height: 10px; border-radius: 999px; transform: translateX(-50%); }
#standing-body .sd-dumb-file { background: var(--card); border: 2px solid var(--mut); }
#standing-body .sd-dumb-meas { background: var(--mut); }
#standing-body .sd-dumb.sd-d-optimistic .sd-dumb-span, #standing-body .sd-dumb.sd-d-optimistic .sd-dumb-meas { background: var(--warn); }
#standing-body .sd-dumb.sd-d-better .sd-dumb-span, #standing-body .sd-dumb.sd-d-better .sd-dumb-meas { background: var(--green-dk); }

/* MARK 8 · the rule ratio. A rule no plan mentioned draws a DASHED empty rail
   and not a red one: it is neither kept nor broken. */
#standing-body .sd-mrow.sd-mrow-bar { display: grid; grid-template-columns: 1fr auto; gap: 4px 10px; padding: 7px 0; }
#standing-body .sd-mrow.sd-mrow-bar .sd-ratio { width: auto; text-align: right; }
#standing-body .sd-rbar { grid-column: 1 / -1; height: 6px; border-radius: 3px; background: var(--line-2); overflow: hidden; }
#standing-body .sd-rbar.sd-r-never { background: transparent; border: 1px dashed var(--line); }
#standing-body .sd-rbar-fill { display: block; height: 100%; border-radius: 3px; background: var(--green-dk); }
#standing-body .sd-rbar.sd-r-some .sd-rbar-fill { background: var(--warn); }
#standing-body .sd-rbar.sd-r-none .sd-rbar-fill { background: var(--mut); }

/* THE FIVE DOORS. One row per section: the rung, the title, the row line the
   module composed, and a chevron. `alert` is the ONE coloured state and it is
   the same one the card behind it wears. */
#standing-body .sd-navs { border: 1px solid var(--line); border-radius: 14px; overflow: hidden; margin: 0 0 12px; }
#standing-body .sd-nav { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  background: var(--card-2); border: 0; border-top: 1px solid var(--line); padding: 11px 12px;
  color: inherit; cursor: pointer; min-height: 44px; }
#standing-body .sd-nav:first-child { border-top: 0; }
#standing-body .sd-nav-n { flex: none; width: 22px; height: 22px; border-radius: 6px; display: flex;
  align-items: center; justify-content: center; font-size: 11px; font-weight: 700;
  background: var(--line-2); color: var(--mut); }
#standing-body .sd-nav.sd-n-plain .sd-nav-n, #standing-body .sd-nav.sd-n-alert .sd-nav-n { background: var(--accent-soft); color: var(--accent); }
#standing-body .sd-nav-b { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
#standing-body .sd-nav-t { font-size: 13px; font-weight: 650; }
/* Two lines on the row, and the whole sentence on the screen it opens: a row
   is a door, and a door that grew to four lines would be the section. */
#standing-body .sd-nav-s { font-size: 11.5px; line-height: 1.4; color: var(--mut);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
#standing-body .sd-nav.sd-n-alert .sd-nav-s { color: var(--warn); }
#standing-body .sd-nav-go { flex: none; font-size: 15px; color: var(--mut); }

/* THE SIXTH ROW (owner decision 1A, 2026-09-19). It sits UNDER the five and
   APART from them: its own accent border and a FILLED badge, because it is
   the one row on this hub that opens words somebody paid for rather than
   arithmetic the app recomputes. The staleness sentence sits beneath it —
   where a reader meets the words — and never as a banner across a document
   whose figures are current. */
#standing-body .sd-readrow { margin: 0 0 12px; }
#standing-body .sd-nav.sd-n-read {
  border: 1px solid var(--accent); border-radius: 14px; width: 100%;
}
#standing-body .sd-nav.sd-n-read .sd-read-n { background: var(--accent); color: var(--on-fill); }
#standing-body .sd-nav.sd-n-read .sd-nav-go { color: var(--accent); }
#standing-body .sd-navstale { font-size: 11.5px; line-height: 1.45; margin: 6px 2px 0; color: var(--warn); }

/* THE READ'S OWN SCREEN. Its date leads, because the one thing the figures
   above can never tell a reader about bought prose is how old it is. */
#standing-body .sd-readhead { display: flex; align-items: baseline; gap: 8px; margin: 0 0 10px; }
#standing-body .sd-written { flex: 1 1 auto; font-size: 12px; line-height: 1.4; }
#standing-body .sd-age {
  flex: none; font-size: 11px; color: var(--mut);
  border: 1px solid var(--line); border-radius: 999px; padding: 2px 8px;
}
#standing-body .sd-readfoot { text-align: left; margin-top: 16px; }

/* A SECTION SCREEN, and a folded card on it. Nothing is deleted by a fold —
   the card keeps every sentence, and the document's own renderings print all
   of them whatever this stylesheet does. */
#standing-body .sd-scr { padding-top: 2px; }
#standing-body .sd-card.sd-c-fold { padding: 0; }
#standing-body .sd-card.sd-c-fold > summary { cursor: pointer; color: var(--mut); font-size: 12.5px;
  font-weight: 600; padding: 12px 13px; min-height: 44px; display: flex; align-items: center; }
#standing-body .sd-card.sd-c-fold .sd-foldbody { padding: 0 13px 13px; }
#standing-body .sd-card.sd-c-fold.sd-c-table .sd-foldbody { padding: 0 0 6px; }

/* THE LADDER as the mockup's vertical rungs: the level, its NAME, the tick
   where it is met, and what it opens. */
#standing-body .sd-ladnote { font-size: 12px; line-height: 1.5; margin: 0 0 10px; }
#standing-body .sd-rungs { display: flex; flex-direction: column; gap: 8px; margin: 0 0 12px; }
#standing-body .sd-rung { display: flex; gap: 10px; background: var(--card-2); border: 1px solid var(--line);
  border-left: 3px solid var(--line); border-radius: 11px; padding: 10px 11px; }
#standing-body .sd-rung.sd-met { border-left-color: var(--accent); }
#standing-body .sd-rung.sd-never { opacity: .7; }
#standing-body .sd-rung-n { flex: none; width: 22px; height: 22px; border-radius: 999px; display: flex;
  align-items: center; justify-content: center; font-size: 11px; font-weight: 700;
  background: var(--line-2); color: var(--mut); }
#standing-body .sd-rung.sd-met .sd-rung-n { background: var(--accent); color: var(--on-fill); }
#standing-body .sd-rung-b { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
#standing-body .sd-rung-t { font-size: 13px; font-weight: 650; }
#standing-body .sd-rung-needs { font-size: 11.5px; line-height: 1.45; color: var(--mut); }
#standing-body .sd-rung.sd-met .sd-rung-needs { color: var(--green-dk); }
#standing-body .sd-rung-opens { font-size: 11.5px; line-height: 1.45; }

/* WHAT IT READ, split in two so "you have none" and "this app will not look"
   are visibly different kinds of absence. The state chip on an absent row
   carries the REASON rather than the word "absent". */
#standing-body .sd-state.sd-st-absent { font-weight: 600; letter-spacing: 0; text-transform: none; }
#standing-body .sd-srow.sd-sr-absent .sd-src-t { color: var(--mut); }

/* Level 0's answer count, over its meter. */
#standing-body .sd-counth { display: flex; align-items: baseline; gap: 8px; margin-bottom: 9px; }
#standing-body .sd-countn { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 24px; font-weight: 700; }
#standing-body .sd-counth .mut { font-size: 12px; }

/* The one thing the What it read screen says that a reader could not work out
   for themselves: which of the nine countable sources is carrying the level.
   The sentence under it (owner decision 3A, 2026-09-19) says what the rest of
   the level-3 group adds — content, never depth — and wears the same type, so
   the two read as one explanatory block above the rows rather than as a note
   somebody added later. */
#standing-body .sd-movedline,
#standing-body .sd-contentnote { font-size: 11.5px; line-height: 1.5; margin: 0 0 12px; }
