/*
  Centurion MCN prototype.

  Visual language borrowed from the PERIO client book: a warm off-white ground,
  deep navy for structure, a single gold accent, thin coloured rules along the
  top of cards, big quiet numerals, and a lot of air. One purpose per section,
  strong titles, short ledes, and a gold-barred line where the page has one
  thing to say.

  Severity carries a mark and a word as well as a colour, so the table still
  reads in greyscale and on a projector.
*/

/* --- the product's first typeface ------------------------------------------

  INTER, VARIABLE, SELF-HOSTED. Instruction 59. Until this landed CRI had no
  specified face at all -- no @font-face, no font-family on body -- so every
  page rendered in whatever sans the reader's OS supplied.

  VARIABLE IS NOT A PREFERENCE. This stylesheet asks for 550, 640, 650 and 750
  across 26 rules. Static cuts snap those to the nearest available weight and
  nothing errors, nothing fails a test, and the page is quietly not what the
  stylesheet says. The axis served here is 400-800.

  SELF-HOSTED RATHER THAN LINKED, deliberately. A fonts.googleapis.com request
  fails on one network and not another -- a blocker, a firewall, an offline
  machine -- and a font that vanishes for some readers is exactly the silent
  fallback this product refuses everywhere else. Same-origin files cannot be
  third-party blocked, and the bytes are pinned rather than whatever the CDN
  serves that day.

  CYRILLIC IS NOT AN EDGE CASE HERE. 120 of 252 video titles are Cyrillic and
  6 of 15 channel names are. A face whose Cyrillic fell back would swap face
  per string INSIDE one column, which reads worse than a whole-page fallback.
  Both cyrillic subsets are below and `unicode-range` keeps them lazy: a Latin
  page fetches 47KB, a Ukrainian one adds 18KB.
*/

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('fonts/inter-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('fonts/inter-cyrillic-ext.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('fonts/inter-greek.woff2') format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('fonts/inter-greek-ext.woff2') format('woff2');
  unicode-range: U+1F00-1FFF;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('fonts/inter-vietnamese.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}

/* --- WHAT `ch` IS, AND WHERE IT IS THE RIGHT UNIT -------------------------

  Instruction 113. Measured in Chrome against the served stylesheet, signed in,
  after `document.fonts.load()` for each script and `document.fonts.check()`
  asserted TRUE -- and against a deliberately nonexistent family as a control,
  because a metric taken before its subset loads silently returns the FALLBACK's
  numbers with no error.

  `1ch` IS THE ADVANCE OF THE LATIN DIGIT ZERO. Nothing else.

      Inter's zero      0.6309 em      <- what `ch` measures
      a Latin character 0.5164 em      <- median over the rendered title corpus
      a Cyrillic char   0.5694 em

  So `1ch` over-states a Latin character by 22.2% and a Cyrillic one by 10.8%,
  and Cyrillic is 10.3% wider per character than Latin. THE FONT SIZE CANCELS:
  an `Nch` box holds 1.22N Latin characters or 1.11N Cyrillic ones at EVERY
  size, so a cap stated in `ch` is always ~10% tighter in Cyrillic than the
  number suggests.

  AND THE GLYPH EFFECT IS THE SMALL HALF. Over the 115 titles the latest runs
  actually render, the median Latin title is 35 characters and the median
  Cyrillic one is 89 -- 154% longer. Any statement of this problem that leads
  with the 10.3% understates it by roughly fifteen times.

  THE UNIT IS NOT THE DEFECT; THE USE IS, and this comment must not be read as
  condemning `ch`:

    RIGHT for a PROSE MEASURE. Line length for readability genuinely is counted
    in characters -- the 45-75 range is characters per line, not pixels. The
    sixteen prose measures in this file are correct and were deliberately left
    alone. `.v3-note` (88ch) and `.v3-nowhere__body` (62ch) are among them.

    WRONG for a LAYOUT WIDTH. A grid or table column is a question about a BOX.
    The three layout uses were replaced with lengths -- see `.lvgrid`,
    `.ownercol` and `.covcol`, each carrying its own measured pixel delta.

    WRONG for CLIPPING, which is also a question about a box. The five
    truncation uses are MEASURED AND LEFT IN PLACE pending a ruling, because
    they decide what a reader cannot see and that is the owner's call. Measured:
    all five are inert or near-inert -- the column binds long before the cap
    does. `.one-line` caps at 52ch = 426.5px while its column renders 297px, so
    a reader gets 44 Latin or 40 Cyrillic characters and the cap never engages.
    A cap that cannot bite is a dormant contradiction, not a safeguard.

  A comment stating this must not be counted as a use. Any guard that greps for
  `ch` here has to strip comments first: stripped 26, unstripped 33.
*/

:root {
  /* The UI face. Inter first, then a stack somebody chose -- never a bare
     `Inter, sans-serif`, so a failed load lands somewhere deliberate. */
  --font-ui: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
             Roboto, Helvetica, Arial, sans-serif;

  /* LIGHT IS THE BASE AND DARK IS THE OVERRIDE, per the owner's 2026-08-24
     reversal: "keep the other tenants with light theme with toggles to switch".

     THREE STATES, AND THE ORDER BELOW IS THE MECHANISM RATHER THAN A STYLE.

       :root                                            the complete LIGHT palette
       @media (prefers-color-scheme: dark)
         :root:not([data-theme="light"])                 dark, when the system says so
       :root[data-theme="dark"]                          dark, when the reader says so

     THE `:not([data-theme="light"])` GUARD IS WHAT LETS AN EXPLICIT LIGHT CHOICE
     SURVIVE A DARK SYSTEM PREFERENCE. Without it a reader who chose light on a
     dark-preferring machine gets the media query anyway, which is the person the
     owner just asked us to serve.

     EVERY COLOUR TOKEN IS DEFINED HERE, ON BARE `:root`. A token defined only
     inside a media block does not exist in one of the three states, and what it
     renders is one theme's foreground on the other's ground.
     `test_theme_cascade` fails on exactly that.

     `--navy` IS THE TRAP IN THIS FILE AND ITS NAME INVERTS. It is not a surface:
     it is the HEADING AND EMPHASIS TEXT colour, read by `h1.display`, `h2.sec`,
     `.cell-title` and the current nav item -- 28 rules. On dark it holds a cool
     near-white; on light it holds the deep navy the name suggests. Anyone
     deriving light values by reading token names gets this one exactly backwards.

     `--band` / `--band-ink` ARE AN INVERTED SURFACE IN BOTH THEMES. Table
     headers, primary buttons, tags and badges: a dark ground with light text on
     it, which is what they are on a white page and on a black one. So their
     values barely move, and that is the role being stable rather than an
     oversight.

     `--gold` NAMES A MAGENTA and keeps its name on purpose -- renaming it across
     every rule that reads it is a separate change. It is ONE HUE AT TWO VALUES:
     syncRed #f61e6f on dark, syncRed-700 #c0073f on light. 500 and 600 were
     measured and rejected -- 3.80:1 and 4.34:1 on this ground, and 4.49:1 for
     600 on pure white, which fails AA by a hundredth and must not be rounded.

     THE FOUR SEMANTICS ARE RE-DERIVED, NOT LIGHTENED. Each is a text, a wash and
     an outline chosen as a set: the text darkens as the wash lightens, so an
     algorithmic inversion produces neither. Held to >= 6.30:1 on the worst pair,
     which is what the dark theme already achieves -- not to the AA minimum, since
     4.5 would be a visible regression from a design that reaches 6.30 to 8.07.
     `test_theme_contrast` computes all of them and asserts the floor.

     `--*-line` ARE NEW NAMES FOR VALUES THAT HAD NONE. The four chip outlines
     were hard-coded literals (var(--material-line), var(--attention-line), var(--settled-line), var(--coverage-line)) repeated across
     eleven rules. They are not a new colour with a new name -- they are the
     existing outline, named, which CONSTRAINTS.md §20 permits and requires: a
     literal repeated in eleven places is a token that has not been declared. */
  --ground: #fbfbfa;
  --paper: #ffffff;
  /* The hover and inset surface: row hover, chip--plain, form controls, the
     rail's current item. On dark it is the LIGHTEST of the three surfaces; on
     light it is the darkest, because both directions mean "separated from the
     page". */
  --raised: #f1f0ec;
  --navy: #12182a;
  --navy-soft: #3a4560;
  --ink: #1a1a18;
  --ink-mid: #55554e;
  /* NOT A TEXT COLOUR. Retired from `color:` in every rule, and the three
     remaining uses are borders, where 4.5:1 does not apply. In dark it is
     #7a7a7a, which measures 4.36:1 on `--paper` and 3.93:1 on `--raised` --
     below the AA floor -- and 4.61:1 on `--ground`, which clears it by 0.11.
     A token that passes on its best ground by a hundredth is one theme edit
     from failing everywhere. Measured across six design-v2 pages: 1,584
     elements painted it, 1,512 of them below AA. Use `--ink-mid`. */
  --ink-faint: #6b6b63;
  --rule: #e3e1db;
  --rule-firm: #b6b3aa;
  /* THE PLAIN CHIP'S OWN BOUNDARY, SEPARATE FROM `--rule-firm` ON PURPOSE.
     176 draws an absent area ledger `quiet` and a complete one `plain`, and that
     distinction rests entirely on fill plus border because both chips carry the
     same `--ink-mid` text. Measured on the deployed sheet:

       plain FILL   vs its ground   1.14:1 light   1.11:1 dark
       plain BORDER vs its ground   2.10:1 light   2.94:1 dark

     No component reached the 3:1 non-text floor, so 176 worked for a
     normal-vision reader and may not have for the one it was made for.

     A NEW TOKEN RATHER THAN A RETUNED `--rule-firm`, WHICH HAS 55 CONSUMERS.
     Fixing one chip by moving that token would move 54 unrelated boundaries,
     and a stylesheet diff cannot show what it did to them.

     #b6b3aa -> #95938b, 2.096:1 -> 3.077:1 against `--paper`. Uniform channel
     scaling (0.819/0.821/0.818) so the warm neutral stays warm. `--ink-mid` is
     6.59:1 on the chip's fill, so the word keeps the emphasis and the border
     only delimits.

     NOT APPLIED TO `.chip--quiet`: its transparency IS its meaning -- the estate
     spends `quiet` on absence -- and raising it to 3:1 would undo 176. */
  --chip-edge: #95938b;
  --gold: #c0073f;
  --gold-ink: #ffffff;
  --gold-tile: #f7dbe4;
  --gold-soft: #f7dbe4;
  --gold-wash: #fdf2f5;

  --material: #9c3125;
  --material-wash: #fdeeec;
  --material-line: #e8b3ab;
  --attention: #71500f;
  --attention-wash: #fbf3e2;
  --attention-line: #dcc188;
  --settled: #1a6440;
  --settled-wash: #e9f7ef;
  --settled-line: #a3d6b8;
  --coverage: #3f439a;
  --coverage-wash: #ecedfa;
  --coverage-line: #b3b5e4;

  --band: #2b3244;
  --band-ink: #edf0f6;
  /* THE THIRD PINNED COLOUR. `--band` and `--band-ink` are pinned because the
     band is a fixed dark surface in both themes; ink drawn on it must not move.
     `.tag--warn` needed a THIRD such ink: a stable colour that still reads as
     coverage rather than as a plain tag. `--coverage` itself is 1.51:1 on the
     band in light. This is `--coverage`'s dark value, held still: 5.38:1 on the
     band in BOTH themes, and 2.08:1 against `--band-ink`, so the distinction a
     warn tag exists to make survives the fix. Pinning it as a token rather than
     writing the hex into the rule is what puts it under the cascade guard's
     every-token-on-bare-root check and the two-dark-blocks-identical check. */
  --band-coverage: #a0a2e8;

  /* RE-DERIVED, NEVER CARRIED. On a near-black ground a black shadow is nearly
     invisible and the depth comes from surfaces being lighter than the page; the
     same rgba(0,0,0,.3)/.7 on white are heavy smears. Light gets a lighter,
     cooler, tighter shadow. */
  --shadow: 0 1px 2px rgba(20, 22, 30, .06), 0 10px 28px -14px rgba(20, 22, 30, .20);

  /* NATIVE CONTROLS FOLLOW THE THEME. Declared here so selects, date pickers and
     scrollbars render with the page rather than against it. The two rule-level
     `color-scheme: dark` declarations this file used to carry forced dark native
     controls onto a white page, and no test in this suite looks at a native
     control. */
  color-scheme: light;

  /* NATIVE CONTROLS TAKE THE PRODUCT'S ACCENT, ONCE, HERE. 66 radios on
     /settings/specialists and 2 on /settings/settings rendered in Chrome's
     default blue because `accent-color` was never set. On the token layer rather
     than per page, so a surface that later grows a radio inherits it. It follows
     `--gold`, so it moves with the theme. */
  accent-color: var(--gold);

  /* Widened from 1200px: the live tables carry more columns than the prototype's
     mock ones, and at 1200 the last column was clipped on two pages. */
  --measure: 1500px;

  /* THE RAIL'S WIDTH IS HALF OF ITS BREAKPOINT, so the two may not drift.
     `--measure` + `--rail` is the viewport at which the rail becomes permanent. */
  --rail: 240px;
}

/* THE DARK PALETTE, TWICE, AND THE DUPLICATION IS REQUIRED RATHER THAN SLOPPY.
   CSS cannot share one declaration list between a media query and an attribute
   selector, and both are needed: the media query serves a reader who has chosen
   nothing, the attribute selector serves one who chose dark on a light machine.
   `test_theme_cascade` asserts the two blocks are IDENTICAL, so they cannot
   drift -- which is the failure duplication actually risks. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
  --ground: #0a0a0a;
  --paper: #121212;
  --raised: #1d1d1d;
  --navy: #E8EBF2;
  --navy-soft: #AEB8CA;
  --ink: #fafafa;
  --ink-mid: #c9c9c9;
  --ink-faint: #7a7a7a;
  --rule: #242424;
  --rule-firm: #5d5f66;
  /* See `--chip-edge` in the light block. #5d5f66 -> #606269 is a nudge:
     2.939:1 -> 3.076:1 against `--paper`, channels scaled 1.032/1.032/1.029. */
  --chip-edge: #606269;
  --gold: #f61e6f;
  --gold-ink: #0a0a0a;
  --gold-tile: #330316;
  --gold-soft: #8e0733;
  --gold-wash: #330316;
  --material: #F08579;
  --material-wash: #331C1C;
  --material-line: #5E3230;
  --attention: #E5B45F;
  --attention-wash: #322718;
  --attention-line: #5A4726;
  --settled: #6FCF9A;
  --settled-wash: #16291F;
  --settled-line: #2C5240;
  --coverage: #A0A2E8;
  --coverage-wash: #22233A;
  --coverage-line: #3E4066;
  --band: #2B3244;
  --band-ink: #EDF0F6;
  --band-coverage: #A0A2E8;
  --shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 10px 28px -14px rgba(0, 0, 0, .7);
  color-scheme: dark;
  }

  /* RULE-LEVEL DARK OVERRIDES -- the three places one value cannot serve both
     themes. Everything else in this stylesheet reads a token and needs nothing
     here, so these are listed together rather than scattered.

     EVERY SELECTOR CARRIES THE THEME PREFIX. A bare `.st-new` inside the media
     query would apply on a dark SYSTEM even to a reader who chose light --
     which is the exact failure the `:not([data-theme="light"])` guard on the
     token block exists to prevent, arriving one level down where the guard
     cannot see it.

     `.st-new` / `.st-open`   its own amber, not one of the four semantics, so it
                              cannot borrow `--attention*` without changing dark.
     `.kpi:hover`             its shadow was ALREADY a light value -- a navy cast
                              from when this product was light -- so dark is the
                              theme that needs correcting here, not light.
     `.search-results` /      a near-black dropdown shadow reads as depth on a
     `.acctmenu`              dark page and as a smear on a white one. */
  :root:not([data-theme="light"]) .st-new, :root:not([data-theme="light"]) .st-open { color: #F0A868; background: #33241A; border-color: #5C4128; }
  :root:not([data-theme="light"]) .kpi:hover { box-shadow: 0 2px 4px rgba(0,0,0,.35), 0 16px 32px -14px rgba(0,0,0,.6); }
  :root:not([data-theme="light"]) .search-results, :root:not([data-theme="light"]) .acctmenu { box-shadow: 0 20px 44px -18px rgba(0,0,0,.8); }
  :root:not([data-theme="light"]) .row--missing { background: color-mix(in srgb, var(--coverage) 6%, transparent); }
  :root:not([data-theme="light"]) .track-played { background: color-mix(in srgb, var(--gold) 14%, transparent); }
}

:root[data-theme="dark"] {
  --ground: #0a0a0a;
  --paper: #121212;
  --raised: #1d1d1d;
  --navy: #E8EBF2;
  --navy-soft: #AEB8CA;
  --ink: #fafafa;
  --ink-mid: #c9c9c9;
  --ink-faint: #7a7a7a;
  --rule: #242424;
  --rule-firm: #5d5f66;
  /* See `--chip-edge` in the light block. #5d5f66 -> #606269 is a nudge:
     2.939:1 -> 3.076:1 against `--paper`, channels scaled 1.032/1.032/1.029. */
  --chip-edge: #606269;
  --gold: #f61e6f;
  --gold-ink: #0a0a0a;
  --gold-tile: #330316;
  --gold-soft: #8e0733;
  --gold-wash: #330316;
  --material: #F08579;
  --material-wash: #331C1C;
  --material-line: #5E3230;
  --attention: #E5B45F;
  --attention-wash: #322718;
  --attention-line: #5A4726;
  --settled: #6FCF9A;
  --settled-wash: #16291F;
  --settled-line: #2C5240;
  --coverage: #A0A2E8;
  --coverage-wash: #22233A;
  --coverage-line: #3E4066;
  --band: #2B3244;
  --band-ink: #EDF0F6;
  --band-coverage: #A0A2E8;
  --shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 10px 28px -14px rgba(0, 0, 0, .7);
  color-scheme: dark;
}

/* RULE-LEVEL DARK OVERRIDES -- the three places one value cannot serve both
   themes. Everything else in this stylesheet reads a token and needs nothing
   here, so these are listed together rather than scattered.

   EVERY SELECTOR CARRIES THE THEME PREFIX. A bare `.st-new` inside the media
   query would apply on a dark SYSTEM even to a reader who chose light --
   which is the exact failure the `:not([data-theme="light"])` guard on the
   token block exists to prevent, arriving one level down where the guard
   cannot see it.

   `.st-new` / `.st-open`   its own amber, not one of the four semantics, so it
                            cannot borrow `--attention*` without changing dark.
   `.kpi:hover`             its shadow was ALREADY a light value -- a navy cast
                            from when this product was light -- so dark is the
                            theme that needs correcting here, not light.
   `.search-results` /      a near-black dropdown shadow reads as depth on a
   `.acctmenu`              dark page and as a smear on a white one. */
:root[data-theme="dark"] .st-new, :root[data-theme="dark"] .st-open { color: #F0A868; background: #33241A; border-color: #5C4128; }
:root[data-theme="dark"] .kpi:hover { box-shadow: 0 2px 4px rgba(0,0,0,.35), 0 16px 32px -14px rgba(0,0,0,.6); }
:root[data-theme="dark"] .search-results, :root[data-theme="dark"] .acctmenu { box-shadow: 0 20px 44px -18px rgba(0,0,0,.8); }
:root[data-theme="dark"] .row--missing { background: color-mix(in srgb, var(--coverage) 6%, transparent); }
:root[data-theme="dark"] .track-played { background: color-mix(in srgb, var(--gold) 14%, transparent); }

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: 15px/1.6 var(--font-ui);
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }

.skip { position: absolute; left: -9999px; }
.skip:focus { left: 12px; top: 12px; z-index: 200; background: var(--raised); color: var(--ink); padding: 10px 14px; }

/* --- chrome --------------------------------------------------------------- */

.topline { height: 4px; background: var(--band); }
.topline::after { content: ""; display: block; height: 3px; background: var(--gold); }

.proto {
  /* `#0E1016` was a near-black banner, which on a white page is a black bar.
     A banner is a surface separated from the page, and that is `--raised` in
     both themes. */
  background: var(--raised); color: var(--ink-mid);
  font-size: 12px; letter-spacing: .04em;
  padding: 7px 0;
}
.proto .wrap { display: flex; gap: 14px; align-items: baseline; flex-wrap: wrap; }
.proto strong { color: var(--gold); text-transform: uppercase; letter-spacing: .1em; font-size: 11px; }

.wrap { max-width: var(--measure); margin: 0 auto; padding: 0 32px; }

.masthead { background: var(--paper); border-bottom: 1px solid var(--rule); position: sticky; top: 0; z-index: 60; }
.masthead .wrap { display: flex; align-items: center; gap: 28px; min-height: 62px; }
.brand { font-weight: 800; color: var(--ink); letter-spacing: -.01em; text-decoration: none; font-size: 17px; }
.brand span { color: var(--gold); }
.mastnav { display: flex; gap: 22px; font-size: 14px; }
.mastnav a { color: var(--ink-mid); text-decoration: none; padding: 4px 0; border-bottom: 2px solid transparent; }
.mastnav a:hover { color: var(--navy); }
.mastnav a[aria-current="page"] { color: var(--navy); font-weight: 600; border-bottom-color: var(--gold); }
.mastright { margin-left: auto; display: flex; align-items: center; gap: 14px; font-size: 13px; color: var(--ink-mid); }

/* --- the operator rail ----------------------------------------------------- */
/*
 * IT LIVES IN THE MARGIN, WHICH IS THE ONLY PLACE IT IS FREE.
 *
 * `.wrap` is `max-width: var(--measure)` with 32px of padding, so the content
 * column caps at 1,436px however wide the window gets. At 2,245px that is 745px
 * of margin doing nothing, and a rail in it costs the tables NOTHING.
 *
 * Below that there is no spare margin to take. At 1,288px the column is already
 * viewport-limited to 1,224px, so every pixel the rail takes comes straight out
 * of a table -- and `.table-wrap` is `overflow: hidden`, so it comes out
 * silently. Measured on the live pages by constraining `.wrap` and counting
 * elements whose scrollWidth exceeds their rendered width:
 *
 *     content 1,436   /concerns 0 clipped
 *     content 1,048   /concerns 48 clipped, five distinct sentences, among them
 *                     the scope statement and the source attribution
 *
 * THAT ARITHMETIC PUT THE BREAKPOINT AT 1500 + 240 = 1740px, AND THE OWNER HAS
 * OVERRULED IT. Instruction 144, in their words:
 *
 *     "the left hand side menu is collapsing even on my laptop view. it only
 *      opens on a very wide screen. It needs to only collapse on mobile or iPad
 *      size screen"
 *
 * A 13" MacBook is 1440, a 16" is 1728, a common external is 1920. So 1740 hid
 * the product's primary navigation behind a hamburger on almost every real
 * machine, including theirs -- on the surface whose weakest point was already
 * navigation. THE TRADE IS DELIBERATE AND IT IS NOT FREE, so here is its price,
 * measured on this tree rather than inherited:
 *
 *     page            width  clipped today  clipped with the rail  unrecoverable
 *     v2 /concerns     1100        15                21                  0
 *     v2 /concerns     1512         0                15                  0
 *     v2 /videos       1100         0                 3                  3
 *     v3 dashboard    1100-1512     0                29                 29
 *
 * WHAT MAKES IT PAYABLE ON /concerns IS RECOVERABILITY, NOT WIDTH. `950035f`
 * added `title` to the cells that clip, so the extra 15-21 there are readable.
 * `title` IS HOVER-ONLY and reaches neither a keyboard nor a touch screen, so
 * that is a mitigation and not a fix; it is stated here rather than relied on
 * silently.
 *
 * THE v3 DASHBOARD'S 29 ARE THE REAL COST AND THEY ARE NOT THIS RULE'S FAULT.
 * All 29 are the LEVEL column, whose whole content is one word -- "Medium". 22px
 * of it is an icon added to that chip by instruction 145; the rest is a column
 * that is 63px wide at 1280 and clips a 66px chip with no icon at all. The fix
 * belongs to that table, not to this breakpoint: raising the breakpoint back to
 * 1740 would hide the navigation again to avoid truncating a single word that a
 * `nowrap` would also fix. Reported to 147.
 *
 * 1100 RATHER THAN A ROUNDER NUMBER: iPad landscape is 1024, so 1100 is the
 * first width above every tablet. Phones and both iPad orientations keep the
 * drawer, which is what was asked for.
 */
.rail {
  position: fixed; top: 0; bottom: 0; left: 0; width: var(--rail);
  z-index: 110;
  background: var(--paper); border-right: 1px solid var(--rule);
  display: flex; flex-direction: column;
  /* Off-canvas is the DEFAULT so that a viewport too narrow for the rail never
     depends on a media query having matched. `visibility` rather than opacity:
     it takes the links out of the tab order, which opacity does not. */
  transform: translateX(-100%); visibility: hidden;
  transition: transform .16s ease, visibility .16s ease;
}
body.rail-open .rail { transform: none; visibility: visible; }

.rail__brand { padding: 18px 20px 14px; border-bottom: 1px solid var(--rule); flex: 0 0 auto; }
.rail__scroll { flex: 1 1 auto; overflow-y: auto; padding: 18px 12px 28px; }

.rail__label {
  font-size: 11px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  color: var(--gold); margin: 0 0 8px; padding: 0 8px;
}
.rail__label + .rail__label { margin-top: 22px; }
.rail__group { list-style: none; margin: 0 0 22px; padding: 0; }
.rail__group li { margin: 0; }
.rail__group a {
  display: block; padding: 8px 8px; border-radius: 8px;
  font-size: 14px; color: var(--ink-mid); text-decoration: none;
  border-left: 2px solid transparent;
}
.rail__group a:hover { color: var(--ink); background: var(--raised); }
/* The mark, the weight and the rule -- three carriers, because a colour is
   never the only one. The horizontal nav used exactly this pair of tokens. */
.rail__group a[aria-current="page"] {
  color: var(--navy); font-weight: 600;
  background: var(--raised); border-left-color: var(--gold);
}
.rail__group a:focus-visible, .rail__brand a:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

.rail__acting { margin: 0 0 22px; padding: 0 8px; }
.rail__acting .finput { width: 100%; }
.rail__note { font-size: 11.5px; line-height: 1.5; color: var(--ink-mid); margin: 8px 0 0; }
.rail__note b { color: var(--ink-mid); font-weight: 600; }

.railtoggle {
  background: transparent; border: 1px solid var(--rule-firm); border-radius: 8px;
  color: var(--ink-mid); font-size: 15px; line-height: 1;
  padding: 8px 10px; cursor: pointer; flex: 0 0 auto;
}
.railtoggle:hover { color: var(--ink); border-color: var(--ink-faint); }
.railtoggle:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* No `display` here, so the `hidden` attribute still hides it. */
.railscrim { position: fixed; inset: 0; z-index: 105; background: rgba(0, 0, 0, .6); }

@media (min-width: 1740px) {
  /* Padding on the body rather than a wrapper element: every child shifts,
     including the sticky masthead, and NOTHING inside the page is restructured.
     `position: fixed` resolves against the viewport rather than the padded
     body, so `.viewer` and `.vmodal` still cover the whole screen and overlay
     the rail -- which is the decision: a modal beside the navigation is a modal
     competing with it. */
  body.has-rail { padding-left: var(--rail); }
  body.has-rail .rail { transform: none; visibility: visible; transition: none; }
  body.has-rail .railtoggle { display: none; }
  /* One wordmark on screen. `_brand.html` renders in both places and exactly
     one is shown at any width; above the breakpoint it is the rail's. */
  body.has-rail .masthead .brand { display: none; }
  body.has-rail .railscrim { display: none; }
}

/* THE SAME RAIL, EARLIER, ON DESIGN-V3 ONLY.
 *
 * The owner's complaint in instruction 144: "the left hand side menu is collapsing
 * even on my laptop view. it only opens on a very wide screen. It needs to only
 * collapse on mobile or iPad size screen". A 13" MacBook is 1440 and a 16" is 1728,
 * so 1740 hid the product's primary navigation on almost every real machine.
 *
 * QUALIFIED ON `.v3-surface`, AND THAT IS THE WHOLE POINT OF THE MARKER. The block
 * above is unqualified and therefore reaches `base.html` too -- all 28 design-v2
 * pages. Lowering it in place was measured and it is not free:
 *
 *     page            width   clipped before   after   unrecoverable
 *     v2 /concerns     1100         15           21          0
 *     v2 /concerns     1512          0           15          0
 *     v2 /videos       1100          0            3          3
 *
 * design-v2 is the product until it is retired, so it keeps the arithmetic --
 * `viewport - rail >= measure`, 1500 + 240 = 1740 -- and every one of those rows
 * goes to zero. design-v3 takes the narrower breakpoint deliberately, and pays for
 * it out of its own tables.
 *
 * 1100 rather than a rounder number: iPad landscape is 1024, so this is the first
 * width above every tablet. Phones and both iPad orientations keep the drawer.
 */
@media (min-width: 1100px) {
  body.v3-surface.has-rail { padding-left: var(--rail); }
  /* THE RAIL ENDS WHERE ITS CONTENT ENDS. 198, owner decision 4, in the owner's
     words: "make the rail shorter, don't fill the void."
   *
   * WHAT WAS WRONG. `.rail` above is `position: fixed; top: 0; bottom: 0`, so it was
   * viewport height on every page whatever it held. After 195 took the v3 rail from
   * eight entries to two, that measured 919.4px of empty column below the last entry
   * at a 1000px-tall viewport -- and 1219.4px at 1400. Two entries in the top sixth
   * of a full-height panel.
   *
   * WHY THE OBVIOUS REPAIR WAS REJECTED. Adding entries back cannot remove the void:
   * it is `bottom: 0`, so it scales with the viewport and twelve entries would still
   * leave several hundred pixels at 1440. And four of the six entries 195 removed --
   * Channels, Findings, Analysis, Events -- have no v3 route to point at; restoring
   * them pointing at design-v2 would reverse the owner's own ruling to get there.
   * Adding entries was the wrong repair for a right complaint.
   *
   * WHAT IS NOW TRUE. `bottom: auto` makes the height the content's, and
   * `max-height: 100dvh` keeps it bounded so a rail that ever outgrows the screen
   * scrolls in `.rail__scroll` rather than overflowing. `dvh` and not `vh`: mobile
   * browsers report `100vh` taller than the visible viewport, which is the one case
   * the bound exists for.
   *
   * `min-height: 0` ON THE SCROLLER, WITHOUT WHICH THE BOUND DOES NOTHING. A flex
   * item's `min-height` is `auto`, meaning its content size, so `flex: 1 1 auto`
   * refuses to shrink below its content and the rail would overflow `max-height`
   * instead of scrolling inside it. The bound and this line are one mechanism.
   *
   * A PANEL WITH A BOTTOM EDGE, NOT A FULL-HEIGHT DIVIDER. `.rail`'s `border-right`
   * now ends with the rail, so it is a stub rather than the full-height rule that
   * separated navigation from content. Keeping that line would need a second element
   * or a body background-image, and a rule below a rail that has ended separates
   * nothing from nothing. So the rail closes with `border-bottom` and reads as a
   * panel. Below it the body ground shows through a column that is still
   * `padding-left: var(--rail)` wide; that seam is deliberate and is checked in both
   * themes, because `--paper` and the ground differ.
   *
   * SCOPED, AND THE SCOPE IS THE POINT. The block above this one is unqualified and
   * reaches `base.html` -- all 28 design-v2 pages, whose rails stay viewport height.
   * `943e46b` made exactly that mistake once.
   *
   * MERGED INTO THE RULE THAT WAS ALREADY HERE, NOT ADDED AS A SECOND ONE. The first
   * version of this put `bottom: auto` in its own
   * `body.v3-surface.has-rail .rail { }` beneath the drawer-reset rule, and
   * `test_the_v3_family_defines_each_selector_once` refused it: two rules with the
   * same selector in the same at-rule context, where the later silently overrides the
   * earlier and any reason written above the earlier one stops being enforced. A
   * selector's identity is (at-rule context, selector), and this comment is exactly
   * the kind of reason that would have been the thing silently unenforced. */
  body.v3-surface.has-rail .rail {
    transform: none; visibility: visible; transition: none;
    bottom: auto;
    max-height: 100dvh;
    border-bottom: 1px solid var(--rule);
  }
  body.v3-surface.has-rail .railtoggle { display: none; }
  body.v3-surface.has-rail .masthead .brand { display: none; }
  body.v3-surface.has-rail .railscrim { display: none; }

  body.v3-surface.has-rail .rail__scroll { min-height: 0; }
}

main { padding: 44px 0 90px; }

/* --- section furniture ---------------------------------------------------- */

.eyebrow {
  font-size: 11.5px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  color: var(--gold); margin: 0 0 10px;
}
h1.display { font-size: 40px; line-height: 1.1; font-weight: 800; color: var(--navy); margin: 0 0 14px; letter-spacing: -.02em; }
.lede { font-size: 16.5px; line-height: 1.62; color: var(--ink-mid); max-width: 74ch; margin: 0; }

.keyline {
  border-left: 5px solid var(--gold);
  padding: 4px 0 4px 20px;
  margin: 26px 0 0;
  font-size: 20px; line-height: 1.45; font-weight: 700; color: var(--navy);
  max-width: 82ch;
}
.keyline .quiet { font-weight: 500; color: var(--ink-mid); font-size: 16px; display: block; margin-top: 6px; }

section { margin-top: 56px; }
.sec-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; flex-wrap: wrap; margin-bottom: 18px; }
/* A section head carrying only a right-aligned control -- no title, no rule
   above it. Used where an action belongs to the table BELOW it rather than to
   a section of its own, so it must not open a visual section it does not have. */
.sec-head--tight { margin-bottom: 10px; align-items: center; }
h2.sec { font-size: 23px; font-weight: 750; color: var(--navy); margin: 0; letter-spacing: -.01em; }
.sec-note { font-size: 13.5px; color: var(--ink-mid); margin: 5px 0 0; }

/* TWO CLASSES THE TEMPLATES HAVE ALWAYS ASSUMED AND THE STYLESHEET NEVER HAD.

   `quiet` is used 33 times across the live pages and had a rule only INSIDE
   `.keyline` and `.claimline`. Everywhere else -- the six-cell facts strip on
   the video page, the scope line on every contribution chip, the Actions
   meta row -- it inherited full `--ink`, so "Not connected" printed at exactly
   the brightness of a recorded value. That is not a styling miss: the house
   rule is that "not connected" must read as muted TEXT, and a gap rendered at
   full strength reads as data.

   `small` had no rule anywhere and is used twice on the video page, both times
   for the sentence that says a thing is NOT a clearance.

   Both were invisible to the class guard because both were sitting on its
   `_NOT_STYLED` exemption list, described there as "behaviour hooks or bare
   HTML, not styled components". They are neither: each names a visual rank.
   The exemption is removed with this rule. */
.quiet { color: var(--ink-mid); }
.small { font-size: 12.5px; color: var(--ink-mid); }

/* --- KPI cards ------------------------------------------------------------ */

.kpis { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; }
@media (max-width: 1100px) { .kpis { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px)  { .kpis { grid-template-columns: 1fr; } }

.kpi {
  background: var(--paper); border: 1px solid var(--rule); border-radius: 12px;
  padding: 0; text-align: left; cursor: pointer; font: inherit; color: inherit;
  box-shadow: var(--shadow); overflow: hidden; transition: transform .12s ease, box-shadow .12s ease;
  display: flex; flex-direction: column;
}
/* THE SHADOW HERE WAS ALREADY A LIGHT-THEME VALUE. `rgba(18,41,74,…)` is a navy
   cast left over from when this product was light, and it is correct on white --
   so this is the one place where DARK is the theme that needs the override. */
.kpi:hover { transform: translateY(-2px); box-shadow: 0 2px 4px rgba(18,41,74,.06), 0 16px 32px -14px rgba(18,41,74,.24); }
.kpi:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
.kpi-rule { height: 4px; background: var(--rule-firm); }
.kpi--material .kpi-rule { background: var(--material); }
.kpi--attention .kpi-rule { background: var(--attention); }
.kpi--coverage .kpi-rule { background: var(--coverage); }
/* A CLASS NAMED NEUTRAL PAINTED THE BRAND ACCENT. Under gold that read as
   "notable" and was tolerable; syncRed is a high-alarm colour and cannot carry
   that second meaning, so six inert cards -- "Channels analysed", "Areas
   concluded", "Frames retained" among them -- wore the same value as the
   primary button a reader is meant to press.

   The accent was doing two jobs: brand-and-press, and section emphasis. Only
   the first transfers. This is the second one, and it is now the grey the
   unmarked `.kpi-rule` already uses -- stated explicitly rather than removed,
   because the class still says something true about the card. */
.kpi--neutral .kpi-rule { background: var(--rule-firm); }
.kpi-body { padding: 18px 20px 20px; display: flex; flex-direction: column; height: 100%; }
.kpi-label { font-size: 11.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-mid); margin: 0 0 12px; min-height: 2.6em; line-height: 1.3; }
.kpi-figure { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; min-height: 46px; }
.kpi-num { font-size: 42px; line-height: 1; font-weight: 800; color: var(--navy); letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.kpi--material .kpi-num { color: var(--material); }
.kpi--coverage .kpi-num { color: var(--coverage); }
.kpi-unit { font-size: 13px; font-weight: 650; color: var(--ink); line-height: 1.3; }
.kpi-sub { font-size: 12.5px; color: var(--ink-mid); margin: 9px 0 0; line-height: 1.45; min-height: 2.9em; }
.kpi-trend { margin-top: auto; padding-top: 14px; font-size: 12px; display: flex; align-items: center; gap: 6px; color: var(--ink-mid); }
.kpi-trend .arrow { font-weight: 700; }
.trend--worse .arrow { color: var(--material); }
.trend--better .arrow { color: var(--settled); }
.trend--neutral .arrow { color: var(--ink-mid); }
.kpi-open { font-size: 11.5px; color: var(--gold); font-weight: 700; margin-top: 10px; letter-spacing: .03em; }

/* --- generic cards -------------------------------------------------------- */

.cards { display: grid; gap: 16px; }
.cards--2 { grid-template-columns: 1fr 1fr; }
.cards--3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 900px) { .cards--2, .cards--3 { grid-template-columns: 1fr; } }

.card { background: var(--paper); border: 1px solid var(--rule); border-radius: 12px; padding: 20px 22px; box-shadow: var(--shadow); }
.card-top { border-top: 4px solid var(--gold); border-radius: 12px 12px 0 0; }
.card-top--navy { border-top-color: var(--band); }
.card h3 { font-size: 16px; font-weight: 700; color: var(--navy); margin: 0 0 8px; }
.card p { margin: 0; font-size: 14px; color: var(--ink-mid); line-height: 1.6; }
.card .tag { display: inline-block; background: var(--band); color: var(--band-ink); font-size: 10.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; padding: 3px 9px; border-radius: 4px; margin-bottom: 12px; }
.card .tag--gold { background: var(--gold-soft); color: var(--gold); }

/* --- pattern callout ------------------------------------------------------ */

.pattern {
  background: var(--gold-wash); border: 1px solid var(--gold-soft); border-radius: 12px;
  padding: 22px 24px; display: grid; grid-template-columns: 1fr auto; gap: 22px; align-items: center;
}
@media (max-width: 800px) { .pattern { grid-template-columns: 1fr; } }
.pattern h3 { margin: 0 0 6px; font-size: 18px; color: var(--navy); font-weight: 750; }
/* WAS PALE GOLD, ORPHANED BY THE RETUNE. Explanatory prose under a heading --
   "this concern was not recorded about this video alone" -- so it takes the
   ink ramp's secondary step, not the accent. Instruction 43's acceptance check
   3 greps for exactly this literal and was failing. */
.pattern p { margin: 0; color: var(--ink-mid); font-size: 14.5px; max-width: 76ch; }
.pattern .count { font-size: 13px; color: var(--gold); margin-top: 10px; font-weight: 650; }

/* --- tables --------------------------------------------------------------- */

.table-wrap { background: var(--paper); border: 1px solid var(--rule); border-radius: 12px; overflow: hidden; box-shadow: var(--shadow); }
/* Fixed layout on every live grid.
   Removing the horizontal scroll revealed that these tables never actually fit
   -- they had been overflowing all along and the scrollbar was hiding it, so
   the last column was clipped the moment the scroll went away. Fixed layout
   makes the columns share the page instead of demanding more of it, and a cell
   that cannot fit truncates visibly rather than falling off the edge. */
table.grid { width: 100%; border-collapse: collapse; font-size: 14px; table-layout: fixed; }
table.grid td { overflow-wrap: anywhere; }
/* A chip in a narrow cell wraps rather than being sliced by the column edge. */
table.grid td .chip { white-space: normal; }
/* The first column carries the name and gets the room to show it; the last
   carries the control and takes exactly what the control needs. */
table.grid th:first-child, table.grid td:first-child { width: 22%; }
/* ...UNLESS the first column is a selection checkbox.
   The rule above assumed the first column carries the name. On the prototype
   channel table it is a checkbox, which was handed 22% of the table and
   squeezed the channel name to about five characters -- "Nordvi k Advent ure".
   A checkbox column takes the width of a checkbox, and the column after it is
   the one that carries the name. */
table.grid th:first-child:has(input[type="checkbox"]),
table.grid td:first-child:has(input[type="checkbox"]) { width: 42px; }
table.grid th:first-child:has(input[type="checkbox"]) + th,
table.grid td:first-child:has(input[type="checkbox"]) + td { width: 22%; }
/* A real width, not 1%. Under fixed layout a percentage that small collapses
   the column and the button spills past the table edge -- the same clipping,
   reintroduced by the fix for it. */
/* Wide enough for the longest control any grid puts here -- "Open concerns" on
   the prototype channel table. At 92px it wrapped to three lines. Fixed layout
   needs a real number; `width: 1%` collapses the column instead of shrinking to
   fit the way it would under auto layout. */
table.grid th:last-child, table.grid td:last-child { width: 136px; white-space: nowrap; }
table.grid td:last-child .btn, table.grid td:last-child .chip,
table.grid td:last-child a, table.grid td:last-child button { white-space: nowrap; }
/* Under fixed layout a `nowrap` cell wider than its column does not widen the
   column, it OVERFLOWS it -- channel names were being drawn on top of the
   published dates. It has to clip. */
table.grid td.nowrap, table.grid td .cell-sub, table.grid td .cell-title {
  overflow: hidden; text-overflow: ellipsis;
}
table.grid td.nowrap { max-width: 0; }
table.grid thead th {
  background: var(--band); color: var(--band-ink); text-align: left; font-size: 11px; font-weight: 700;
  letter-spacing: .09em; text-transform: uppercase; padding: 12px 16px; white-space: nowrap;
}
table.grid td { padding: 14px 16px; border-bottom: 1px solid var(--rule); vertical-align: middle; }
table.grid tbody tr:last-child td { border-bottom: 0; }
table.grid tbody tr.rowlink { cursor: pointer; transition: background .1s ease; }
table.grid tbody tr.rowlink:hover { background: var(--raised); }
table.grid tbody tr.rowlink:focus-visible { outline: 3px solid var(--gold); outline-offset: -3px; }
.cell-title { font-weight: 650; color: var(--navy); }
.cell-sub { font-size: 12.5px; color: var(--ink-mid); margin-top: 3px; }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.nowrap { white-space: nowrap; }

/* --- severity + status chips --------------------------------------------- */

.chip {
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
  font-size: 12px; font-weight: 650; padding: 3px 10px; border-radius: 999px; border: 1px solid;
}
.chip .dot { font-size: 9px; line-height: 1; }
.chip--material { color: var(--material); background: var(--material-wash); border-color: var(--material-line); }
.chip--attention { color: var(--attention); background: var(--attention-wash); border-color: var(--attention-line); }
.chip--settled { color: var(--settled); background: var(--settled-wash); border-color: var(--settled-line); }
.chip--coverage { color: var(--coverage); background: var(--coverage-wash); border-color: var(--coverage-line); }
.chip--plain { color: var(--ink-mid); background: var(--raised); border-color: var(--chip-edge); }

/* --- dashboard scan-date quick links -------------------------------------- */
/* LINKS INTO the queue, not filters of the dashboard. The queue's own control
   is a <select> and it is a tab-like thing because a table answers it; nothing
   on this page changes when one of these is pressed, so they are pills. A tab
   strip here would promise a response the page cannot give. */
.winchips { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; margin: 12px 0 0; }
.winchips-label { font-size: 13px; color: var(--ink-mid); }
a.chip { text-decoration: none; }
a.chip:hover { color: var(--ink); border-color: var(--ink-faint); }

.statuspill {
  display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 600;
  padding: 5px 12px; border-radius: 999px; background: var(--raised); border: 1px solid var(--rule-firm); color: var(--ink-mid);
}
.statuspill::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
/* THE `new` / `open` STATUS FAMILY. Not one of the four semantics -- it is its
   own amber, and mapping it onto `--attention*` would change the DARK theme's
   appearance, which the boundary forbids. So it gets light values inline and a
   dark override below. */
.st-new { color: #6d4a12; background: #fbf3e4; border-color: #e0c48d; }
/* The LIVE status vocabulary is four states, not the shell's eight. `open` and
   `in_progress` join the two nearest shell states rather than inventing two more
   colours -- and neither is the only carrier of meaning: every pill prints its
   own label beside the dot. `resolved` and `dismissed` already have rules below
   and are deliberately NOT green-as-in-cleared: `settled` is the operator's
   state, and a channel's result is drawn from the assessment, never from here. */
.st-open { color: #6d4a12; background: #fbf3e4; border-color: #e0c48d; }
.st-under_review, .st-action_in_progress, .st-in_progress { color: var(--attention); background: var(--attention-wash); border-color: var(--attention-line); }
.st-creator_notified, .st-waiting_creator { color: var(--coverage); background: var(--coverage-wash); border-color: var(--coverage-line); }
.st-escalated { color: var(--material); background: var(--material-wash); border-color: var(--material-line); }
.st-resolved { color: var(--settled); background: var(--settled-wash); border-color: var(--settled-line); }
.st-dismissed { color: var(--ink-mid); }

/* --- buttons -------------------------------------------------------------- */

.btn {
  display: inline-flex; align-items: center; gap: 8px; font: inherit; font-size: 13.5px; font-weight: 650;
  padding: 9px 16px; border-radius: 8px; border: 1px solid var(--rule-firm); background: var(--paper);
  color: var(--navy); cursor: pointer; text-decoration: none; transition: all .12s ease;
}
.btn:hover { border-color: var(--navy); }
.btn--primary { background: var(--band); color: var(--band-ink); border-color: var(--rule-firm); }
.btn--primary:hover { background: #35405A; }
.btn--gold { background: var(--gold); color: #ffffff; border-color: var(--gold); }
.btn--gold:hover { filter: brightness(1.05); }
.btn:disabled { opacity: .4; cursor: not-allowed; }
.btn--sm { padding: 6px 12px; font-size: 12.5px; }

/* --- timestamp play control ---------------------------------------------- */

/* ONE DEFINITION, AND IT USED TO BE TWO, FIVE HUNDRED LINES APART.
   `.tsplay` was declared here as a FILLED button and again near the end of the
   file as a BARE link, at equal specificity. The later one won the base --
   stripping the background, the border and the padding and changing the colour --
   while THIS one's `:hover { background: #35405A }` survived, because the two set
   different properties. The element landed on `var(--ink)` over `#35405A`:
   9.90:1 in dark, 1.69:1 in light. A light theme did not cause that; it revealed
   it.

   AND THE COLLISION WAS WIDER THAN THE HOVER. `--block` and `--ghost` were built
   on the filled base, so the duplicate silently took their background, border and
   padding too. Both carry their own now and depend on nothing that can be
   redefined out from under them.

   THE BARE FORM IS THE ONE THAT SHIPS -- it is what the later rule made render --
   so it is the base rather than a third variant. `#35405A` survives only on
   `--block`, where `--band-ink` sits on it at 9.05:1 in either theme, which is
   the `--band`/`--band-ink` pairing this file already gets right. */
.tsplay {
  display: inline-flex; align-items: center; gap: 8px; font: inherit; font-size: 14px; font-weight: 700;
  cursor: pointer; font-variant-numeric: tabular-nums; letter-spacing: .01em;
  background: none; border: 0; padding: 0; color: var(--gold);
}
.tsplay:hover { color: var(--ink); }
.tsplay .tri { color: var(--gold); font-size: 12px; }
.tsplay--ghost {
  background: transparent; color: var(--ink); border: 1px solid var(--rule-firm);
  padding: 8px 15px; border-radius: 8px;
}
.tsplay--ghost:hover { background: var(--gold-wash); border-color: var(--gold); color: var(--ink); }

/* --- tabs ----------------------------------------------------------------- */

.tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--rule); margin: 30px 0 26px; flex-wrap: wrap; }
.tab {
  font: inherit; font-size: 14px; font-weight: 600; color: var(--ink-mid);
  background: none; border: 0; border-bottom: 3px solid transparent;
  padding: 11px 18px; cursor: pointer; margin-bottom: -1px;
}
.tab:hover { color: var(--navy); }
.tab[aria-selected="true"] { color: var(--navy); border-bottom-color: var(--gold); }
/* The count beside a heading or a tab.
   This rule was scoped to `.tab` alone, so `<h2 class="sec">Evidence cited
   <span class="badge">7</span></h2>` rendered as "Evidence cited7" -- a bare
   number welded to the last word. The structural test could not see it: the
   selector `.tab .badge` contains `.badge`, so the class counted as defined
   while applying nowhere else. */
.badge { display: inline-block; margin-left: 8px; font-size: 12px; background: var(--band); color: var(--band-ink); border-radius: 999px; padding: 1px 8px; font-weight: 700; vertical-align: middle; }
.tab .badge { display: inline-block; margin-left: 7px; font-size: 11px; background: var(--band); color: var(--band-ink); border-radius: 999px; padding: 1px 7px; font-weight: 700; }
.tabpanel[hidden] { display: none; }

/* --- concern list item ---------------------------------------------------- */

.clist { display: flex; flex-direction: column; gap: 12px; }
.citem {
  display: grid; grid-template-columns: auto 1fr auto; gap: 18px; align-items: center;
  background: var(--paper); border: 1px solid var(--rule); border-left: 4px solid var(--rule-firm);
  border-radius: 10px; padding: 16px 20px; cursor: pointer; text-align: left; font: inherit; color: inherit;
  box-shadow: var(--shadow); transition: transform .1s ease, border-color .1s ease;
}
.citem:hover { transform: translateX(2px); border-color: var(--gold); }
.citem--material { border-left-color: var(--material); }
.citem--attention { border-left-color: var(--attention); }
.citem h4 { margin: 0 0 4px; font-size: 15.5px; font-weight: 700; color: var(--navy); }
.citem .meta { font-size: 12.5px; color: var(--ink-mid); display: flex; gap: 12px; flex-wrap: wrap; }
.citem .cid { font-size: 11.5px; font-weight: 700; color: var(--ink-mid); letter-spacing: .06em; font-variant-numeric: tabular-nums; }

/* --- full-screen concern viewer ------------------------------------------ */

.viewer[hidden] { display: none; }
.viewer {
  position: fixed; inset: 0; z-index: 120; background: var(--ground);
  display: flex; flex-direction: column;
}
.viewer-bar {
  background: var(--band); color: var(--band-ink); padding: 0;
  border-bottom: 3px solid var(--gold);
}
.viewer-bar .wrap { display: flex; align-items: center; gap: 18px; min-height: 60px; }
.pager { display: flex; align-items: center; gap: 10px; }
.pager-count { font-size: 13.5px; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: .04em; min-width: 74px; }
.pager button {
  font: inherit; font-size: 13px; font-weight: 650; color: #fff; background: rgba(255,255,255,.1);
  border: 1px solid rgba(255,255,255,.24); border-radius: 7px; padding: 7px 14px; cursor: pointer;
}
.pager button:hover:not(:disabled) { background: rgba(255,255,255,.2); }
.pager button:disabled { opacity: .32; cursor: not-allowed; }
/* `.viewer-bar` is `var(--band)` -- stable in all three blocks -- so its text
   must be stable too. `var(--ink-mid)` here was 11.81:1 in dark and 2.60:1 in
   light. Found by `test_theme_surface_and_text_agree`, not by looking. */
.viewer-setname { font-size: 12px; color: color-mix(in srgb, var(--band-ink) 78%, var(--band)); letter-spacing: .05em; text-transform: uppercase; font-weight: 600; }
.viewer-close {
  margin-left: auto; font: inherit; font-size: 14px; color: #fff; background: none;
  border: 1px solid rgba(255,255,255,.28); border-radius: 7px; padding: 7px 14px; cursor: pointer;
}
.viewer-close:hover { background: rgba(255,255,255,.14); }
.viewer-scroll { overflow-y: auto; flex: 1; padding: 34px 0 80px; }

.vhead { display: grid; grid-template-columns: 1fr 320px; gap: 40px; align-items: start; }
@media (max-width: 1000px) { .vhead { grid-template-columns: 1fr; } }

.vtitle { font-size: 29px; line-height: 1.22; font-weight: 800; color: var(--navy); margin: 10px 0 14px; letter-spacing: -.02em; }
.vwhere { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; font-size: 13.5px; color: var(--ink-mid); margin-bottom: 18px; }
.vwhere a { color: var(--navy); font-weight: 650; text-decoration: none; border-bottom: 1px solid var(--rule-firm); }
.vwhere a:hover { border-bottom-color: var(--gold); }

.qa { margin-top: 30px; }
.qa h3 {
  font-size: 11.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--gold); margin: 0 0 9px;
}
.qa p { margin: 0; font-size: 15.5px; line-height: 1.66; color: var(--ink); max-width: 78ch; }

.policy {
  background: var(--paper); border: 1px solid var(--rule); border-left: 4px solid var(--gold);
  border-radius: 10px; padding: 18px 22px; margin-top: 10px;
}
.policy .pname { font-weight: 700; color: var(--navy); font-size: 15px; margin: 0 0 8px; }
.policy .pbody { font-size: 14px; color: var(--ink-mid); margin: 0; line-height: 1.6; }
.policy .pkind { font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-mid); margin-top: 12px; }

.doit {
  background: var(--raised); color: var(--ink); border: 1px solid var(--rule-firm); border-radius: 12px; padding: 24px 26px; margin-top: 10px;
}
.doit h3 { color: var(--gold); }
.doit p { color: var(--ink-mid); font-size: 16px; line-height: 1.6; margin: 0; max-width: 74ch; }
.doit .prio { display: inline-block; margin-top: 14px; font-size: 11.5px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; background: var(--gold); color: #ffffff; padding: 4px 11px; border-radius: 5px; }

/* --- evidence ------------------------------------------------------------- */

.evidence { display: grid; grid-template-columns: 300px 1fr; gap: 24px; align-items: start;
  background: var(--paper); border: 1px solid var(--rule); border-radius: 12px; padding: 20px; margin-bottom: 14px; }
@media (max-width: 800px) { .evidence { grid-template-columns: 1fr; } }
.evframe { position: relative; border-radius: 8px; overflow: hidden; background: #000; cursor: pointer; border: 0; padding: 0; display: block; width: 100%; }
.evframe img { display: block; width: 100%; height: auto; }
.evframe .play {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,.42); color: #fff; font-size: 34px; opacity: 0; transition: opacity .15s ease;
}
.evframe:hover .play { opacity: 1; }
.evstamp { position: absolute; left: 8px; bottom: 8px; background: rgba(0,0,0,.82); color: #fff;
  font-size: 11.5px; font-weight: 700; padding: 3px 8px; border-radius: 4px; font-variant-numeric: tabular-nums; }
.evlabel { font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-mid); margin: 0 0 6px; }
.evscene { font-size: 14.5px; color: var(--ink); margin: 0 0 16px; line-height: 1.6; }
.transcript {
  background: var(--gold-wash); border-left: 3px solid var(--gold); border-radius: 0 8px 8px 0;
  /* FULL INK, NOT THE SECONDARY STEP. This is QUOTED SPEECH from a video --
     the evidence itself, not a note about it -- and it sits on `--gold-wash`,
     which the retune moved from a warm brown to a deep red. Pale gold on that
     is barely there, and a transcript a reader cannot read is evidence
     withheld. */
  padding: 12px 16px; font-size: 14.5px; color: var(--ink); font-style: italic; margin: 0 0 16px; line-height: 1.55;
}
.support { list-style: none; margin: 0; padding: 0; font-size: 13px; color: var(--ink-mid); }
.support li { padding: 4px 0 4px 18px; position: relative; }
.support li::before { content: "▸"; position: absolute; left: 0; color: var(--gold); }

/* --- workflow panel ------------------------------------------------------- */

/* The live concern overlay, two columns. The reading keeps a measure on the
   left; the decision panel rides in a sticky aside on the right, in the space
   the single-column version was leaving empty. Same proportions as the mock's
   `.vhead`, so the two surfaces do not drift apart. */
/* A form's answer, inside the form. Refusals used to render in `.sec-note` --
   the page's caption grey -- above the section heading, where a reader looking
   at the field they just changed never sees it. */
/* The saved state of a submit button: it reads "Saved" and stops inviting a
   press, without going disabled -- the form is still submittable, and a control
   that cannot be pressed after a save is a control that cannot be pressed twice
   on purpose. */
/* A credential shown once. Deliberately the loudest block on the page: it is the
   only moment this string exists anywhere a person can read it, and a reader who
   scrolls past it has lost it. `user-select: all` so one click selects the whole
   password rather than half of it. */
/* Sign out lives in the account menu as a POST, because a GET logout is a link
   any page can fire on a reader's behalf -- and Django refuses it outright. */
/* The member edit dialog. One place to change a person, rather than three
   controls scattered across their row. */
.editdlg { width: min(560px, calc(100vw - 40px)); padding: 0; border: 1px solid var(--rule);
  border-radius: 14px; background: var(--raised); color: var(--ink); }
.editdlg::backdrop { background: rgba(6, 8, 13, .66); }
.editdlg__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  padding: 18px 20px; border-bottom: 1px solid var(--rule); }
.editdlg__title { margin: 2px 0 3px; font-size: 20px; font-weight: 750; color: var(--ink); }
.editdlg__body { padding: 18px 20px; }
.editdlg__danger { padding: 14px 20px 18px; border-top: 1px solid var(--rule);
  background: var(--paper); border-radius: 0 0 13px 13px; }
.editdlg__danger .inlineform { gap: 8px; flex-wrap: wrap; }
.editdlg__danger p { margin: 9px 0 0; }
.editdlg .formfoot { margin-top: 16px; display: flex; gap: 10px; align-items: center; }

.acctform { margin: 0; }
.acctitem--action { width: 100%; text-align: left; background: none; border: 0; font: inherit;
  cursor: pointer; }

.credential { border: 1px solid var(--gold); border-radius: 12px; padding: 16px 18px;
  background: var(--raised); margin: 0 0 20px; }
.credential__head { margin: 0 0 12px; font-size: 14px; line-height: 1.55; color: var(--ink); }
.credential__pair { display: grid; grid-template-columns: max-content 1fr; gap: 6px 18px; margin: 0 0 12px; }
.credential__pair dt { color: var(--ink-mid); font-size: 12.5px; }
.credential__pair dd { margin: 0; }
.credential__pair code { font-size: 13.5px; color: var(--ink); }
.credential__secret { user-select: all; font-size: 15px !important; letter-spacing: .02em;
  padding: 3px 8px; border-radius: 6px; background: var(--paper); border: 1px solid var(--rule-firm); }
.inlineform { display: inline-flex; gap: 6px; align-items: center; }

.btn--saved { border-color: var(--settled); color: var(--settled); background: var(--settled-wash); }
.btn--saved:hover { border-color: var(--settled); color: var(--settled); }

.formnotice { display: inline-block; margin: 0; padding: 8px 13px; border-radius: 9px;
  font-size: 13.5px; line-height: 1.45; border: 1px solid var(--rule); color: var(--ink); }
.formnotice b { font-weight: 700; }
/* The refusal carries a wash as well as a border: it is the one that has to
   survive being read at a glance, because the reader believes their change
   landed until this tells them otherwise. */
.formnotice--refused { border-color: var(--attention); background: var(--attention-wash, transparent); }
.formnotice--refused b { color: var(--attention); }
.formnotice--saved { border-color: var(--settled); background: var(--settled-wash); }
.formnotice--saved b { color: var(--settled); }

/* 44.95rem WAS `76ch`, and a reading column is a question about the box rather
   than about the digit zero. Instruction 113's layout family.

   Unlike the two table columns below, this one resolved unambiguously: only one
   element carries the declaration, so 76ch at its 15px is exactly 719.18px and
   `grid-template-columns` computed to `minmax(0px, 719.18px)`. 44.95rem is
   719.2px at the 16px root. DELTA +0.02px -- the unit changed, the layout did
   not. */
.lvgrid { display: grid; grid-template-columns: minmax(0, 44.95rem) 340px; gap: 44px;
  align-items: start; justify-content: start; }
.lvgrid__read { min-width: 0; }
.lvgrid__manage { min-width: 0; position: sticky; top: 84px; }

@media (max-width: 1100px) {
  /* Stacked, where 340px would leave the reading column too narrow for a
     policy quotation. */
  .lvgrid { grid-template-columns: minmax(0, 1fr); }
  .lvgrid__manage { position: static; }
}

.workflow { background: var(--paper); border: 1px solid var(--rule); border-radius: 12px; padding: 22px; position: sticky; top: 84px; }
.workflow h3 { font-size: 11.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--gold); margin: 0 0 14px; }
.field { margin-bottom: 15px; }
.field label { display: block; font-size: 11.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-mid); margin-bottom: 5px; }
.field select, .field input[type="date"], .field textarea, .field input[type="text"] {
  width: 100%; font: inherit; font-size: 14px; padding: 9px 11px; border: 1px solid var(--rule-firm);
  border-radius: 7px; background: var(--raised); color: var(--ink);
}
.field select:focus, .field input:focus, .field textarea:focus { outline: 2px solid var(--gold); outline-offset: -1px; }
.field textarea { min-height: 74px; resize: vertical; line-height: 1.5; }
.subfield { margin-top: 10px; padding-left: 12px; border-left: 3px solid var(--gold); }
.subfield[hidden] { display: none; }

.notes { margin-top: 6px; display: flex; flex-direction: column; gap: 10px; }
.note {
  background: var(--ground); border: 1px solid var(--rule); border-radius: 8px; padding: 11px 13px; font-size: 13px; color: var(--ink-mid); line-height: 1.55;
}
.note .who { font-weight: 700; color: var(--navy); font-size: 12.5px; }
.note .when { color: var(--ink-mid); font-size: 11.5px; margin-left: 6px; }

.wf-divider { height: 1px; background: var(--rule); margin: 20px 0; }
.saved { font-size: 12px; color: var(--settled); font-weight: 650; margin-top: 10px; }
.saved[hidden] { display: none; }

/* --- video modal ---------------------------------------------------------- */

.vmodal[hidden] { display: none; }
.vmodal { position: fixed; inset: 0; z-index: 200; background: rgba(0,0,0,.82); display: flex; align-items: center; justify-content: center; padding: 32px; }
/* PINNED, AND ITS TEXT IS PINNED WITH IT -- one class doing two jobs, split.

   THE RULE, ONCE: a surface stable across themes must carry stable text; a
   surface that flips must carry flipping text. This was `#0A0C11` -- stable --
   under `color: var(--ink)`, which flips: 18.74:1 in dark and **1.12:1 in
   light**. Not weak text. Text that is not there.

   TWO OF THE THREE CALLERS ARE MEDIA LIGHTBOXES and keep the pinned ground,
   because chrome around footage is black in every product that has one:
   `_vmodal.html` holds a `<video>`; `real_video.html:1333` an `<img>` frame
   enlargement with a pager. THE THIRD IS A FORM DIALOG --
   `_manage_video_review.html`: three selects, a date input, a text field and a
   submit, a page surface the reader types INTO rather than watches. It takes
   `.vmodal-inner--form`, below.

   A MODIFIER RATHER THAN TWO CLASSES, because the two share `.vmodal` (the
   scrim), `.vmodal-head` (title, sub-line, close) and `.vmodal-foot`. Only the
   ground and the ink differ, which is one modifier's worth of difference.

   `--band-ink` IS THE STABLE LIGHT INK AND IT ALREADY EXISTS -- `#edf0f6` in all
   three blocks, this file's own model for light text on a dark surface. §20
   forbids a new property for a colour that already has one and this is that
   colour: 17.13:1 here, identical in both themes. The muted sub-line and foot
   take `color-mix()` over the same token rather than a second name. */
/* THE LIGHTBOX DECLARES ITS OWN INK, AND THIS IS DEFENCE RATHER THAN A REPAIR.
   Today `.vmodal-head`, the `<video>` and `.vmodal-foot` tile this box completely,
   so nothing unclassed inherits onto the pinned ground and the guard names it
   zero times. But inherited ink is the one thing the guard cannot see -- it reads
   declarations, and an absent `color` is not a declaration. Were any bare text
   added here it would arrive as `--ink`: 1.12:1 on this ground in light, the worst
   ratio measured anywhere in this sequence, and silent. The `--form` variant
   re-grounds to `--paper`, which flips, so it takes flipping ink -- pinned ground
   pinned ink, moving ground moving ink, stated once in the CSS instead of held in
   a comment. `.vmodal-body` renders only inside `--form`, so it is covered. */
.vmodal-inner { background: #0A0C11; color: var(--band-ink); border-radius: 14px; overflow: hidden; width: min(940px, 100%); box-shadow: 0 40px 80px -20px rgba(0,0,0,.6); }
.vmodal-head { display: flex; align-items: center; gap: 14px; padding: 14px 20px; color: var(--band-ink); border-bottom: 1px solid rgba(255,255,255,.1); }
.vmodal-head .vm-title { font-size: 14.5px; font-weight: 700; }
.vmodal-head .vm-sub { font-size: 12.5px; color: color-mix(in srgb, var(--band-ink) 72%, #0A0C11); }
/* The white alpha border and `#fff` were ALREADY correct on a pinned surface. */
.vmodal-close { margin-left: auto; background: none; border: 1px solid rgba(255,255,255,.28); color: #fff; border-radius: 7px; padding: 6px 13px; cursor: pointer; font: inherit; font-size: 13px; }
.vmodal video { display: block; width: 100%; background: #000; }
.vmodal-foot { padding: 12px 20px 16px; color: color-mix(in srgb, var(--band-ink) 78%, #0A0C11); font-size: 12.5px; display: flex; gap: 16px; align-items: center; flex-wrap: wrap; }
/* `var(--gold)` was 4.97:1 in dark and 3.11:1 in light on this pinned ground, and
   there is no stable accent token to pin it to. The weight and the tabular
   figures carry the emphasis instead -- which is this product's rule anyway: a
   colour is never the only carrier of meaning. */
.vmodal-foot .clipband { color: var(--band-ink); font-weight: 700; font-variant-numeric: tabular-nums; }

/* THE FORM DIALOG. A page surface, so it follows the theme -- and every piece of
   ink on it goes back to a flipping token, because the ground flips. */
.vmodal-inner--form { background: var(--paper); color: var(--ink); }
.vmodal-inner--form .vmodal-head { color: var(--ink); border-bottom-color: var(--rule); }
.vmodal-inner--form .vmodal-head .vm-sub { color: var(--ink-mid); }
.vmodal-inner--form .vmodal-close { border-color: var(--rule-firm); color: var(--ink); }
.vmodal-inner--form .vmodal-foot { color: var(--ink-mid); }

/* A dialog that holds a FORM rather than a frame. The lightbox body is an image
   on the modal's own dark ground; a form is not -- every control in `.field`
   and `.vwf__*` was drawn for `--paper`, so the body carries that ground with
   it rather than the form being restyled for the dark one.

   It scrolls, because the panel this replaced was 635px tall and a dialog that
   pushes its own Record button off-screen has moved the problem rather than
   solved it. */
.vmodal-body { background: var(--paper); max-height: 68vh; overflow-y: auto; padding: 18px 20px 20px; }
/* `.vwf` rides along so every descendant rule still matches, but its resting
   frame -- the margin, the gold left edge, the shadow -- belonged to the
   section it is no longer. */
.vmodal-body.vwf { margin: 0; border: 0; border-radius: 0; box-shadow: none; }

/* --- video page timeline -------------------------------------------------- */

.player-shell { background: #0A0C11; border-radius: 14px; overflow: hidden; box-shadow: var(--shadow); }
.player-shell video { display: block; width: 100%; background: #000; }

.timeline { background: var(--paper); border: 1px solid var(--rule); border-radius: 12px; padding: 20px 22px 16px; margin-top: 16px; box-shadow: var(--shadow); }
.timeline h3 { font-size: 11.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--gold); margin: 0 0 16px; }
.track { position: relative; height: 44px; background: #262A33; border-radius: 8px; cursor: pointer; overflow: hidden; }
.track-played { position: absolute; left: 0; top: 0; bottom: 0; background: color-mix(in srgb, var(--gold) 22%, transparent); width: 0; }
/* PINNED MEDIA CHROME, AND ITS TEXT PINNED WITH IT. `.track` is `#262A33` and
   stays: it is media chrome under the player on both callers
   (`video.html:36`, `real_video.html:995`). The SURFACE was never the problem --
   `var(--ink)` over it was 13.77:1 in dark and 1.21:1 in light. `--band-ink` is
   12.59:1, identical in both themes. */
.track-head { position: absolute; top: -3px; bottom: -3px; width: 2px; background: var(--band-ink); left: 0; }
.marker {
  position: absolute; top: 6px; bottom: 6px; border-radius: 4px; border: 0; padding: 0; cursor: pointer;
  min-width: 6px;
}
/* A MARKER IS A SHAPE ON A PINNED TRACK, so it needs a stable EDGE. Its fill is
   a semantic token and therefore flips: against `#262A33` that is 5.70 and 7.54
   in dark but 1.97 and 1.95 in light, so in light the shape dissolves into the
   track it sits on. Its own label is already fine either way -- `.mlabel` is
   `--paper`, 7.29 and 7.35 in light -- so what is missing is the outline, not the
   text. A stable `--band-ink` hairline is 12.59:1 on the track in both themes.
   It adds a hairline to the dark rendering, which is a visible change and is
   reported rather than slipped in. */
/* FOUR TONES, BECAUSE THE MAP PRODUCES FOUR. `SEVERITY_TONES` sends high ->
   material, medium -> attention, low AND none -> plain, not_applicable -> quiet.
   Two had rules. `plain` is 95 of the 117 markers this corpus draws, across 45
   (run, video) pairs -- the majority marker rendered as a 6px button with no
   fill and no border, invisible since markers existed. `material` and `quiet`
   draw ZERO markers today, and they get rules for exactly that reason: a tone
   with no data is the one that ships unstyled, because nothing renders it for
   anybody to notice. That is how `plain` got here.

   THE FILLS ARE PINNED, AND THE ARITHMETIC FORCED IT, not taste. `.track` is
   `#262A33` in both themes, so a marker on it takes stable colour. Each fill is
   its semantic token's DARK value held still, because the track is dark in both
   themes and the LIGHT values cannot do the job: `--material` light is 1.97:1 on
   this track and `--attention` light is 1.95:1, both under the 3:1 a non-text
   indicator needs. Pinned to the dark values they are 5.70 and 7.54.

   THE SIGNAL IS ON THE 32px AXIS, NOT THE 6px ONE, and that is the whole design.
   A marker is `width: 6px` on a 44px track with `top/bottom: 6px` -- so it is
   6 x 32, and 6px is the constrained axis while 32px is free. Colour alone
   CANNOT carry four categories here: everything clearing 3:1 on a dark ground is
   necessarily light, which compresses the luminance range, and the best possible
   four-fill greyscale separation from this palette is 1.54 -- material against
   quiet is 1.06, indistinguishable. So geometry carries and colour reinforces:

     material   uniform fill                      5.70 on track
     attention  band-ink CAP over the amber       12.59 cap / 7.54 body
     plain      uniform fill, neutral, no hue     12.59 on track
     quiet      HATCHED, indigo over track        6.04 on the stripe

   Four appearances that survive greyscale: uniform-mid, capped-two-zone,
   uniform-bright, striped. `plain` is neutral so it cannot read as severity, and
   at 12.59:1 over 6 x 32 it cannot read as absent either. `quiet` is `--coverage`
   because that is the tone meaning "a limit on what we can say", and a hatch is
   the conventional shape for not-applicable rather than a verdict -- CONSTRAINTS
   3, both directions.

   The height is NOT the channel, deliberately: shortening a marker would shrink
   an already 6px-wide hit target. Every tone stays the full 32px and clickable;
   the gradient paints inside it. */
.marker--material,
.marker--attention,
.marker--plain,
.marker--quiet { border: 1px solid var(--band-ink); }
.marker--material { background: #F08579; }
.marker--attention { background: linear-gradient(#EDF0F6 0 7px, #E5B45F 7px 100%); }
.marker--plain { background: #EDF0F6; }
.marker--quiet {
  background: repeating-linear-gradient(180deg, #A0A2E8 0 3px, #262A33 3px 6px);
}
.marker:hover { filter: brightness(1.18); }
.marker:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
.ticks { display: flex; justify-content: space-between; font-size: 11px; color: var(--ink-mid); margin-top: 7px; font-variant-numeric: tabular-nums; }
.tl-legend { display: flex; gap: 18px; flex-wrap: wrap; font-size: 12.5px; color: var(--ink-mid); margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--rule); }
.tl-legend span { display: inline-flex; align-items: center; gap: 7px; }
.tl-swatch { width: 14px; height: 8px; border-radius: 2px; display: inline-block; }

/* --- misc ----------------------------------------------------------------- */

.meta-strip { display: flex; gap: 22px; flex-wrap: wrap; font-size: 13.5px; color: var(--ink-mid); margin-top: 10px; }
.meta-strip b { color: var(--navy); font-weight: 650; }

.families { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 12px; }
.fam { background: var(--paper); border: 1px solid var(--rule); border-radius: 10px; padding: 14px 16px; }
.fam h4 { margin: 0 0 6px; font-size: 14px; font-weight: 700; color: var(--navy); }
.fam .areas { font-size: 12.5px; color: var(--ink-mid); line-height: 1.5; }
.fam--not_applicable { opacity: .62; }
.fam--incomplete { border-color: var(--coverage-line); background: var(--coverage-wash); }

.empty { background: var(--paper); border: 1px dashed var(--rule-firm); border-radius: 12px; padding: 32px; text-align: center; color: var(--ink-mid); font-size: 14.5px; }

.footnote { margin-top: 60px; padding-top: 22px; border-top: 1px solid var(--rule); font-size: 12.5px; color: var(--ink-mid); display: flex; justify-content: space-between; gap: 20px; flex-wrap: wrap; }
.footnote b { color: var(--navy); }

.backlink { display: inline-flex; align-items: center; gap: 7px; font-size: 13.5px; color: var(--ink-mid); text-decoration: none; margin-bottom: 18px; }
.backlink:hover { color: var(--navy); }

/* --- alignment corrections ------------------------------------------------ */

/*
  The family cards were ragged: each one sized to its own text, so the state
  chips landed at four different heights across a row and the grid read as
  broken rather than as a set. The cards now stretch to the tallest in the row
  and the chip is pinned to the bottom of each, which is what makes a row of
  cards scan as one row.
*/
.families { align-items: stretch; }
.fam { display: flex; flex-direction: column; }
.fam .areas { flex: 1; margin: 0 0 12px; }
.fam .fam-state { margin-top: auto; }

/* Same rule for the concern list: the chips on the right were vertically
   centred against items of different heights, so they drifted. */
.citem { align-items: start; }
.citem > span:last-child { align-self: center; }

/* Table cells align to the top so a two-line title does not push a one-line
   neighbour off the baseline. */
table.grid td { vertical-align: top; }
table.grid td .chip, table.grid td .statuspill { margin-top: 1px; }
table.grid td.nowrap { vertical-align: middle; }
table.grid tbody tr[hidden] { display: none; }

/* KPI cards: equal height, and the trend line pinned to the bottom so the
   five read as one instrument panel rather than five separate boxes. */
.kpis { align-items: stretch; }
.kpi { height: 100%; }

/* The meta strip under a video title wrapped unevenly against the title. */
.meta-strip { align-items: baseline; row-gap: 6px; }

/* --- pagination ----------------------------------------------------------- */

.pagebar {
  display: flex; align-items: center; justify-content: space-between; gap: 18px;
  flex-wrap: wrap; margin-top: 14px; padding: 0 2px;
}
.pagecount { font-size: 13px; color: var(--ink-mid); }
.pagebtns { display: flex; align-items: center; gap: 8px; }
.pagenums { display: flex; gap: 4px; }
.pagenum {
  font: inherit; font-size: 13px; font-weight: 650; min-width: 32px; height: 32px;
  border: 1px solid var(--rule-firm); background: var(--raised); color: var(--ink-mid);
  border-radius: 7px; cursor: pointer; font-variant-numeric: tabular-nums;
}
.pagenum:hover { border-color: var(--gold); color: var(--ink); }
.pagenum.is-on { background: var(--gold); border-color: var(--gold); color: #ffffff; }

/* --- timeline marker focus ------------------------------------------------ */

/* THE ACTIVE OUTLINE, on the same pinned `#262A33` track as the hairline above.
   `--ink` measured 1.21:1 there in light -- the selected marker was the one you
   could not see. Only the JavaScript adds this class, so no template walk finds
   it and nothing but the guard's script-applied expansion would have. 12.59:1. */
.marker--active { outline: 2px solid var(--band-ink); outline-offset: 2px; }

/* Status pills must not wrap: "Under review" broke to two lines and the row
   grew around it, which is what made the table look ragged. */
.statuspill { white-space: nowrap; }
table.grid td .statuspill { display: inline-flex; }

/* --- video page layout ---------------------------------------------------- */

/*
  The findings column carries the full detail now, so the player column is
  narrower and sticks while the reader works down the drawers -- scrubbing to
  02:18 is useless if the player has scrolled away.
*/
.video-grid { display: grid; grid-template-columns: 460px 1fr; gap: 32px; align-items: start; }
@media (max-width: 1080px) { .video-grid { grid-template-columns: 1fr; } }
.video-grid > div:first-child { position: sticky; top: 78px; }
@media (max-width: 1080px) { .video-grid > div:first-child { position: static; } }

/* --- concern drawers ------------------------------------------------------ */

.cdrawer {
  background: var(--paper); border: 1px solid var(--rule);
  border-left: 4px solid var(--rule-firm); border-radius: 10px;
  box-shadow: var(--shadow); overflow: hidden;
}
.cdrawer--material { border-left-color: var(--material); }
.cdrawer--attention { border-left-color: var(--attention); }
.cdrawer + .cdrawer { margin-top: 12px; }

.cdrawer-head {
  display: flex; align-items: flex-start; gap: 16px; justify-content: space-between;
  padding: 16px 20px; cursor: pointer; list-style: none;
}
.cdrawer-head::-webkit-details-marker { display: none; }
.cdrawer-head:hover { background: var(--raised); }
.cdrawer-head:focus-visible { outline: 3px solid var(--gold); outline-offset: -3px; }
.cdrawer-title { display: block; font-size: 15.5px; font-weight: 700; color: var(--ink); margin-bottom: 6px; }
.cdrawer-main .meta { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; font-size: 12.5px; color: var(--ink-mid); }
.cdrawer-right { display: flex; align-items: center; gap: 12px; flex-shrink: 0; }
.cdrawer-caret { color: var(--ink-mid); font-size: 13px; transition: transform .15s ease; }
.cdrawer[open] .cdrawer-caret { transform: rotate(180deg); }
.cdrawer[open] .cdrawer-head { border-bottom: 1px solid var(--rule); }

.cdrawer-body { padding: 4px 20px 20px; }
.cdrawer-watch {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 16px 0 4px;
}

/* Inside a drawer the section spacing is tighter than the full-screen viewer,
   which has a whole page to breathe in. */
.cdetail .qa { margin-top: 22px; }
.cdetail .qa p { font-size: 14.5px; }
.cdetail .evidence { grid-template-columns: 210px 1fr; gap: 18px; padding: 16px; }
@media (max-width: 700px) { .cdetail .evidence { grid-template-columns: 1fr; } }
.cdetail .doit { padding: 18px 20px; }
.cdetail .doit p { font-size: 15px; }
.cdetail .pattern { padding: 18px 20px; }

.cdetail-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  flex-wrap: wrap; margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--rule);
}

/* --- evidence box, tidied ------------------------------------------------- */

/*
  Three problems, one cause: the play control lived at the foot of the prose
  column, so its position depended on how long the transcript happened to be.
  Boxes ended at different heights, the button floated away from the still it
  played, and the labels ran together for want of air.

  The control now sits under its own still, the columns are top-aligned, and
  every block gets room to breathe.
*/
.evidence {
  padding: 20px;
  gap: 22px;
  align-items: start;
  background: var(--raised);
  border-color: var(--rule-firm);
}
.evidence + .evidence { margin-top: 14px; }

.evmedia { display: flex; flex-direction: column; gap: 10px; }
.evmedia .evframe { margin: 0; }
.evtext { min-width: 0; }

/* A full-width play control under the still reads as belonging to it. */
.tsplay--block {
  width: 100%;
  justify-content: center;
  padding: 10px 14px;
  /* Self-sufficient, so a redefinition of the base cannot strip it again. */
  background: var(--band); color: var(--band-ink);
  border: 1px solid var(--rule-firm); border-radius: 8px;
}
/* THE HOVER MUST CARRY THE INK TOO. `.tsplay:hover { color: var(--ink) }` is a
   two-class selector and `.tsplay--block`'s `color` is one, so on hover the base
   won and put `--ink` on this pinned `#35405A`: 1.69:1 in light. Self-sufficient
   means self-sufficient in every state, not just at rest. 9.05:1 in both now. */
.tsplay--block:hover { background: #35405A; color: var(--band-ink); }
/* THE TRIANGLE ON THE FILLED VARIANT, WHICH I BROKE AND THE NEW GUARD CAUGHT.
   Making `--block` self-sufficient put `var(--gold)` back on `var(--band)`:
   3.25:1 in dark and 2.04:1 in light. A stable surface takes stable ink. */
.tsplay--block .tri { color: var(--band-ink); }

/* Labels need space above them, not below the thing they follow. */
.evlabel { margin: 0 0 8px; letter-spacing: .11em; }
.evtext > .evlabel + p { margin-bottom: 0; }
.evtext > p + .evlabel { margin-top: 20px; }
.evtext > ul + .evlabel { margin-top: 20px; }
.evscene { margin: 0; line-height: 1.62; }
.transcript { margin: 0; padding: 14px 18px; line-height: 1.6; }
.support { margin: 0; }
.support li { padding: 5px 0 5px 20px; line-height: 1.5; }

/* Inside a drawer the still is narrower, so keep the button legible. */
.cdetail .evidence { grid-template-columns: 228px 1fr; padding: 20px; gap: 22px; }
@media (max-width: 760px) {
  .evidence, .cdetail .evidence { grid-template-columns: 1fr; }
  .evmedia { max-width: 320px; }
}

/* --- page head, breadcrumbs ----------------------------------------------- */

.crumbs { font-size: 12.5px; color: var(--ink-mid); margin: 0 0 18px; display: flex; gap: 8px; flex-wrap: wrap; }
.crumbs a { color: var(--ink-mid); text-decoration: none; }
.crumbs a:hover { color: var(--ink); }
.crumbs span[aria-current] { color: var(--ink); font-weight: 600; }

.pagehead { display: flex; align-items: flex-start; justify-content: space-between; gap: 28px; flex-wrap: wrap; }
.pagehead-actions { display: flex; gap: 8px; flex-wrap: wrap; padding-top: 28px; }

/* --- header search -------------------------------------------------------- */

.search { position: relative; margin-left: auto; width: min(340px, 34vw); }
#gsearch {
  width: 100%; font: inherit; font-size: 13.5px; padding: 8px 13px;
  background: var(--raised); border: 1px solid var(--rule-firm); border-radius: 8px; color: var(--ink);
}
#gsearch::placeholder { color: var(--ink-mid); }
#gsearch:focus { outline: 2px solid var(--gold); outline-offset: -1px; }
.search-results {
  position: absolute; top: calc(100% + 8px); right: 0; width: min(460px, 80vw);
  background: var(--paper); border: 1px solid var(--rule-firm); border-radius: 10px;
  box-shadow: 0 20px 44px -18px rgba(20,22,30,.22); z-index: 90; overflow: hidden; max-height: 70vh; overflow-y: auto;
}
.search-results[hidden] { display: none; }
.sgroup { padding: 8px 0; }
.sgroup + .sgroup { border-top: 1px solid var(--rule); }
.sgroup-label { font-size: 10.5px; font-weight: 700; letter-spacing: .11em; text-transform: uppercase;
  color: var(--ink-mid); padding: 4px 16px 8px; margin: 0; }
.sresult { display: flex; gap: 12px; align-items: center; padding: 9px 16px; text-decoration: none; color: inherit; }
.sresult:hover, .sresult:focus { background: var(--raised); outline: none; }
.sresult img { width: 62px; height: 35px; object-fit: cover; border-radius: 4px; flex-shrink: 0; }
.sresult .stitle { font-size: 13.5px; font-weight: 640; display: block; }
.sresult .ssub { font-size: 11.5px; color: var(--ink-mid); }
.sempty { padding: 16px; font-size: 13px; color: var(--ink-mid); }
.hide-narrow { }
@media (max-width: 1180px) { .hide-narrow { display: none; } .search { width: 240px; } }

/* --- filter bars ---------------------------------------------------------- */

.filterbar { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin: 26px 0 20px; }
.chipfilters { display: flex; gap: 7px; flex-wrap: wrap; }
/* The by-type summary can run to a dozen chips where the severity row runs to
   four, so it gets its own breathing room and a rule separating it from the
   table it filters. Without the separator the two chip rows read as one control
   with two lines, and the reader cannot tell that they combine. */
.chipfilters--wrap { row-gap: 3px; }
/* Filters are TABS, not free-floating pills.
   A pill row says "these are buttons you may press"; a tab strip says "you are
   looking at one of these right now, and the thing below belongs to it". The
   second is true here -- the filter is a view of the table underneath, and the
   selected tab is connected to it rather than hovering above it. */
.chipfilters {
  gap: 3px; align-items: flex-end; width: 100%;
  background: transparent; border: 0; border-bottom: 1px solid var(--rule-firm);
  border-radius: 0; padding: 0 2px; margin: 0 0 18px;
}
/* The strip is the top edge of the panel below it, so it has to be the same
   width as that panel. Shrink-to-content left a tab bar floating short of the
   table it belongs to, which reads as two unrelated objects. */
.filterbar { display: block; }
.fchip {
  display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600;
  padding: 10px 16px; border-radius: 0; border: 0;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  background: transparent; color: var(--ink-mid); text-decoration: none;
}
.fchip:hover { color: var(--ink); border-bottom-color: var(--rule-firm); }
/* The selected tab takes the surface colour of the panel below and drops the
   line between them, so the two read as one object. */
/* The selected tab is marked by an underline in the accent, the way a tab bar
   marks a tab. It reads as selected without changing shape, so the strip stays
   one clean line instead of a row of boxes. */
.fchip.is-on { color: var(--ink); font-weight: 700; border-bottom-color: var(--gold); }
/* The count is a badge on the tab, the way a tab bar carries a number. */
.fchip b {
  font-variant-numeric: tabular-nums; font-weight: 700; font-size: 11.5px;
  min-width: 20px; padding: 1px 7px; border-radius: 999px; text-align: center;
  background: var(--raised); color: var(--ink-mid);
}
.fchip.is-on b { background: var(--gold); color: #ffffff; }

/* A panel directly under a tab strip squares its top corners -- the strip has
   already rounded them, and rounding both draws a seam between two things that
   are meant to be one. */
/* Whatever panel follows the strip is the tab's own surface -- table, legend or
   empty state -- so it squares its top and loses the doubled border. */
   it belongs above the strip rather than wedged between the tabs and their
   table -- which is what detached them. */


.statekey { margin-bottom: 16px; }
.filterright { display: flex; gap: 8px; align-items: center; margin-left: auto; flex-wrap: wrap; }
/* A select riding on the tab strip. It sits on the band rather than in the
   panel, because it filters the panel rather than belonging to it. */
.filterright { padding-bottom: 7px; }
.fsel-label { font-size: 11px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-mid); }
.fselect, .finput {
  font: inherit; font-size: 13px; padding: 7px 11px; border-radius: 7px;
  border: 1px solid var(--rule-firm); background: var(--raised); color: var(--ink);
}
.fselect:focus, .finput:focus { outline: 2px solid var(--gold); outline-offset: -1px; }

/* --- status tabs ---------------------------------------------------------- */

.statustabs { display: flex; gap: 4px; border-bottom: 1px solid var(--rule); margin: 28px 0 0; flex-wrap: wrap; }
.stab {
  font-size: 13.5px; font-weight: 600; color: var(--ink-mid); text-decoration: none;
  padding: 11px 15px; border-bottom: 3px solid transparent; margin-bottom: -1px;
  display: inline-flex; align-items: center; gap: 8px;
}
.stab:hover { color: var(--ink); }
.stab.is-on { color: var(--ink); border-bottom-color: var(--gold); }
.stab-count { font-size: 11px; background: var(--raised); border: 1px solid var(--rule-firm);
  border-radius: 999px; padding: 1px 8px; font-variant-numeric: tabular-nums; }
.stab.is-on .stab-count { background: var(--gold); border-color: var(--gold); color: #ffffff; }

/* --- entity cells --------------------------------------------------------- */

.entity { display: flex; gap: 12px; align-items: center; text-decoration: none; color: inherit; }
.entity:hover .cell-title { color: var(--gold); }
.avatar {
  width: 36px; height: 36px; border-radius: 50%; flex-shrink: 0;
  background: var(--band); color: var(--band-ink); display: inline-flex;
  align-items: center; justify-content: center; font-size: 12.5px; font-weight: 700; letter-spacing: .02em;
}
.thumb { border-radius: 4px; object-fit: cover; flex-shrink: 0; border: 1px solid var(--rule-firm); }

.countlink {
  font: inherit; font-size: inherit; color: var(--gold); background: none; border: 0; padding: 0;
  cursor: pointer; text-decoration: none; border-bottom: 1px solid transparent; font-weight: 640;
}
.countlink:hover { border-bottom-color: var(--gold); }

/* A LINK THAT LEAVES THE PRODUCT IS A REFERENCE, NOT AN ACTION.
   `countlink` is one treatment over three jobs -- 27 uses across 22 anchors, 4
   buttons and a `<summary>` -- so navigation, departure and in-page action all
   read the same. Under gold that was tolerable: everything read "notable link".
   Under an alarm accent it overstates the reference AND understates the
   control, which is rule 2 failing in both directions at once.

   On the video page the pair sat 22px apart, identical in colour, size and
   weight: an anchor to youtube.com beside a button that opens the dialog
   containing `Record this decision`. The only thing separating them was a
   trailing glyph.

   A MODIFIER, NOT A NEW COMPONENT. The accent is what the product presses; a
   departure takes the ink ramp instead and keeps the underline on hover, so it
   still reads as a link. The `↗` stays and is the honest part -- it says where
   the click goes. */
.countlink--away { color: var(--ink-mid); font-weight: 600; }
.countlink--away:hover { color: var(--ink); border-bottom-color: var(--ink-mid); }

/* --- operational status --------------------------------------------------- */

.opstatus {
  display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 600;
  padding: 5px 11px; border-radius: 7px; border: 1px solid var(--rule-firm);
  background: var(--raised); color: var(--ink-mid);
}
.opstatus::before { content: ""; width: 7px; height: 7px; border-radius: 2px; background: currentColor; }
.op--open { color: var(--material); background: var(--material-wash); border-color: var(--material-line); }
.op--review { color: var(--attention); background: var(--attention-wash); border-color: var(--attention-line); }
.op--clean { color: var(--settled); background: var(--settled-wash); border-color: var(--settled-line); }
.op--incomplete { color: var(--coverage); background: var(--coverage-wash); border-color: var(--coverage-line); }
.op--unscanned { color: var(--ink-mid); }

/* --- honest trend placeholder --------------------------------------------- */

/* A placeholder is not a movement, so it does not get an arrow or a colour. */
.notrend { color: var(--ink-mid); font-style: italic; line-height: 1.4; }
.trend--none { align-items: flex-start; }

/* --- relationship card ---------------------------------------------------- */

.relcard {
  background: var(--paper); border: 1px solid var(--rule); border-top: 4px solid var(--gold);
  border-radius: 12px; padding: 22px 26px; box-shadow: var(--shadow);
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
}
.relpart { display: flex; align-items: baseline; gap: 9px; text-decoration: none; color: inherit;
  padding: 6px 10px; border-radius: 8px; }
.relpart:hover { background: var(--raised); }
.relnum { font-size: 34px; font-weight: 800; color: var(--ink); letter-spacing: -.03em;
  font-variant-numeric: tabular-nums; }
.rellabel { font-size: 14px; font-weight: 640; color: var(--ink-mid); }
.reldot { color: var(--ink-mid); font-size: 22px; }
.relnote { width: 100%; margin: 6px 0 0; font-size: 13px; color: var(--ink-mid); }

.chanhead { display: flex; gap: 22px; align-items: flex-start; }
.avatar--lg { width: 68px; height: 68px; font-size: 22px; margin-top: 6px; }
@media (max-width: 700px) { .chanhead { flex-direction: column; gap: 14px; } }

/* Wide tables scroll inside their own frame rather than being clipped by it.
   The channels table carries ten columns and was losing the last two. */
/* NO horizontal scrolling. A table that scrolls sideways hides its own right
   edge, and the right edge is where the action lives -- so the control a reader
   needs looks absent rather than off-screen, which is exactly how "Open ›" and
   "Details ›" came to be reported as cut off. The table fits the page, and the
   cells that cannot fit truncate visibly instead. */
.table-wrap { overflow: hidden; }
.table-wrap table.grid { width: 100%; }

/* Inside an entity cell the title and its sub-line are stacked, not inline.
   A long video title wrapped and then ran straight into its own ID. */
.entity .cell-title, .entity .cell-sub { display: block; }
.entity > span { min-width: 0; }
.entity .cell-title { line-height: 1.35; }

/* Give the video column room; it carries the longest strings on the page. */
table.grid td:first-child, table.grid th:first-child { min-width: 300px; }
.table-wrap table.grid { min-width: 1000px; }

/* --- account / network selector and notifications ------------------------- */

.acct { position: relative; }
.acctbtn, .bell {
  display: inline-flex; align-items: center; gap: 9px; font: inherit; font-size: 13px;
  background: none; border: 1px solid transparent; border-radius: 9px; padding: 5px 9px;
  color: var(--ink-mid); cursor: pointer;
}
.acctbtn:hover, .bell:hover { border-color: var(--rule-firm); color: var(--ink); }
.acctinit {
  width: 28px; height: 28px; border-radius: 7px; background: var(--band); color: var(--band-ink);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 700; flex-shrink: 0;
}
.acctname { display: flex; flex-direction: column; line-height: 1.2; text-align: left; font-weight: 640; color: var(--ink); }
.acctsub { font-size: 11px; color: var(--ink-mid); font-weight: 400; }
.caret { font-size: 10px; color: var(--ink-mid); }

.bell { position: relative; font-size: 15px; }
.belldot {
  position: absolute; top: 0; right: 0; background: var(--material); color: #fff;
  font-size: 10px; font-weight: 700; min-width: 16px; height: 16px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center; padding: 0 4px;
}

.acctmenu {
  position: absolute; top: calc(100% + 8px); right: 0; width: 280px; z-index: 95;
  background: var(--paper); border: 1px solid var(--rule-firm); border-radius: 10px;
  box-shadow: 0 20px 44px -18px rgba(20,22,30,.22); padding: 8px 0; max-height: 70vh; overflow-y: auto;
}
.acctmenu--wide { width: 360px; }
.acctmenu[hidden] { display: none; }
.acctmenu-label { font-size: 10.5px; font-weight: 700; letter-spacing: .11em; text-transform: uppercase;
  color: var(--ink-mid); margin: 6px 0 6px; padding: 0 14px; }
.acctmenu-foot { font-size: 11px; color: var(--ink-mid); padding: 10px 14px 4px; margin: 6px 0 0;
  border-top: 1px solid var(--rule); line-height: 1.45; }
.acctitem, .acctuser {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  font: inherit; font-size: 13.5px; background: none; border: 0; color: var(--ink);
  padding: 8px 14px; cursor: pointer;
}
.acctuser { cursor: default; }
.acctuser span:last-child { display: flex; flex-direction: column; line-height: 1.25; }
.acctitem:hover:not(:disabled) { background: var(--raised); }
.acctitem:disabled { opacity: .45; cursor: not-allowed; }
.acctitem.is-on { font-weight: 650; }
.acctnote { margin-left: auto; font-size: 10.5px; color: var(--ink-mid); text-transform: uppercase; letter-spacing: .06em; }

.notif {
  display: block; width: 100%; text-align: left; font: inherit; background: none;
  border: 0; border-left: 3px solid transparent; padding: 9px 14px; cursor: pointer; color: inherit;
}
.notif:hover { background: var(--raised); }
.notif--material { border-left-color: var(--material); }
.notif--overdue { border-left-color: var(--material); }
.notif--waiting { border-left-color: var(--coverage); }
.notif--incomplete { border-left-color: var(--coverage); }
.notif-title { display: block; font-size: 12.5px; font-weight: 700; color: var(--ink); }
.notif-text { display: block; font-size: 12px; color: var(--ink-mid); line-height: 1.4; margin-top: 2px; }

/* --- dashboard density ---------------------------------------------------- */

.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.pagehead--tight { align-items: center; margin-bottom: 4px; }
.pagehead--tight h1.display { margin-bottom: 0; }

.scopebar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding-top: 8px; }
.segmented { display: inline-flex; border: 1px solid var(--rule-firm); border-radius: 9px; overflow: hidden; }
.seg {
  font-size: 12.5px; font-weight: 600; color: var(--ink-mid); text-decoration: none;
  padding: 7px 13px; border-right: 1px solid var(--rule-firm);
}
.seg:last-child { border-right: 0; }
.seg:hover { background: var(--raised); color: var(--ink); }
.seg.is-on { background: var(--gold); color: #ffffff; }

.scopenote { font-size: 12.5px; color: var(--ink-mid); margin: 12px 0 0; }

.row-kpi { margin-top: 22px; }
.trendnote {
  font-size: 12.5px; color: var(--ink-mid); margin: 12px 0 0; line-height: 1.5;
  display: flex; gap: 18px; flex-wrap: wrap;
}
.trendnote span { color: var(--coverage); }

.row-split { display: grid; grid-template-columns: 1fr 340px; gap: 26px; align-items: start; margin-top: 40px; }
@media (max-width: 1100px) { .row-split { grid-template-columns: 1fr; } }
.sidecol { display: flex; flex-direction: column; gap: 14px; }

.sidecard { background: var(--paper); border: 1px solid var(--rule); border-radius: 11px; padding: 16px 18px; }
.sidecard--gold { border-top: 3px solid var(--gold); }
.sidecard--coverage { border-top: 3px solid var(--coverage); }
.sidecard-label { font-size: 10.5px; font-weight: 700; letter-spacing: .11em; text-transform: uppercase;
  color: var(--ink-mid); margin: 0 0 8px; }
.sidecard h3 { font-size: 15px; font-weight: 700; color: var(--ink); margin: 0 0 7px; line-height: 1.35; }
.sidecard p { font-size: 13px; color: var(--ink-mid); margin: 0; line-height: 1.55; }
.sidecard-links { margin-top: 12px !important; font-size: 12.5px; display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.sidelist { list-style: none; margin: 12px 0 0; padding: 0; font-size: 12.5px; }
.sidelist li { display: flex; justify-content: space-between; gap: 10px; padding: 5px 0;
  border-bottom: 1px dotted var(--rule); }
.sidelist li:last-child { border-bottom: 0; }
.sidelist a { color: var(--ink); text-decoration: none; font-weight: 600; }
.sidelist a:hover { color: var(--gold); }
.sidelist span { color: var(--ink-mid); white-space: nowrap; }

/* Compact tables: shorter rows, and a title that stays on one line so the
   column does not turn into a narrow vertical block of words. */
table.grid--compact td { padding: 9px 9px; }
table.grid--compact th { padding: 9px 9px; }
.one-line { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 52ch; }
table.grid--compact .cell-sub { margin-top: 1px; }

/* The 1000px table minimum was set for the wide standalone lists. Inside the
   dashboard's split row the table has a narrower column, and forcing that
   minimum pushed the whole PAGE into horizontal scroll. Compact tables size to
   their container and scroll inside their own frame if they must. */
.table-wrap table.grid--compact { min-width: 0; }
.row-split .table-wrap table.grid { min-width: 620px; }
body { overflow-x: hidden; }
.wrap { max-width: var(--measure); }

/* The dashboard's compact table has no room for a wide first column. */
table.grid--compact td:first-child, table.grid--compact th:first-child { min-width: 0; }
.row-split .one-line { max-width: 38ch; }

/* A grid item will not shrink below its content unless told to. The dashboard's
   1fr column held a table, so the whole page inherited its width and scrolled
   sideways. This is the classic grid blowout and the fix is one line. */
.row-split > * { min-width: 0; }
.video-grid > * { min-width: 0; }

/* --- selection, bulk actions and row utilities --------------------------- */

/* Bulk actions appear only once something is selected. A permanently visible
   toolbar of disabled buttons teaches the reader to ignore that strip. */
.bulkbar {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin: 16px 0 -6px; padding: 11px 16px;
  background: var(--raised); border: 1px solid var(--gold-soft); border-radius: 9px;
}
.bulkcount { font-size: 13px; color: var(--ink); }
.bulkcount b { font-variant-numeric: tabular-nums; color: var(--gold); }
.bulkactions { display: flex; gap: 8px; flex-wrap: wrap; }
.bulknote { font-size: 12px; color: var(--ink-mid); margin-left: auto; }

.selcol { width: 34px; padding-right: 0 !important; }
.selcol input { width: 15px; height: 15px; accent-color: var(--gold); cursor: pointer; }
tr.is-selected > td { background: var(--raised); }

.tagchip {
  display: inline-block; font-size: 11px; line-height: 1; padding: 4px 7px; margin: 0 4px 0 0;
  border: 1px solid var(--rule-firm); border-radius: 4px; color: var(--ink-mid);
  white-space: nowrap;
}

/* Coverage reads as a STATE first — Full / Partial / Limited — with the
   material listed beneath it. "Picture + transcript" alone made the reader
   work out for themselves whether that was everything. */
.covstate {
  display: block; font-size: 12.5px; font-weight: 700; letter-spacing: .02em;
}
.cov--full { color: var(--settled); }
.cov--partial { color: var(--coverage); }
.cov--limited { color: var(--attention); }
.cov--none { color: var(--ink-mid); }

/* The action column is a TABLE CELL, and `display: flex` on a <td> takes it out
   of the table's column model altogether. Every `.grid` is `table-layout: fixed`
   and the header reserved 136px for this column -- but the flex cell measured
   18px, so the 84px control was laid out starting 91px PAST the table's right
   edge, where `.table-wrap { overflow: hidden }` clipped it. On screen that is
   indistinguishable from a button that is merely narrow, and it cannot be
   clicked at all. The earlier `width: 1%` shrink-to-fit fix below never had a
   chance: it addresses `table-layout: auto`, and it was addressing the wrong
   defect anyway. Flex the CONTENTS of the cell if anything, never the cell. */
.rowacts { white-space: nowrap; }
td.rowacts { vertical-align: middle; }
.rowacts .btn--sm { padding: 4px 9px; }
.rowacts .btn + .btn { margin-left: 6px; }

.concerncol { max-width: 34ch; }

/* THE OWNER COLUMN NEEDS THE WHOLE ADDRESS. Every `.grid` is
   `table-layout: fixed`, so ten columns divided evenly gave this one 123px
   against the 168px an email needs -- and 12 of 15 rows sliced at the ellipsis,
   on a column added the same day. Rule 8: never truncate meaning, and a person
   is identified by the whole of their address, not the half that fits.

   Taken from the numeric columns, which were holding values like "8" in the
   same 123px.

   `ch` WAS CHOSEN HERE "so it tracks the font it is measured in", AND THAT
   REASON DOES NOT HOLD. Instruction 113 measured it: this selector matches the
   `<th>` at 11px AND the `<td>` at 13px, so ONE declaration states TWO widths
   for ONE column --

       24ch on the header (11px)             166.6px
       24ch on the cells  (13px)             196.8px
       what the column actually renders      178.016px    <- neither of them

   -- and the auto table algorithm arbitrates to a width no declaration asked
   for. PROVED BY SUBSTITUTION rather than by reading the spec: a single
   unambiguous length is honoured exactly (166.6px renders 166.594, 400px
   renders 400.000, `auto` renders 197.422), so the declaration does control
   this column and `ch` was handing it two answers.

   11.125rem is 178px at the 16px root -- what the arbitration was already
   producing. DELTA -0.016px: the unit changes, the column does not.

   THE WIDTH ITSELF IS NOT TOUCHED. 113 reserves column width to the owner, and
   178px is an arbitration artefact rather than a chosen number, so the three
   figures above are what a ruling would need. */
.ownercol { width: 11.125rem; }
table.grid--compact .ownercol { width: 11.125rem; }

/* THE COVERAGE CHIP WAS WRAPPING, AND THAT MADE IT THE HEAVIEST THING IN THE
   ROW. Measured on four rows, identical each time: "10 areas not assessed" at
   96x46px over TWO lines against the severity chip's "◆ Medium" at 81x27px over
   one -- same 12px font, both washes unchanged, neither the accent. 2.04x the
   area of the severity chip beside it, purely because the column was too narrow.

   That is a layout consequence, not a colour one, and the chip was behaving
   correctly: rule 8 says a chip in a narrow cell wraps rather than being sliced.
   The cell is what was wrong.

   12.05rem WAS `26ch`, and the same split applies as on `.ownercol` above --
   header at 11px and cells at 13px, one declaration, two widths:

       26ch on the header (11px)             180.4px
       26ch on the cells  (13px)             213.2px
       what the column actually renders      192.844px    <- neither of them

   12.05rem is 192.8px at the 16px root. DELTA -0.044px. Substituted and
   re-measured, as above: 180.4px renders 180.391 and 213.2px renders 213.188,
   so the column follows a single length exactly. The width is the owner's. */
.covcol { width: 12.05rem; }
table.grid--compact .covcol { width: 12.05rem; }
table.grid--compact .concerncol .one-line { max-width: 34ch; }

/* A button that has to read as prose, because it sits inside a sentence. */
.linklike {
  background: none; border: 0; padding: 0; font: inherit; color: var(--gold);
  cursor: pointer; text-decoration: underline; text-underline-offset: 2px;
}
.linklike:hover { color: var(--ink); }

/* The guidance a finding rests on is the strongest part of the argument, so it
   is given the page's only gold bar rather than a grey footnote block. */
.policy--prominent {
  background: var(--gold-wash); border: 1px solid var(--gold-soft);
  border-left: 4px solid var(--gold); border-radius: 8px; padding: 16px 18px;
}
.policy--prominent .psource {
  font-size: 10.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--gold); margin: 0 0 8px;
}
.policy--prominent .pname { font-size: 15px; font-weight: 700; color: var(--ink); margin: 0 0 8px; line-height: 1.4; }
.policy--prominent .pbody { font-size: 14px; color: var(--ink-mid); margin: 0; line-height: 1.6; }
.policy--prominent .pmissing { font-size: 12px; color: var(--ink-mid); margin: 12px 0 0; font-style: italic; }
/* The one block of text on the page that is not Centurion's. It is the
   retrieved passage, copied from the corpus, so it is set apart from the
   prose around it rather than blended into it. */
.policy--prominent .pquote {
  font-size: 14px; color: var(--ink); line-height: 1.65; margin: 12px 0 0;
  padding: 10px 0 10px 14px; border-left: 2px solid var(--gold-soft);
}

/* A viewer that ends with nowhere to go sends the reader to the browser Back
   button, which loses the queue they were paging through. */
.viewer-exits { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 26px;
  padding-top: 20px; border-top: 1px solid var(--rule); }

/* --- timeline marker labels ---------------------------------------------- */

/* Each marker states its own time and review area. Where a marker is too
   narrow for its words, the words move to a tooltip on hover and focus rather
   than disappearing — the label is the point of the marker, not decoration. */
.marker { overflow: hidden; display: flex; align-items: center; }
.mlabel {
  font-size: 11px; font-weight: 700; letter-spacing: .01em; white-space: nowrap;
  padding: 0 7px; color: var(--paper); pointer-events: none;
}
/* THE LABEL SITS ON A PINNED FILL, SO IT IS PINNED TOO -- and the direction
   reversed when the fills did. `--paper` used to be right in both themes because
   the fill flipped WITH it: a dark red marker in light took white text, a light
   salmon one in dark took dark text, and one token covered both. Pinning the
   fills to their dark values makes every marker light in BOTH themes, so the
   label is dark in both. `#12182a` is `--navy`'s light value held still, and it
   is the far side of all four fills: 7.01 on material, 9.27 on attention, 15.46
   on plain, 7.42 on the quiet stripe.

   All four are listed rather than pinned on the base `.mlabel`, so a fifth tone
   arriving is VISIBLY absent here instead of silently inheriting ink that may be
   wrong for it. That is decision 1 applied to the label. */
.marker--material .mlabel,
.marker--attention .mlabel,
.marker--plain .mlabel,
.marker--quiet .mlabel { color: #12182a; }
.mtip {
  position: absolute; z-index: 5; transform: translate(-50%, -100%);
  background: var(--band); color: var(--band-ink); font-size: 12px; font-weight: 600;
  padding: 6px 10px; border-radius: 6px; white-space: nowrap; pointer-events: none;
  box-shadow: var(--shadow);
}

/* The concern column takes whatever the fixed columns leave, rather than a
   fixed share: at 1134px the row was 63px too wide and Detected fell off the
   right edge while the title was still truncating at 40 characters. */
/* A compact row is a smaller row, not just a shorter one. At 14px the eight
   columns needed 1158px inside a 1134px frame, so Detected fell off the edge
   and the concern title truncated at forty characters while it did. */
table.grid--compact { width: 100%; font-size: 13px; }
table.grid--compact .concerncol { width: 40%; }
table.grid--compact .concerncol .one-line { max-width: 100%; }
table.grid--compact .chip { padding: 3px 8px; }
table.grid--compact .statuspill { padding: 3px 9px; }

/* A KPI card that is a link still has to look like a card. */
a.kpi { text-decoration: none; }
/* A chip that leads somewhere is still a chip. The severity chips on the
   dashboard each open their own filtered queue, and an underline would make
   them read as prose links rather than as the counts they are. */
a.chip { text-decoration: none; }
a.kpi span { text-decoration: none; }

/* The timeline frame, not the clipping track, hosts the hover label. */
.timeline { position: relative; }

/* The marker key: every finding on the timeline, in words. */
.mkey { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.mkey-item {
  display: flex; align-items: baseline; gap: 9px; width: 100%; text-align: left;
  background: none; border: 1px solid var(--rule); border-left-width: 3px; border-radius: 6px;
  padding: 7px 10px; cursor: pointer; font: inherit; color: var(--ink-mid);
}
.mkey-item:hover { background: var(--raised); color: var(--ink); }
.mkey-item:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
/* THE KEY TAKES THE SAME FOUR TONES, AND THE OPPOSITE TREATMENT. The defect was
   doubled: the marker on the track was invisible and its entry in this key was
   untyped, so a reader got no signal from either place. One defect wearing two
   hats.

   FLIPPING TOKENS ARE CORRECT HERE, and that is not an inconsistency with the
   markers above. `.mkey-item` is `background: none` inside `.timeline`, whose
   ground is `--paper` -- so this ground MOVES with the theme and its ink must
   move with it. The rule is that a surface and its ink agree, not that ink is
   always pinned. Pinned ground pinned ink on the track; moving ground moving ink
   here. On `--paper`, light/dark: material 7.29/7.44, attention 7.35/9.84,
   plain 5.37/4.36, quiet 8.46/7.88 -- all clear of the 3:1 a 3px border needs.

   `plain` is `--ink-faint` because a low or unrecorded severity is neutral, not
   a verdict colour; `quiet` is `--coverage`, the tone that says "a limit on what
   we can say". The glyph carries the distinction too -- `SEVERITY_MARKS` gives
   plain an open circle and quiet a slashed one -- which is the product's own rule
   that colour is never the only carrier. */
.mkey--material { border-left-color: var(--material); }
.mkey--attention { border-left-color: var(--attention); }
.mkey--plain { border-left-color: var(--ink-faint); }
.mkey--quiet { border-left-color: var(--coverage); }
.mkey--material .mkey-dot { color: var(--material); }
.mkey--attention .mkey-dot { color: var(--attention); }
.mkey--plain .mkey-dot { color: var(--ink-mid); }
.mkey--quiet .mkey-dot { color: var(--coverage); }
.mkey-dot { font-size: 10px; }
.mkey-time { font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink); }
.mkey-fam { font-size: 12.5px; }

/* In the portfolio table the status label may wrap: holding "Incomplete
   assessment" on one line cost 90px that the manager and tag columns needed. */
table.grid--compact .opstatus { white-space: normal; }

/* Severity and status stacked in one cell: two narrow columns cost more width
   than the two labels do side by side in a 768px frame. */
/* Always two lines, never sometimes-two: rows whose chips happened to be short
   put severity and status side by side and the column read as ragged. */
.sevstack { white-space: nowrap; }
.sevstack .chip, .sevstack .statuspill { display: flex; width: fit-content; }
.sevstack .statuspill { margin-top: 4px; }

/* In a compact row the sub-line is a single line too, so every row is the same
   height and the eye can run down the column.

   THIS RULE ASSUMES THE ELEMENT IS A TRUNCATABLE SUB-LINE, AND IT IS A TRAP ON
   ANYTHING ELSE. It was wrong three times on one page during instruction 28,
   in three different ways, and every test passed each time because none of
   them look at the page:

     display: block   removed a <summary>'s disclosure triangle -- the marker
                      only draws at display: list-item -- so a drawer rendered
                      as plain text with nothing to say it opened
     (the same)       made that summary read as a label rather than a control
     nowrap+ellipsis  cut "from the account default" to "from the account ...",
                      in the column that exists solely to name which level
                      decided a setting

   It is correct where it was written. Before putting `.cell-sub` on something
   that is not a sub-line -- a <summary>, a control, a sentence whose ending
   carries the meaning -- use `.cell-sub--wrap` below, which keeps the type and
   drops the truncation. */
table.grid--compact .cell-sub {
  display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}

.cdetail-exits { display: inline-flex; gap: 8px; flex-wrap: wrap; }

/* ==========================================================================
   The five operational pages
   ========================================================================== */

/* --- gaps: what the record does not hold --------------------------------- */

/* Every operational page ends with one of these. It is a deliverable, not an
   apology, so it gets structure rather than a grey footnote. */
.gapblock { margin-top: 46px; padding-top: 30px; border-top: 2px solid var(--rule-firm); }
.gaps { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 14px; }
.gap {
  background: var(--paper); border: 1px solid var(--rule); border-left: 3px solid var(--coverage);
  border-radius: 9px; padding: 16px 18px;
}
.gap h3 { font-size: 14.5px; font-weight: 700; color: var(--ink); margin: 0 0 7px; line-height: 1.35; }
.gap p { font-size: 13px; color: var(--ink-mid); margin: 0 0 8px; line-height: 1.55; }
.gap-ev, .gap-fix { font-size: 12px !important; color: var(--ink-mid) !important; }
.gap-fix code { font-size: 11.5px; word-break: break-word; }
.gap p:last-child { margin-bottom: 0; }

/* A value the record does not carry is never blank and never zero. */
.missing { color: var(--coverage); font-style: italic; }

/* --- intake --------------------------------------------------------------- */

.intake { display: grid; grid-template-columns: 1fr 380px; gap: 20px; align-items: start; margin-top: 26px; }
.intake > * { min-width: 0; }
@media (max-width: 1000px) { .intake { grid-template-columns: 1fr; } }

.card--form { background: var(--paper); border: 1px solid var(--rule); border-radius: 12px; padding: 22px 24px; }
.card--refusal { border-left: 3px solid var(--attention); }
.card--empty { border-style: dashed; }

.fieldlabel {
  display: block; font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-mid); margin: 0 0 7px;
}

/* THE QUALIFIER OPTS OUT OF THE LABEL'S CASE AND WEIGHT.
 *
 * `<label class="fieldlabel">Full name <span>optional</span></label>` rendered
 * "FULL NAME OPTIONAL": a bare span inherits everything, and measured on the
 * deployed page the qualifier matched the label on FIVE properties -- 11px,
 * weight 700, uppercase, rgb(122,122,122) and 1.1px letter-spacing. Instruction
 * 98 recorded colour and size; case and weight are what made it invisible, and
 * letter-spacing is a fifth nobody had measured.
 *
 * CASE, WEIGHT AND TRACKING. NOT colour and NOT size: a regular-weight lowercase
 * word beside a bold uppercase label is subordinate without a second colour, and
 * dimmer than `--ink-faint` on this palette is approaching the ground.
 *
 * THE TRACKING GOES BECAUSE THE REASON FOR IT GOES. `.1em` is on the label
 * because the label is UPPERCASE, and tracking capitals is a legibility
 * convention -- caps set at normal tracking read tight. Lowercase does not need
 * it and looks loose carrying it. So this is the same inheritance one property
 * further along: a value applied for a reason that stops holding the moment the
 * transform is off.
 *
 * NOT `.small`: 12.5px against 11px makes the qualifier LARGER than what it
 * qualifies. NOT `.acctnote`: it sets `text-transform: uppercase` ITSELF, so of
 * the options considered it was the one guaranteed to preserve the defect.
 *
 * The name was grepped before it was defined. `.cell-sub--wrap` already existed
 * with a comment recommending it by name when a second rule was added for it,
 * and that rule then outranked the original across six templates. */
.fieldlabel__note { text-transform: none; font-weight: 400; letter-spacing: normal; }
.fieldhelp { font-size: 12.5px; color: var(--ink-mid); margin: 8px 0 0; line-height: 1.55; }
.field { margin-bottom: 20px; }
.inline-field { display: flex; align-items: center; gap: 9px; }
.inline-field span { font-size: 13px; color: var(--ink-mid); }

.reffield { display: flex; gap: 10px; }
.reffield .finput { flex: 1; }
.finput--lg { font-size: 15px; padding: 12px 14px; }
.finput--sm { max-width: 110px; }

.twoup { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin-bottom: 4px; }
@media (max-width: 700px) { .twoup { grid-template-columns: 1fr; } }

.depths { border: 0; padding: 0; margin: 22px 0 0; display: flex; flex-direction: column; gap: 9px; }
.depths--row { margin-top: 18px; }
.depth {
  display: flex; gap: 11px; align-items: flex-start; padding: 12px 14px; cursor: pointer;
  border: 1px solid var(--rule); border-radius: 9px; background: var(--ground);
}
.depth:hover { border-color: var(--rule-firm); }
.depth.is-on { border-color: var(--gold); background: var(--gold-wash); }
.depth b { display: block; font-size: 13.5px; color: var(--ink); }
.depth-means { display: block; font-size: 12.5px; color: var(--ink-mid); margin-top: 3px; line-height: 1.5; }

.modes { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 18px; margin-top: 14px; }
@media (max-width: 700px) { .modes { grid-template-columns: 1fr; } }
.modebox { display: flex; gap: 8px; align-items: center; font-size: 12.5px; color: var(--ink-mid); cursor: pointer; }

.scope {
  margin-top: 22px; padding: 14px 16px; border: 1px solid var(--gold-soft);
  background: var(--gold-wash); border-radius: 9px;
}
.scope p { font-size: 13px; color: var(--ink-mid); margin: 8px 0 0; }

.formfoot { display: flex; align-items: center; gap: 14px; margin-top: 24px; flex-wrap: wrap; }
.btn--lg { padding: 11px 22px; font-size: 14px; }

.orline { display: flex; align-items: center; gap: 14px; margin: 22px 0; color: var(--ink-mid); font-size: 12px; }
.orline::before, .orline::after { content: ""; flex: 1; height: 1px; background: var(--rule); }

.resolved { display: flex; gap: 22px; align-items: flex-start; background: var(--paper);
  border: 1px solid var(--rule); border-radius: 12px; padding: 22px 24px; }
.resolved-facts { flex: 1; min-width: 0; }
.resolved-facts h3 { font-size: 22px; font-weight: 700; color: var(--ink); margin: 0 0 14px; }
.resolved-facts dl { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 12px 22px; margin: 0 0 14px; }
.resolved-facts dt { font-size: 10.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-mid); margin-bottom: 3px; }
.resolved-facts dd { font-size: 14px; color: var(--ink); margin: 0; }

.row--muted td { opacity: .72; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; }

/* --- analysis ------------------------------------------------------------- */

/* Bulk submissions, one line each. The card version put three full-width boxes
   between the page heading and the table the page exists for. Supporting facts
   get a line; the work gets the space. */
.batchbar {
  display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap;
  background: var(--paper); border: 1px solid var(--rule); border-top: 3px solid var(--gold);
  border-radius: 11px; padding: 16px 20px; margin-bottom: 22px;
}
.batchbar h3 { font-size: 15px; font-weight: 700; color: var(--ink); margin: 8px 0 4px; }
.batchact { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; }

/* Run state: a word, a colour and a shape, never colour alone. */
.runstate {
  display: inline-block; font-size: 11.5px; font-weight: 700; letter-spacing: .02em;
  padding: 3px 9px; border-radius: 999px; border: 1px solid;
}
.runstate--lg { font-size: 13px; padding: 5px 13px; margin-right: 8px; }
.rs-queued { color: var(--ink-mid); border-color: var(--rule-firm); background: var(--raised); }
.rs-running { color: var(--gold); border-color: var(--gold-soft); background: var(--gold-wash); }
.rs-succeeded { color: var(--settled); border-color: var(--settled); background: var(--settled-wash); }
.rs-partial { color: var(--coverage); border-color: var(--coverage); background: var(--coverage-wash); }
.rs-failed, .rs-expired { color: var(--material); border-color: var(--material); background: var(--material-wash); }
.rs-cancelled { color: var(--ink-mid); border-color: var(--rule-firm); background: var(--raised); }

.progbar { height: 8px; background: var(--raised); border-radius: 999px; overflow: hidden; margin-bottom: 20px; }
.progbar span { display: block; height: 100%; background: var(--gold); }

.ladder { list-style: none; margin: 0; padding: 0; }
.step { display: flex; align-items: baseline; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--rule); }
.step:last-child { border-bottom: 0; }
.step-mark { width: 16px; text-align: center; font-size: 12px; }
.step-body { flex: 1; min-width: 0; }
.step-label { display: block; font-size: 13.5px; color: var(--ink-mid); }
.step-count { display: block; font-size: 12px; color: var(--gold); margin-top: 2px; font-variant-numeric: tabular-nums; }
.step-state { font-size: 11.5px; color: var(--ink-mid); white-space: nowrap; }
.step--done .step-mark { color: var(--settled); }
.step--done .step-label { color: var(--ink); }
.step--running .step-mark { color: var(--gold); }
.step--running .step-label { color: var(--ink); font-weight: 700; }
.step--waiting { opacity: .55; }

.reasons { list-style: none; margin: 0; padding: 0; }
.reasons li {
  font-size: 13px; color: var(--ink-mid); line-height: 1.55; padding: 11px 14px; margin-bottom: 8px;
  background: var(--coverage-wash); border-left: 3px solid var(--coverage); border-radius: 0 7px 7px 0;
}

.factlist { margin: 12px 0 0; }
.factlist div { display: flex; justify-content: space-between; gap: 14px; padding: 7px 0;
  border-bottom: 1px dotted var(--rule); }
.factlist div:last-child { border-bottom: 0; }
.factlist dt { font-size: 12px; color: var(--ink-mid); }
.factlist dd { font-size: 12.5px; color: var(--ink); margin: 0; text-align: right; }

.runacts { display: flex; flex-direction: column; gap: 6px; margin-top: 12px; align-items: flex-start; }
.runacts p { font-size: 12px; color: var(--ink-mid); margin: 0 0 8px; line-height: 1.5; }

.timeline-list { list-style: none; margin: 0; padding: 0; font-size: 13px; color: var(--ink-mid); }
.timeline-list li { padding: 7px 0; border-bottom: 1px dotted var(--rule); }
.timeline-list li:last-child { border-bottom: 0; }
.timeline-list b { color: var(--ink); margin-right: 8px; }

/* --- audit ---------------------------------------------------------------- */

.auditstage {
  display: block; font-size: 12.5px; font-weight: 700; color: var(--ink);
}
.as-compared { color: var(--settled); }
.as-awaiting_reference { color: var(--gold); }
.as-awaiting_production { color: var(--ink-mid); }

.vocab { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 8px; }
.vocab-row { display: flex; align-items: baseline; gap: 12px; padding: 9px 13px;
  background: var(--paper); border: 1px solid var(--rule); border-radius: 7px; }
.vocab-row code { font-size: 11px; color: var(--ink-mid); }
.vocab-row span { font-size: 13px; color: var(--ink); font-weight: 600; }

.emptystate {
  background: var(--paper); border: 1px dashed var(--rule-firm); border-radius: 12px;
  padding: 28px 30px; text-align: left;
}
.emptystate h3 { font-size: 17px; font-weight: 700; color: var(--ink); margin: 0 0 10px; }
.emptystate p { font-size: 13.5px; color: var(--ink-mid); margin: 0 0 10px; line-height: 1.6; max-width: 74ch; }

/* --- settings and people -------------------------------------------------- */

.settingsnav { display: flex; gap: 4px; margin: 26px 0 8px; border-bottom: 1px solid var(--rule); }
.settingsnav a {
  font-size: 13.5px; font-weight: 600; color: var(--ink-mid); text-decoration: none;
  padding: 10px 16px; border-bottom: 2px solid transparent;
}
.settingsnav a:hover { color: var(--ink); }
.settingsnav a.is-on { color: var(--ink); border-bottom-color: var(--gold); }

.grid-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 14px; }
.grid-cards .card p { font-size: 13px; color: var(--ink-mid); margin: 10px 0 0; line-height: 1.55; }
/* A WARN TAG SITS ON THE PINNED BAND, so its ink is pinned too. `--coverage` was
   1.51:1 on `--band` in light against a 4.5 floor -- unreadable in the theme that
   was added second. `--band-ink` would have cleared the floor at 11.20 and DELETED
   THE CLASS: `.card .tag` already gives `--band-ink`, so "Retry" and "Cannot be
   resolved" would have become visually identical to a plain tag. Fixing a ratio by
   removing the distinction is not fixing it. `--band-coverage` is 5.38:1 in both.
   The `!important` stays: it is what beats `.card .tag`'s two-class selector, and
   the value still differs from it. The `border-color` paints NOTHING -- no `.tag`
   rule sets `border-width` or `border-style`, so the computed width is 0 -- and it
   is moved with the colour rather than left behind to rot. Reported separately. */
.tag--warn { border-color: var(--band-coverage) !important; color: var(--band-coverage) !important; }

table.matrix .matcell { text-align: center; }
.matyes { color: var(--settled); font-size: 15px; font-weight: 700; }
.matno { color: var(--ink-mid); font-size: 15px; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* --- mapping table -------------------------------------------------------- */

table.maptable { font-size: 12px; }
table.maptable td { vertical-align: top; }
table.maptable .cell-title { font-size: 12.5px; }
.src { font-size: 10.5px; color: var(--ink-mid); word-break: break-word; display: block; max-width: 34ch; }
.val { display: block; white-space: normal; max-width: 40ch; }
.xf { display: block; color: var(--gold); margin-top: 4px; }
.row--missing { background: color-mix(in srgb, var(--coverage) 8%, transparent); }
.cls {
  display: inline-block; width: 22px; height: 22px; line-height: 22px; text-align: center;
  border-radius: 5px; font-size: 11.5px; font-weight: 700;
}
.cls--A { background: var(--settled-wash); color: var(--settled); }
.cls--B { background: var(--gold-wash); color: var(--gold); }
.cls--C { background: var(--coverage-wash); color: var(--coverage); }

/* The mapping table is reference material and is allowed to be wide. */
.maptable { min-width: 0; }

/* A sub-line that is a sentence rather than a label may wrap: clamping it to
   one line made it set the width of the whole column. */
table.grid--compact .cell-sub--wrap { white-space: normal; max-width: 26ch; }
.vocab-row--tall { flex-direction: column; align-items: flex-start; gap: 5px; }
.vocab-mean { font-size: 12.5px; color: var(--ink-mid); font-weight: 400 !important; line-height: 1.5; }

/* The mapping table has five known columns and content that is deliberately
   verbatim — some of it very long. Fixed layout with explicit shares is the
   only way to keep a reference table of exact strings inside its frame. */
table.maptable { table-layout: fixed; }
table.maptable th:nth-child(1), table.maptable td:nth-child(1) { width: 18%; }
table.maptable th:nth-child(2), table.maptable td:nth-child(2) { width: 21%; }
table.maptable th:nth-child(3), table.maptable td:nth-child(3) { width: 28%; }
table.maptable th:nth-child(4), table.maptable td:nth-child(4) { width: 25%; }
table.maptable th:nth-child(5), table.maptable td:nth-child(5) { width: 8%; }
table.maptable .src, table.maptable .val, table.maptable .cell-sub,
table.maptable .cell-title {
  max-width: 100%; white-space: normal; overflow-wrap: anywhere;
}

/* An outcome is a phrase, not a label: let it wrap rather than set the width
   of its column. */
table.grid--compact .outcome { white-space: normal; max-width: 22ch; display: block; }

/* --- live analysis surfaces ------------------------------------------------
   The real pages use this stylesheet, not a second one. A separate look would
   mean the design was reviewed on screens the product does not ship, and the
   whole point of rendering real output through the approved shell is that what
   a reviewer signs off is what a customer sees.

   What is added below is vocabulary the mock data never needed: severities that
   are high/medium/low rather than material/attention, dimension outcomes, and a
   policy citation block. Nothing here restyles an approved component. */


/* Severity, as the validator grades it. The tone RANKS; the word STATES. Every
   chip below is rendered with its label beside it, so the colour is never the
   only thing carrying the meaning. */
.chip--quiet { color: var(--ink-mid); background: transparent; border-color: var(--rule); }

/* The concern rows on a live channel page carry the same left rule, keyed to
   the same tones. A row is also a link here rather than a viewer trigger --
   there is a real page behind it. */
.citem--plain { border-left-color: var(--rule-firm); }
.citem--quiet { border-left-color: var(--rule); }
.citem--coverage { border-left-color: var(--coverage); }
.citem--settled { border-left-color: var(--settled); }
a.citem { text-decoration: none; }
a.citem h4 { color: var(--navy); }
a.citem:hover h4 { color: var(--gold); }

/* The headline claim, and what is allowed to make it. A refusal is not an
   error state -- it is the honest answer -- so it is drawn in the coverage
   tone rather than the severity one. */
.claimline {
  margin: 20px 0 6px; padding: 16px 18px; border-radius: 12px;
  border: 1px solid var(--rule-firm); background: var(--raised);
  font-size: 15.5px; line-height: 1.5; color: var(--ink);
  border-left: 4px solid var(--coverage);
}
.claimline--clear { border-left-color: var(--settled); }
/* A NARROW SCAN IS A COVERAGE LIMIT, NOT A SEVERITY. It is drawn in the
   coverage tone and washed to separate it from the claim it qualifies -- never
   in the clearance green, which would let a scan that looked at less earn the
   colour of one that looked at everything, and never in a severity tone, which
   would read as a finding about the channel rather than about the look. The
   word "Narrow scan" carries the meaning; the colour only separates it. */
.claimline--narrow { border-left-color: var(--coverage); background: var(--coverage-wash); }
.claimline b { color: var(--navy); }
.claimline .quiet { display: block; margin-top: 6px; color: var(--ink-mid); font-size: 13.5px; }

/* Areas that were not assessed. Listed, named, and never folded into a count
   of concerns -- a channel Centurion could not fully examine is not a channel
   with a problem, and the two must not share a number. */
.gapgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(310px, 1fr)); gap: 12px; margin-top: 14px; }
.gapcard {
  border: 1px solid var(--coverage-line); background: var(--coverage-wash);
  border-radius: 10px; padding: 13px 15px;
}
.gapcard h4 { margin: 0 0 5px; font-size: 14px; color: var(--ink); }
.gapcard p { margin: 0; font-size: 12.5px; color: var(--ink-mid); line-height: 1.5; }

/* The official YouTube basis. Every field comes from the stored corpus; the
   date is part of the citation because a policy quotation is a claim about
   what YouTube said AT A MOMENT. */
.policybox {
  border: 1px solid var(--rule-firm); border-left: 4px solid var(--gold);
  background: var(--raised); border-radius: 12px; padding: 18px 20px; margin-top: 12px;
}
.policybox .ptitle { margin: 0 0 4px; font-size: 15.5px; font-weight: 700; color: var(--ink); }
.policybox .pheading { margin: 0 0 10px; font-size: 13px; color: var(--ink-mid); }
.policybox .pmeta { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 0 0 10px; }
.policybox .purl { margin: 0 0 6px; font-size: 12.5px; word-break: break-all; }
.policybox .purl a { color: var(--gold); }
.policybox .pretrieved { margin: 0; font-size: 12px; color: var(--ink-mid); }
.policybox .pretrieved code { color: var(--ink-mid); }
.policybox .pmore { margin: 14px 0 4px; font-size: 12px; color: var(--ink-mid); text-transform: uppercase; letter-spacing: .08em; }
.policybox ul { margin: 0; padding-left: 18px; font-size: 13px; color: var(--ink-mid); }
.policybox ul li { margin-bottom: 4px; }
.policybox ul a { color: var(--navy-soft); }

/* Evidence the assessment cited, one row each. */
.evlist { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 8px; }
.evlist li {
  border: 1px solid var(--rule); background: var(--raised); border-radius: 9px;
  padding: 10px 13px; font-size: 13px; color: var(--ink-mid);
}
.evlist code { color: var(--gold); font-size: 12px; }
.evlist .kind { margin-left: 8px; font-size: 11.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-mid); }
.evlist .desc { margin: 6px 0 0; color: var(--ink); line-height: 1.5; }

/* A section the design calls for and no backend produces. Declared, blank, and
   labelled -- never filled with a plausible value. */
.shellnote {
  border: 1px dashed var(--rule-firm); border-radius: 10px; padding: 14px 16px;
  color: var(--ink-mid); font-size: 13px; background: transparent; margin-top: 12px;
}

/* One concern inside the live viewer. Only one is ever visible; the rest stay
   in the DOM so Previous/Next is instant and needs no round trip. Constrained
   to a reading measure because the viewer is full-bleed and prose that runs the
   width of a 1900px screen is prose nobody finishes. */
/* The walk set, listed. Numbered because the pager says "3 of 5" and the
   reader has to be able to find the 3. Wraps rather than scrolls: a horizontal
   strip hides its own tail, and the tail is the part nobody has read yet. */
/* The run-state legend. A definition list rather than table cells, because the
   meaning belongs to the state and not to any one row -- putting it in the rows
   repeated one sentence 53 times in a column too narrow to hold it. */
/* The action column, on EVERY live grid. A control you cannot see all of is one
   you do not trust -- and one you cannot click is worse, because it looks
   present. Under `table-layout: fixed` the column width comes from the HEADER
   row, which `table.grid th:last-child` sets to 136px; the `1%` here is inert
   on the td and kept only so an auto-layout grid still shrinks to fit. What
   actually clipped the button was `display: flex` on the cell -- see `.rowacts`. */
.grid td.rowacts, .grid th:last-child, .grid td:last-child { width: 1%; white-space: nowrap; }

/* Secondary identifiers -- a channel handle, a 24-character video id -- are
   reference material, not reading material. Left to wrap they demanded width
   from the columns carrying the finding. They truncate instead. */
.grid td:first-child .cell-sub {
  display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Column proportions for the runs table.
   Left to itself the browser gave the channel cell a third of the table and
   squeezed Outcome to about eleven characters, so every outcome wrapped to two
   words per line. The outcome is the column a manager reads; the channel id
   under the name is the part that can afford to wrap. */
.grid--runs { table-layout: fixed; }
.grid--runs th:nth-child(1), .grid--runs td:nth-child(1) { width: 19%; }
.grid--runs th:nth-child(2), .grid--runs td:nth-child(2) { width: 7%; }
.grid--runs th:nth-child(3), .grid--runs td:nth-child(3) { width: 26%; }
.grid--runs th:nth-child(4), .grid--runs td:nth-child(4) { width: 16%; }
.grid--runs th:nth-child(5), .grid--runs td:nth-child(5) { width: 15%; }
.grid--runs th:nth-child(6), .grid--runs td:nth-child(6) { width: 7%; }

/* The channel id sub-line is a 24-character opaque string. Left to wrap it
   demanded width the OUTCOME column needed -- and the outcome is what a manager
   reads, while the id is what they copy on the rare occasion they need it. It
   truncates rather than dictating the table. */
.grid--runs td:nth-child(1) .cell-sub {
  display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The action column has to fit its button. At 6% "Details ›" was clipped by the
   table edge -- a control you cannot fully see is a control you do not trust. */
.grid--runs th:nth-child(7), .grid--runs td:nth-child(7) { width: 10%; }
.grid--runs td:nth-child(7) { overflow-wrap: normal; white-space: nowrap; }
.grid--runs td { overflow-wrap: anywhere; }

/* The state legend. Two columns, so five states fall 2-2-1 instead of five
   full-width rows -- it is a key to the chips in the table, read once, and it
   was taking a screen-third above the table to be read once. Two columns rather
   than three: the Partial sentence is the longest in the product and a third
   column wraps it to four lines, which is taller than the row it saved. */
/* THREE columns holding 2, 2 and 1 -- counted down the columns, which is how a
   reader scans a key. Two columns filled row-first gave rows of 2-2-1 and
   therefore COLUMNS of 3 and 2, which is the same five items arranged to look
   lopsided. `grid-auto-flow: column` over two rows fills col1 (Cancelled,
   Expired), col2 (Partial, Queued), col3 (Running). */
.statekey { display: grid; gap: 6px 20px;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-rows: repeat(2, auto);
  grid-auto-flow: column;
  margin: 0 0 16px; padding: 11px 14px;
  border: 1px solid var(--rule); border-radius: 11px; background: var(--raised); }
.statekey > div { display: flex; gap: 10px; align-items: baseline; min-width: 0; }
.statekey dt { flex: 0 0 auto; min-width: 74px; margin: 0; }
.statekey dd { margin: 0; font-size: 12px; color: var(--ink-mid); line-height: 1.45; min-width: 0; }

@media (max-width: 1200px) {
  /* Back to reading down one column. Three columns at this width leave each
     one too narrow for the Partial sentence, which is the longest in the
     product and would wrap to five lines to save a row. */
  .statekey { grid-template-columns: minmax(0, 1fr); grid-template-rows: none;
    grid-auto-flow: row; }
}

.setstrip { margin: 0 0 22px; padding: 14px 16px; border: 1px solid var(--rule);
  border-radius: 11px; background: var(--raised); }
.setstrip .sec-note { margin: 0 0 9px; }
/* The walk-set picker. Replaced a wrapped strip of pills: complete sets run to
   42 members and the strip pushed the video itself below the fold. */
.setpick { display: flex; align-items: center; gap: 10px; }
.setpick__label {
  flex: 0 0 auto; font-size: 12px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-mid); font-weight: 700;
}
/* A FIXED HEAD, A FULL-WIDTH LIST. The closed control is 200px and truncates;
   the open list is drawn by the browser and sizes itself to the longest option,
   so nothing is lost by narrowing the head. Applied via a modifier rather than
   to `.setpick__select` itself: the video walk picker shares this class and its
   options ("1. POV: Azov Fighters Assault — AZOV") need the room. */
.setpick--fixed .setpick__select {
  flex: 0 0 200px; width: 200px; max-width: 200px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  /* Room on the right for the caret the browser draws inside the control, and
     matching room on the left so the label is not pinned to the border. At
     200px the ellipsis lands where the text runs out, not under the arrow. */
  padding: 9px 32px 9px 12px;
}
.setpick--fixed { justify-content: flex-start; }

.setpick__select {
  flex: 1 1 auto; min-width: 0; max-width: 100%;
  padding: 8px 11px; border-radius: 9px;
  border: 1px solid var(--rule); background: var(--raised); color: var(--ink);
  font: inherit; font-size: 13.5px;
}
.setpick__select:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

.setlist { list-style: none; display: flex; flex-wrap: wrap; gap: 7px; margin: 0; padding: 0; }
.setitem {
  display: inline-flex; align-items: baseline; gap: 7px; font-size: 12.5px;
  padding: 6px 11px; border-radius: 999px; border: 1px solid var(--rule-firm);
  background: var(--paper); color: var(--ink-mid); text-decoration: none;
}
a.setitem:hover { border-color: var(--gold); color: var(--ink); }
.setitem b { font-variant-numeric: tabular-nums; color: var(--ink-mid); font-weight: 700; }
.setitem.is-on { border-color: var(--gold); color: var(--ink); font-weight: 640; }
.setitem.is-on b { color: var(--gold); }

/* 76ch was a READING measure, set when this panel was one column of prose. With
   the decision aside inside it, that cap became the width of both columns
   together and squeezed the reading to ~34ch — narrower than before the aside
   existed. The measure belongs on the reading column, not on the container. */
.lvpanel { max-width: none; padding-bottom: 40px; }
.lvpanel[hidden] { display: none; }

.prose { font-size: 14.5px; line-height: 1.65; color: var(--ink); max-width: 78ch; margin: 6px 0 0; }
.prose--quiet { color: var(--ink-mid); }
/* The channel profile, side by side. Prose keeps a reading measure on the left;
   the eight axis readings sit in the gutter they were previously leaving empty.
   1.15fr/1fr rather than equal halves: the summary is a paragraph and the axes
   are label/value pairs, so the text column earns the extra. */
.profilegrid { display: grid; gap: 8px 34px;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); align-items: start; }
.profilegrid__axes { border-left: 1px solid var(--rule); padding-left: 26px; }
.profilegrid__head { margin: 0 0 2px; font-size: 12px; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase; color: var(--ink-mid); }
.profilegrid__axes .deflist { margin-top: 8px; }

@media (max-width: 1100px) {
  /* Stacked again where two columns would each be too narrow: the axis VALUES
     wrap ("No commercial signal observed — recorded as unknown, not as none")
     long before the labels do. */
  .profilegrid { grid-template-columns: minmax(0, 1fr); }
  .profilegrid__axes { border-left: 0; padding-left: 0; }
}

.deflist { display: grid; grid-template-columns: max-content 1fr; gap: 6px 22px; margin: 12px 0 0; font-size: 14px; }
.deflist dt { color: var(--ink-mid); }
.deflist dd { margin: 0; color: var(--ink); }
.unknown { color: var(--ink-mid); font-style: italic; }

/* The one nav item that crosses between the shell and the live output. Marked
   because the two look identical on purpose, and an unmarked crossing is how a
   reviewer ends up quoting a mock number. */
.navshell, .navlive { color: var(--ink-mid) !important; font-size: 13px; }
.navlive:hover { color: var(--settled) !important; }
.navshell:hover { color: var(--gold) !important; }

/* A title that is also a link keeps the table's type, not the browser's. The
   prototype's `.entity` already did this; the live tables link the title cell
   directly and were picking up a default underline. */
a.cell-title { text-decoration: none; color: var(--navy); }
a.cell-title:hover { color: var(--gold); }

/* Frames retained for one video. Bounded on purpose and captioned with the
   SCENE start rather than the frame's own position -- several frames are stored
   per scene and their individual timings were never recorded, so a per-frame
   timestamp would be a precision the detector did not have. */
.framegrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; margin-top: 14px; }
.frame { margin: 0; border: 1px solid var(--rule); border-radius: 10px; overflow: hidden; background: var(--raised); }
.frame img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; background: var(--ground); }
.frame figcaption { padding: 7px 10px; font-size: 11.5px; color: var(--ink-mid); font-variant-numeric: tabular-nums; }

/* The bounded preview strip. Frames past the sixth stay in the DOM -- the
   lightbox walks all of them -- but do not lengthen the page. `display:none`
   rather than `hidden` so the JS can reveal them without fighting the
   attribute. */
.framegrid--strip { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.frame--rest { display: none; }
.framegrid--all .frame--rest { display: block; }

/* The frame itself is the control that opens it. A button wrapper rather than a
   click handler on the image, so it is reachable by keyboard and announces
   itself -- an image that silently opens a dialog on click does neither. */
.frame-open {
  display: block; width: 100%; padding: 0; border: 0; background: none; cursor: zoom-in;
}
.frame-open:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }

/* The analysis-details drawer is a section container, not one observation, so
   it carries more weight than the observation drawers nested inside it. */
/* "About this page". TOP RIGHT WHEN CLOSED, FULL WIDTH WHEN OPEN, and that is
 * the whole of the new styling -- everything else is `.cdrawer` as it already
 * is, so the caret, the focus outline and the announced open state come for
 * free. A control that is a small chip until it is needed, and a readable
 * column once it is. */
.cdrawer--about { width: max-content; max-width: 100%; margin-left: auto; }
/* SAME PLACE ON EVERY PAGE, which is most of the point of one component.
 * `.pagehead` wraps, and its text column sizes to its content -- so on a page
 * with a long lede the control wrapped onto its own line and sat 194px lower
 * than on the page beside it. Measured: y=150 on `videos`, y=344 on `actions`.
 * The text column flexes and the control does not, so they stay on one line. */
.pagehead--about > :first-child { flex: 1 1 340px; min-width: 0; }
.pagehead--about > .cdrawer--about { flex: 0 0 auto; margin-top: 4px; }
.cdrawer--about[open] { width: 100%; }
.cdrawer--about > .cdrawer-head { padding: 9px 15px; }
.cdrawer--about .cdrawer-title { font-size: 13px; margin-bottom: 0; }
.cdrawer--about .cdrawer-body p { margin: 0 0 11px; max-width: 82ch; font-size: 13.5px; line-height: 1.62; color: var(--ink-mid); }
.cdrawer--about .cdrawer-body p:last-child { margin-bottom: 0; }

.cdrawer--analysis { border-color: var(--rule-firm); background: var(--raised); }
.cdrawer--analysis > .cdrawer-head { padding: 15px 18px; }
.cdrawer--analysis > .cdrawer-head .cdrawer-title { font-size: 16px; font-weight: 700; }

/* The lightbox reuses the video modal's frame; only the media differs. */
#fbox-img { display: block; width: 100%; height: auto; background: var(--ground); border-radius: 8px; }

/* An evidence ref that reaches its video. The id is already the first segment
   of the ref, so the ref itself is the link -- printing the id beside it said
   the same thing twice. */
.evlist a code { color: var(--gold); text-decoration: underline; text-underline-offset: 3px; }
.evlist a:hover code { color: var(--ink); }

/* Previous / position / next on a live page. The prototype pages through a set
   inside its viewer; these are real routes, so the same move is a link. */
.livepager { display: flex; align-items: center; gap: 10px; }
.livepager-pos { font-size: 12.5px; color: var(--ink-mid); font-variant-numeric: tabular-nums; white-space: nowrap; }
.livepager [aria-disabled="true"] { opacity: .4; cursor: default; }

/* The back link and the pager, on a row of their own.
   They used to live in `.pagehead-actions`, which is inline with the title, so
   a short title put them on the right and a title that wrapped pushed them to
   the next line -- the Next button moved on almost every step of a walk through
   eight videos. Controls a reader clicks repeatedly have to stay still. */
.pagebar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 2px 0 20px; }

/* A concern row with no id column. `.citem` is a three-column grid whose first
   column held the raw `specialist_id`; with the id gone the row is two columns,
   and without this the chip would land in the flexible middle column instead of
   the right edge. */
.citem--flush { grid-template-columns: 1fr auto; }

/* The embedded player on a live video page. */
.player-shell iframe { display: block; width: 100%; aspect-ratio: 16 / 9; border: 0; border-radius: 10px; background: #000; }
/* `.tsplay` WAS REDEFINED HERE and is not any more -- it has one definition
   above. The duplicate won the base at equal specificity while the first rule's
   hover survived, which is how `var(--ink)` came to sit on `#35405A`. */
[data-seek].is-seeking { outline: 2px solid var(--gold); outline-offset: 2px; }

/* --- fixed-layout table overrides, last so they win --------------------------
   These sit at the end of the file deliberately: the base `thead th` rule sets
   `white-space: nowrap`, and an override placed beside the table definition was
   losing to it on source order. Under fixed layout a nowrap header wider than
   its share does not widen the column, it prints over the next one -- "HIGHEST
   SEVERITY" was drawn across "COVERAGE" on the nine-column channel table. Two
   short lines is the right answer for a narrow column; a collision never is. */
table.grid thead th { white-space: normal; vertical-align: bottom; }
table.grid thead th.num { white-space: nowrap; }

/* Progress on a queue row. Slim, behind the stage name, and drawn only for a
   run that is actually running -- see `_live_progress`. The number sits beside
   the bar rather than inside it: inside, it is unreadable at 4% and lands on
   the wrong side of the fill at every width in between. */
.rowprog { display: flex; align-items: center; gap: 7px; margin-top: 5px; }
.rowprog__bar {
  flex: 1 1 auto; height: 4px; min-width: 40px; border-radius: 999px;
  background: var(--rule); overflow: hidden;
}
.rowprog__bar > span { display: block; height: 100%; background: var(--gold); border-radius: 999px; }
.rowprog__num { flex: 0 0 auto; font-size: 11.5px; color: var(--ink-mid); font-variant-numeric: tabular-nums; }

/* --- the video as the unit of work (2026-08-21) ---------------------------
   The review block on a video page. Deliberately quiet: it is a plan drawn
   from evidence linkage, not a verdict, and it sits above the concern detail
   rather than competing with it. Every "not connected" reads as muted text,
   never as a disabled input -- an input a manager can click and type into is
   an input that loses what they typed. */
/* WRITTEN AGAINST A VOCABULARY THAT DID NOT EXIST. This block and the one
   below it named `--panel`, `--line`, `--muted`, `--muted-fg` and a
   `--sev-high/medium/low` family. `:root` declares none of them. An undefined
   var() in a SHORTHAND takes the whole declaration with it, so
   `border: 1px solid var(--line)` left `.vplan` and `.vchip` with no border
   AND -- on the next line -- no background either: a card with no edge and no
   surface, rendered as loose text on the page ground while every other card on
   the same scroll had both. That is what made this section unreadable.

   Everything here is now keyed to tokens `:root` actually declares, and
   `tests/test_stylesheet_tokens.py` fails the build if that stops being true.
   Note the severity modifiers are keyed on the design's TONE names, not on raw
   severities: `vchip--low` and `vchip--none` were emitted and matched nothing. */
.vplan { margin: 22px 0 26px; padding: 18px 20px; border: 1px solid var(--rule);
         border-radius: 12px; background: var(--paper); box-shadow: var(--shadow); }
/* 180px could not hold "Packaging read; file not downloaded" -- `.chip` is
   `white-space: nowrap`, so the chip ran straight over the value in the next
   column. The track now fits the longest real label AND the chip may wrap,
   the same exemption `table.grid td .chip` already gets. */
/* The linkage sentence, at reading rank. `.lede` alone is the page-opening
   size; inside a section it sits one step down so it does not compete with the
   h2 immediately above it. */
.vplan__lede { font-size: 15.5px; line-height: 1.6; color: var(--ink-mid);
               max-width: 82ch; margin: 6px 0 0; }
/* The note under the lede shares its measure. Running one to 78ch and the
   other to the full 1290px made two halves of one thought look like a heading
   and an unrelated footnote. */
.vplan .sec-head .sec-note { max-width: 82ch; }
/* THREE UP, TWO DOWN -- not auto-fit. Six cells against `auto-fit` landed as
   five and one, leaving DUE alone on a row of its own with four empty columns
   beside it, which reads as a rendering fault rather than as a list. An even
   3x2 also gives each label room to sit on one line. */
.vplan__facts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
                gap: 16px 26px; margin: 16px 0 4px; padding: 15px 0 2px;
                border-top: 1px solid var(--rule); }
@media (max-width: 980px) { .vplan__facts { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) { .vplan__facts { grid-template-columns: minmax(0, 1fr); } }
.vplan__facts > div { min-width: 0; }
.vplan__facts dt { font-size: 12px; font-weight: 700; letter-spacing: .04em;
                   text-transform: uppercase; color: var(--ink-mid); margin-bottom: 4px; }
.vplan__facts dd { margin: 0; font-size: 14px; }
.vplan__facts dd .chip { white-space: normal; }
.vplan__actions { margin-top: 18px; }
/* A sub-heading was 14px -- smaller than the 14px values above it and smaller
   than the body it introduced, so it ranked BELOW its own content. */
.vplan__actions h3, .vplan__chips h3 { font-size: 15px; font-weight: 700; color: var(--navy);
                                       margin: 0 0 6px; letter-spacing: -.005em; }
.vplan__actions ul { margin: 8px 0 0; padding-left: 18px; }
.vplan__actions li { margin-bottom: 6px; }
.vplan__chips { margin-top: 18px; display: flex; flex-wrap: wrap; gap: 8px; }
.vplan__chips h3 { flex-basis: 100%; }
.vplan__chips .sec-note { flex-basis: 100%; margin-top: 2px; }
/* `border: 1px solid` with NO var() in the shorthand, the way `.chip` (line
   ~250) already writes it: an unmatched modifier then degrades to a
   currentColor edge rather than to nothing at all. */
.vchip { display: inline-flex; flex-direction: column; gap: 3px; padding: 8px 12px;
         border: 1px solid; border-color: var(--rule-firm); border-radius: 9px;
         background: var(--raised); font-size: 13px; max-width: 100%; }
.vchip b { font-weight: 650; color: var(--ink); }
.vchip .quiet { font-size: 12px; color: var(--ink-mid); }
.vchip .dot { font-size: 9px; line-height: 1; }
.vchip--material { border-color: var(--material); }
.vchip--attention { border-color: var(--attention); }
.vchip--plain { border-color: var(--rule-firm); }
.vchip--quiet { border-color: var(--rule); }
.vplan__shell { margin-top: 18px; padding-top: 12px; border-top: 1px dashed var(--rule-firm); }

/* --- manage this video review (2026-08-21) --------------------------------
   The MANAGER's panel, and the primary workflow on the video page. It reads as
   a card of the same family as `.vplan` above it -- same surface, same edge --
   because they are two halves of one job: what the video contributed, and what
   you decided to do about it. */
.vwf { margin: 22px 0 26px; padding: 18px 20px; border: 1px solid var(--rule);
       border-left: 3px solid var(--gold); border-radius: 12px;
       background: var(--paper); box-shadow: var(--shadow); }
.vwf__standing { margin: 14px 0 4px; font-size: 15px; display: flex; flex-wrap: wrap;
                 align-items: baseline; gap: 4px 10px; }
.vwf__standing b { font-weight: 700; color: var(--ink); }
/* The creator-state sentence, beside the state rather than under the fold. */
.vwf__notsent { flex-basis: 100%; font-size: 12.5px; color: var(--attention); }
.vwf__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
             gap: 0 20px; margin-top: 14px; }
@media (max-width: 900px) { .vwf__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px) { .vwf__grid { grid-template-columns: minmax(0, 1fr); } }
.vwf__sendnote { margin: 2px 0 16px; font-size: 12.5px; color: var(--ink-mid); line-height: 1.55;
                 padding: 9px 12px; border-radius: 8px; background: var(--raised);
                 border: 1px solid var(--rule-firm); }
.vwf__sendnote code { font-size: 11.5px; }
.vwf__actions { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 14px; }
.vwf__actions .quiet { font-size: 12.5px; }
.vwf__history { margin-top: 16px; }
/* The workflow line on the dashboard. Not a card: see the template comment. */
.vwfline { margin-top: 10px; }
.vwfline__late { color: var(--material); }

/* --- channel-level action groups (2026-08-21) -----------------------------
   A finding held where it was reasoned: across a channel's examined sample.
   The video chips beneath are EVIDENCE, so they read as links to look at, not
   as a checklist of work. */
.cgroups { display: flex; flex-direction: column; gap: 14px; margin-top: 12px; }
/* The `border` shorthand died on `--line` here too, which took `border-style`
   with it -- so the surviving `border-left-width: 3px` longhand had no style to
   paint and the severity rank was invisible on the Actions page as well. */
.cgroup { border: 1px solid var(--rule); border-left-width: 3px; border-radius: 11px;
          padding: 15px 18px; background: var(--paper); box-shadow: var(--shadow); }
.cgroup--material { border-left-color: var(--material); }
.cgroup--attention { border-left-color: var(--attention); }
.cgroup--plain { border-left-color: var(--rule-firm); }
.cgroup--quiet { border-left-color: var(--rule); }
.cgroup__head { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.cgroup__title { margin: 0; font-size: 15px; }
.cgroup__scope { margin: 4px 0 0; font-size: 13px; color: var(--ink-mid); }
.cgroup__meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 12px; }
.cgroup__action { margin: 12px 0 0; font-size: 14px; }
.cgroup__evidence-label { margin: 16px 0 7px; font-size: 12px; font-weight: 700;
                          letter-spacing: .04em; text-transform: uppercase;
                          color: var(--ink-mid); }
.cgroup__chips { display: flex; flex-wrap: wrap; gap: 8px; }
/* A TITLE IS A LABEL HERE, NOT THE TEXT. These are evidence links, and the
   real titles run to eighty characters of Cyrillic and emoji -- four of them
   wrapped into a paragraph-shaped block that buried the finding above it. The
   chip is capped and the title is clipped to one line; the full text is on the
   anchor's `title` so nothing is lost, and the second line still says what the
   video contributed. */
.cgroup__chips .vchip { text-decoration: none; max-width: min(100%, 34ch); }
.cgroup__chips .vchip b { display: block; white-space: nowrap; overflow: hidden;
                          text-overflow: ellipsis; max-width: 100%; }
.cgroup__chips .vchip:hover { border-color: var(--gold); background: var(--paper); }
.cgroup__foot { margin: 14px 0 0; }

/* --- video list filters (2026-08-21) --------------------------------------
   GET-only, so a filtered list is a URL somebody can send to a colleague. */
.filterbar { margin: 14px 0 10px; }
.filterbar__row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.filterbar__row .finput { min-width: 150px; }
.filterbar__row input[type="search"] { min-width: 240px; flex: 1 1 240px; }

/* --- why a row is in the attention queue (2026-08-21) --------------------- */
.whyhere { display: block; margin-top: 5px; }
.whychip { display: inline-block; padding: 2px 8px; margin: 3px 4px 0 0; border-radius: 999px;
           border: 1px solid var(--rule-firm); color: var(--ink-mid);
           font-size: 11px; letter-spacing: .01em; }
.whychip--quiet { opacity: .65; }

/* --- today's review (2026-08-21) ----------------------------------------- */
.attentionrow { margin: 6px 0 22px; }
.attentionrow .kpi { text-decoration: none; }
.kpi--act { border-color: var(--gold); }

/* THE FRONT DOOR. The only page on this surface that creates anything, and the
   only one a signed-out visitor can open. Four classes, each declared here
   rather than borrowed from a shape that means something else: `.credential__pair`
   is the closest existing key-value list and it belongs to the API-credential
   panel, so reusing it would tie an intake screen's layout to a change made for
   a different page. */
.cardtitle { margin: 0 0 14px; font-size: 16.5px; font-weight: 640; color: var(--ink); }
.kv { display: grid; grid-template-columns: max-content 1fr; gap: 7px 20px; margin: 0 0 20px; }
.kv > div { display: contents; }
.kv dt { color: var(--ink-mid); font-size: 12.5px; }
.kv dd { margin: 0; color: var(--ink); }
.counts { border: 0; padding: 0; margin: 20px 0 0; display: flex; flex-wrap: wrap; gap: 16px; }
.countfield { display: flex; flex-direction: column; gap: 5px; font-size: 12.5px; color: var(--ink-mid); }
.countfield .field { width: 7.5rem; }
.counts > .small { flex-basis: 100%; margin: 0; }

/* --- the videos queue: six columns, width by content -----------------------
 *
 * TEN COLUMNS SHARING ONE ALLOWANCE was the defect. Seven of them rendered at
 * the same width, so "Downloaded and examined · 11 scenes · transcript" got the
 * room given to "Named by: 1", wrapped to four lines, and a row cost 107-137px.
 * Every width below is chosen from what the column actually holds.
 *
 * `grid--compact` is on twenty-one tables, so none of this is put there.
 * Two classes beat one, so these win over `table.grid` wherever they overlap.
 */
/* WIDTH FOLLOWS WHAT IS READ, NOT WHAT IS LONGEST. This was 32% to let
 * 102-character titles wrap to two lines. The owner's correction is that the
 * title is not what a manager triages on -- the id, format, duration and
 * published date are -- so the title is trimmed to 50 characters and the width
 * moves to the column doing the work.
 * Video 25%: a 50-character title on one line, with id · format · duration ·
 *   published beneath it.
 * Channel 11%: names are cut at 28 characters upstream and fit one line.
 * Why it is here 26%: the column this instruction exists for. Holds
 *   "2 concerns cite it · not downloaded" on one line and the timestamp
 *   variant on two -- 7 rows of 59 carry that.
 * Severity 9%: one chip, one word and a mark, and it may not break.
 * Review 12%: a chip with an optional due date beneath it.
 * Open 17%: the button and the disclosure summary side by side. At 15% the
 *   summary wrapped and the cell, not the title, set the row height. */
table.grid.grid--queue th:nth-child(1), table.grid.grid--queue td:nth-child(1) { width: 25%; }
table.grid.grid--queue th:nth-child(2), table.grid.grid--queue td:nth-child(2) { width: 11%; }
table.grid.grid--queue th:nth-child(3), table.grid.grid--queue td:nth-child(3) { width: 24%; }
table.grid.grid--queue th:nth-child(4), table.grid.grid--queue td:nth-child(4) { width: 9%; }
table.grid.grid--queue th:nth-child(5), table.grid.grid--queue td:nth-child(5) { width: 12%; }
/* 19%, MEASURED RATHER THAN CHOSEN. The button, the gap and the summary label
 * need 215px; the cell's content box is its width less 24px of padding, so 17%
 * gave 189px and 18% gave 209px and BOTH wrapped the label below the button --
 * which then, not the title, set every row to 71px. 19% gives 222px. The row
 * height was never the title's fault after the first fix. */
table.grid.grid--queue th:last-child,   table.grid.grid--queue td:last-child   { width: 19%; white-space: normal; }

/* THE ROW HEIGHT IS THE POINT OF THE PASS, so the padding is part of the
 * design rather than inherited. 7px against 14px, with tight line boxes: a
 * one-line title lands near 48px and a two-line one near 67px, which is what
 * keeps a WRAPPED title under the target instead of ellipsising it. */
table.grid.grid--queue td { padding: 7px 12px; }
/* `display: block` matters more than the line-height did. As an inline `<a>`
 * the title shared a line box with the sub beneath it and carried the leading
 * of both; measured, that alone was 11px a row. */
/* ONE LINE, CLIPPED AT THE COLUMN EDGE. `table.grid td .cell-title` already
 * sets `overflow: hidden` and an ellipsis; what it does not set is `nowrap`,
 * so the title wrapped to two lines and the ellipsis never fired. Owner
 * decision: trim to fit the column. */
table.grid.grid--queue td .cell-title {
  display: block; line-height: 1.25; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
table.grid.grid--queue td .cell-sub { line-height: 1.15; margin-top: 0; }

/* The button and the disclosure on one line. On the wrapper, never on the
 * `<td>`: a flex `<td>` leaves the table layout and rows measured 414px. */
/* WRAPS RATHER THAN OVERFLOWS. `table.grid td .chip` sets `white-space: normal`
 * for the long coverage chips elsewhere, which on a one-word severity chip broke
 * "Medium" across two lines and left an "m" on its own -- a 46px chip in a 62px
 * row. And at an intermediate width the button and summary together needed
 * 225px in a 213px cell, so 12 of 12 rows overflowed. Wrapping costs a few
 * pixels of height only where the room is genuinely absent. */
table.grid.grid--queue td .chip { white-space: nowrap; }
.vqact { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.vqact details { margin: 0; }
/* The disclosure names what it holds and stays quiet doing it. The label is
 * never cut: `table.grid td .cell-sub` is `nowrap` with an ellipsis, and the
 * label is what a reader uses to decide whether to open it. */
.vqmore summary { cursor: pointer; overflow: visible; text-overflow: clip; }
.vqmore summary:hover { color: var(--ink-mid); }

/* THE PANEL TAKES THE ROW, NOT THE CELL. In the 144px cell its content laid out
 * at 404px, pushed the table 249px past its wrapper, and `overflow: hidden`
 * hid it -- no scrollbar and no ellipsis. Out of flow, so it cannot widen the
 * table whatever it holds; and the lines WRAP here, unlike everywhere else a
 * `.cell-sub` appears in a grid. */
table.grid.grid--queue tbody tr { position: relative; }
.vqpanel {
  position: absolute; left: 0; right: 0; top: 100%; z-index: 6;
  display: grid; gap: 3px;
  padding: 10px 12px;
  background: var(--raised); border: 1px solid var(--rule-firm); border-radius: 10px;
  box-shadow: var(--shadow);
}
.vqpanel .cell-sub { white-space: normal; overflow: visible; text-overflow: clip; }

/* The permissions list on /settings/people, folded. It flows INSIDE the
   `<details>` rather than in a `.vqpanel`: the panel is absolute against a
   positioned row, and anchoring one here would mean a `position: relative`
   modifier on a table that has no other need of it. Closed, the cell is one
   line; open, the list wraps in place and the row grows, which is the honest
   behaviour for a list somebody asked to see. */
.permfold > .cell-sub--wrap { display: block; margin-top: 6px; max-width: none; }

/* Only while something is open, and only then. The wrapper clips by design --
 * `.table-wrap`'s comment records that removing its scroll is what revealed
 * these tables never fit -- so this is scoped to the one state that needs it
 * rather than made a property of every grid on the site. */
.table-wrap:has(.vqmore[open]) { overflow: visible; }

/* One clause per row, and it may wrap rather than be cut. */
.whyline { display: block; line-height: 1.3; }

/* THE LIMIT, IN THE COVERAGE COLOUR AND IN WORDS. Never the colour alone --
 * `--coverage` exists so a limit on the look never reads as a grade, and the
 * two words carry the meaning on their own for a reader who does not separate
 * the tones. */
.covword { color: var(--coverage); }

/* The demoted five. A `<summary>` is keyboard reachable with no script. */
.vqmore { margin-top: 3px; }
.vqmore .cell-sub { display: block; }

/* --- the video page's verdict line -----------------------------------------
 *
 * The page answered with the system's reasoning before the manager's task --
 * 2,220px of scroll before the first finding. This is the answer, above
 * everything, and it is a LINE rather than a panel: rule 1, supporting
 * information does not get the prime reading area, and a heading over a
 * one-sentence answer is a label and its answer taking two lines.
 */
.verdict {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin: 14px 0 0; padding: 12px 14px;
  background: var(--paper); border: 1px solid var(--rule); border-radius: 10px;
  font-size: 14px; color: var(--ink);
}
.verdict b { font-weight: 650; }
.verdict__sep { color: var(--ink-mid); }
.verdict .btn { margin-left: auto; }

/* What was observed, in prose rather than a taxonomy label. */
.cprose { margin: 6px 0 0; font-size: 13px; color: var(--ink-mid); line-height: 1.45; }

/* The fold NAMES what it collapses -- decision 2, and CONSTRAINTS.md 3 read
 * the other way round: a page showing only the cards would imply the rest were
 * never looked at. Names, not rows, because the rows were the repetition. */
.cnames { margin: 8px 0 0; font-size: 13px; line-height: 1.7; }
.cnames .countlink { background: none; border: 0; padding: 0; cursor: pointer; font: inherit; }

/* --- the video page's disclosure row ---------------------------------------
 *
 * Requirement 6: the classification, the coverage detail, the scene list and
 * the contribution strip collapse. Four summary rows at ~44px each in place of
 * 1,857px of always-open sections -- and COLLAPSED, NOT REMOVED: every one is
 * one click away and its summary says what is inside before it is opened.
 *
 * The coverage drawer carries its VALUE on the summary and opens itself when
 * the file was never downloaded -- decision 6. A reader who sees findings and
 * no coverage note takes the absence of others as their absence in fact, when
 * they were never looked for.
 */
.drawerrow { margin-top: 10px; }
.drawerrow__d { border: 1px solid var(--rule); border-radius: 10px; background: var(--paper); }
.drawerrow__d > summary {
  cursor: pointer; padding: 12px 14px; font-weight: 650; font-size: 14px; color: var(--navy);
  display: flex; align-items: center; gap: 10px;
}
.drawerrow__d > summary:hover { color: var(--ink); }
.drawerrow__d[open] > summary { border-bottom: 1px solid var(--rule); }
.drawerrow__d > *:not(summary) { padding: 0 14px; }
.drawerrow__d[open] > *:not(summary):last-child { padding-bottom: 14px; }

/* The observation's source, so a card cannot present what was seen in a
 * thumbnail as what was seen in the video. */
.cprose__src { color: var(--ink-mid); font-weight: 650; }
/* The fold marks the findings shown as cards above. A word, not a colour. */
.cnames__mark { color: var(--ink-mid); }
/* The verdict line is one sentence; the button sits at its end. */
.verdict__say { flex: 1 1 auto; line-height: 1.5; }

/* --- is this channel for children ------------------------------------------
 *
 * Right-aligned to the channel name. The switch is what CENTURION assesses; the
 * line beneath is YOUTUBE's own declaration, which CRI reads and cannot set --
 * they are two facts from two sources and the type scale keeps them apart, with
 * the attribution in the words rather than only in the styling.
 */
.kidsbox { margin-left: auto; text-align: right; max-width: 300px; }
.kidsbox__label { margin: 0 0 6px; font-size: 11px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-mid); font-weight: 650; }
.kidsbox form { margin: 0 0 6px; }
.kidsbox__state { margin: 0; font-size: 13px; color: var(--ink); }
.kidsbox__yt { margin: 4px 0 0; font-size: 12px; color: var(--ink-mid); }
.kidsbox__which { margin-top: 6px; font-size: 12px; }
.kidsbox__which > summary { cursor: pointer; color: var(--ink-mid); }
.kidsbox__which > p { margin: 4px 0 0; color: var(--ink-mid); line-height: 1.5; }
.kidsbox__note { margin: 8px 0 0; font-size: 12px; color: var(--ink-mid); line-height: 1.5; }
@media (max-width: 700px) { .kidsbox { margin-left: 0; text-align: left; } }

/* The video-level switch sits in the title row like the channel's, and says
 * WHICH of five levels decided -- "off" with no reason is a state a manager
 * cannot act on. The against-YouTube line is a statement, not a warning: the
 * product reports rather than enforces. */
.kidsbox--video { max-width: 280px; }
/* THE BOX'S OWN WAY OF SAYING "THIS LINE MATTERS MORE", now naming two elements
   instead of one. The video page has had this treatment since the box was built;
   the channel page's YouTube line needs it for exactly one of its four states --
   the one where the videos and the channel disagree. Extended rather than copied,
   so the emphasis cannot drift into two slightly different weights. No colour, no
   chip, no icon: `--ink` on the card ground is what the box already uses to
   promote a line. Measured on the RENDERED page against the ground the box
   actually sits on -- `rgb(251,251,250)` light and `rgb(10,10,10)` dark, neither
   of which is `--paper`:

       emphasised  --ink       16.83:1 light   18.97:1 dark
       baseline    --ink-mid    7.25:1 light   11.96:1 dark

   AA wants 4.5 for body text and all four clear it. Computing from `--paper`
   instead gives 17.43 and 17.95 -- close enough to pass either way here, and
   wrong, which is the habit worth not having. */
.kidsbox__note--against,
.kidsbox__yt--against { color: var(--ink); font-weight: 550; }

/* THE COVERAGE LINE, WHICH IS ON EVERY CHANNEL AND IS NOT A WARNING. It states
   what the reading rests on, so an empty box is never read as a clean bill --
   the detector has ever completed on 8 of 16 channels, and without this line
   "nothing shown" and "nothing looked" are the same pixel. Muted deliberately:
   it is context for whatever is or is not above it, not a finding. */
.kidsbox__cov { margin: 8px 0 0; font-size: 11.5px; color: var(--ink-mid); line-height: 1.5; }

/* THE OBSERVATION, CLAMPED TO FOUR LINES. Not truncated in the view: every word
   stays in the DOM and in the accessibility tree, so a screen reader and a
   copy-paste get the whole quotation and only the painting is bounded. The clamp
   breaks at a LINE BOX, so it never cuts mid-word -- which `truncatechars`
   would, and which is why that filter is not used here.

   `-webkit-line-clamp` is the only cross-browser spelling that works today; the
   `max-height` beneath it is what bounds the box in anything that ignores the
   clamp, at four times the line height. */
.kidsbox__excerpt {
  margin: 6px 0 0; padding: 0 0 0 9px; border-left: 2px solid var(--rule-firm);
  font-size: 11.5px; line-height: 1.5; color: var(--ink-mid); font-style: normal;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4;
  overflow: hidden; max-height: calc(11.5px * 1.5 * 4);
}

/* THE REFERENCE. The first external link in design-v2, so it is marked as one
   rather than left to look like an internal navigation: the box is 300px of
   muted text and an unmarked link inside it reads as another sentence. */
.kidsbox__ref { margin: 6px 0 0; font-size: 11.5px; line-height: 1.5; }
.kidsbox__reflink { color: var(--gold); text-decoration: underline; text-underline-offset: 2px; }
.kidsbox__reflink:hover { color: var(--ink); }

/* The channel context, reduced to one surfaced line above the work list. The
 * rest is in the drawer directly beneath it, which already held the evidence
 * behind these same archetypes. */
.profileline { margin-top: 4px; }

/* The channels table anchors its own disclosure panel.
 *
 * SCOPED TO THIS TABLE, NOT TO `grid--compact`. That class is on 29 table
 * instances across 22 templates; giving every row of all of them a containing
 * block and a stacking context to serve one table would silently re-anchor any
 * absolutely-positioned descendant added to any of them later. Surveyed at the
 * time: none has one today, so the blanket was avoidable rather than harmless.
 */
table.grid.grid--channels tbody tr { position: relative; }

/* === design-v3 =============================================================
 *
 * ONE STYLESHEET, RULED. v3 ADDS; it does not fork the palette, the light/dark
 * mechanism or the theme guard. Every rule below is prefixed `v3-` because 13 class
 * names already collided between the v3 prototype and this file -- `.chip` (12
 * rules), `.evidence` (10), `.kpi` (9), `.btn` (7), `.rail` (3) -- and under one
 * shared stylesheet a collision is a silent override in BOTH directions.
 *
 * EVERY GROUND HERE FLIPS, so every ink here flips with it. These surfaces sit on
 * `--paper`, `--ground` and `--raised`, which move with the theme, so the correct
 * ink is a moving token -- the opposite of the marker rules above, which are pinned
 * because `.track` is pinned. The rule is that a surface and its ink AGREE, never
 * that ink is always pinned.
 */

/* THE ICON, AND ITS BOX IS THE WHOLE POINT.
 *
 * An inline `<svg>` with no `fill` and no `stroke` paints SOLID BLACK, and one with
 * no width collapses to nothing. Both were true here: eight use sites rendered as
 * black blobs, and no test said a word, because the class guard asks "is there a
 * rule" and any rule satisfies it. A screenshot somebody looked at is what found it.
 *
 * So `fill: none` and `stroke: currentColor` are load-bearing, not tidiness: the
 * symbols in `_v3_icons.html` are stroke-drawn outlines and inherit their colour
 * from the text they sit beside, which is what makes them theme-correct for free.
 * `test_every_v3_icon_has_a_non_zero_box` asserts the dimensional half, because
 * "defined" is not the property for a class whose entire job is a box. */
.v3-icon {
  width: 16px; height: 16px; flex: 0 0 auto; vertical-align: -2px;
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
.v3-icon--lg { width: 26px; height: 26px; }
/* The sprite is a definitions carrier and must occupy nothing. */
.v3-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* THE SURFACE SWITCH. Instruction 167 turned this from a label into a control, and
 * the chip treatment is the owner's reference -- beside the brand, at the masthead's
 * left, on both surfaces.
 *
 * IT WAS A `<span>` READING "v3" AND IT DID NOTHING. A bare version chip beside a
 * brand reads as a build tag, which is what it was. Two controls shipped on this
 * estate this week that looked live and did nothing, and both times the tell was
 * that nothing distinguished them from static text -- so the states below are not
 * decoration, they are the difference between a tag and a place.
 *
 * `text-decoration: none` with a BORDER that reacts, rather than an underline: the
 * chip already has an edge, and underlining text inside a bordered chip reads as two
 * competing affordances. The border and the ground are what move on hover.
 */
.v3-surfacemark {
  display: inline-block; margin-left: 10px; padding: 2px 7px; border-radius: 4px;
  background: var(--gold-wash); color: var(--gold); border: 1px solid var(--gold-soft);
  font-size: 10.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  text-decoration: none; cursor: pointer; white-space: nowrap;
  transition: background-color .12s ease, border-color .12s ease;
}
.v3-surfacemark:hover { background: var(--gold); color: var(--paper); border-color: var(--gold); }
/* `:focus-visible`, not `:focus` -- a mouse click on a link would otherwise leave a
 * ring behind, which trains readers to ignore rings. Same rule the rail's links use
 * at `.rail__group a:focus-visible`. */
.v3-surfacemark:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
/* KNOWINGLY RETAINED WITH ZERO SUBJECTS. 195 removed the six rail entries that
 * carried this class, so nothing in any template uses it and a CSS audit will find it
 * orphaned. It stays because it is the marker for "this link leaves design-v3", and
 * that need has not gone away -- `v3_base.html`'s account menu still carries Settings
 * and People to design-v2 UNMARKED, and the review page's "Channel context" link does
 * the same. Whichever instruction labels those wants this rule; re-adding it later is
 * more work than the four bytes of keeping it, and deleting it would read as a ruling
 * that unmarked crossings are fine. 198 decision. */
.v3-railout { color: var(--ink-mid); font-size: 12px; }

/* --- the counters ---------------------------------------------------------- */
/* 12, so that h1-to-counters is the design's 16: the h1 contributes 4 of it. Set
   from the measured pair rather than from the design's 16 directly -- the gap is the
   sum of two margins and writing 16 here would have given 20. */
.v3-counters { margin-top: 12px; }
.v3-counterrow {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 14px;
}
.v3-counter {
  display: flex; flex-direction: column; gap: 4px;
  background: var(--paper); border: 1px solid var(--rule); border-radius: 8px;
  /* 146.5: radius 8, padding 12, NO shadow -- the design's `.kpi` is a flat
     bordered box. The shadow and the roomier padding were design-v2's card
     habit, not a drawn decision. */
  padding: 12px; box-shadow: none;
}
/* The icon chip and the label share a row, so the label is the small uppercase
   kicker the design draws rather than a heading in its own right. */
/* 151.4: the design sets each counter's glyph in a rose rounded tile, 38x38,
   radius 7. `--gold-soft` IS that rose -- rgb(247,219,228) to the byte -- so this
   is the estate's own token in LIGHT -- rgb(247,219,228) to the byte.

   IN DARK IT IS `--gold-wash`, AND THAT IS THE RIDER AGAIN. `--gold` on
   `--gold-soft` measures 4.85:1 in light and **2.39:1 in dark** -- below even the
   3.0 floor a 16px glyph has to clear, and the same 2.39 that started this whole
   line of findings on the filter chip. `--gold-tile` is the pair: `--gold-soft` in
   light, `--gold-wash` in dark, giving 4.85 and 4.58. Rejected 2.39, shipped 4.58,
   and the light tile is the design's colour exactly. */
/* THE ICON SPANS ROWS 1-2, NOT ALL THREE, and that is derived rather than chosen.
   The design puts it at y=20 in a 98px card with 12px padding. Centred over all
   three rows it lands at 30; centred over the label and the value -- 14.84 + 10 + 27
   -- it lands at 12 + (51.84 - 38) / 2 = 18.9, which is the measurement. So the
   design centres it against the label-and-value block and the sub-label hangs below
   it. Reported as a derivation because `align-self: center` over three rows does not
   give 20 and I was asked which rule does. */
/* THE ROW HEIGHTS ARE LINE-HEIGHT, AND THE CARD'S 98px COMES FROM TIGHTENING THEM
   RATHER THAN FROM SHRINKING ANY TEXT. Measured: our rows were 18.39 / 28.34 / 20
   against the design's 14.84 / 27 / 10.20, and every one of those differences is
   leading rather than font size.

   THE DESIGN'S 10.20px SUB-LABEL ROW WOULD NEED A ~10px FONT and ours is 12.5px on
   purpose -- 151 sized the population line "to be read, not to be found", and it is
   the line that stops a bare number lying. So the fonts stay and the leading closes
   the gap. */
/* 1.12 AND NOT 1. At `line-height: 1` a 27px font gives a 27px box for 30px of
   glyphs -- the content paints outside its own box. It looks right today because
   every value is an integer and digits have no descenders, which is an assumption
   about the data rather than a property of the box, and it is the shape half the
   defects found on this surface have had. 1.12 contains them. */
.v3-counter__tile {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 38px; height: 38px; border-radius: 7px;
  background: var(--gold-tile); color: var(--gold);
  grid-column: 1; grid-row: 1 / 3; align-self: center;
}
.v3-counter__label {
  font-size: 11.5px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-mid);
  grid-column: 2; grid-row: 1; line-height: 1.15;
}
.v3-counter__value {
  /* 146.5: 27px, the design's `.kpi strong`. `tabular-nums` stays -- it is not a
     size decision, and four counters whose digits shift width as the data moves
     is the defect it was added for. */
  font-size: 27px; font-weight: 800; color: var(--navy);
  font-variant-numeric: tabular-nums; letter-spacing: -.02em;
  grid-column: 2; grid-row: 2; line-height: 1.12;
}
/* A COUNTER THAT LEADS SOMEWHERE STILL READS AS A COUNTER. It inherits colour
   rather than taking link blue, because the CARD is the affordance and a 27px blue
   numeral reads as an error state.

   THE ANCHOR IS THE CARD'S FLEX COLUMN AS OF 147, because the whole card is the
   target and not just the number. `.v3-counter` keeps the border, padding and
   ground -- the layout stays on the `<li>` the theme guard already knows, and the
   anchor only carries the stacking. Moving `.v3-counter` onto the `<a>` would make
   a new class chain, which is what had to be undone on `.v3-selected__go`. */
/* 153: THE DESIGN'S CARD IS A TWO-COLUMN GRID, NOT A STACK. The icon has a column
   of its own and the label, the value and the sub-label stack beside it -- all three
   at the same x. Ours put the icon and label on one row and dropped the value and
   sub-label back to the card's left edge under the icon, which cost a whole row each
   and made the card 120px against the design's 98.

   THREE ROWS AND 38px + 1fr, from the design's own `grid-template-columns:
   38px 263.5px`. `1fr` rather than the measured 263.5 because that number is one
   card at one viewport; the column it describes is "whatever is left". */
.v3-counter__link {
  color: inherit; text-decoration: none;
  display: grid; grid-template-columns: 38px 1fr; grid-template-rows: auto auto auto;
  column-gap: 10px; row-gap: 10px; align-items: start;
}

.v3-counter__link:hover .v3-counter__value,
.v3-counter__link:focus-visible .v3-counter__value { text-decoration: underline; }
/* THE POPULATION, and it is not small print by accident -- it is quieter than the
   label and still legible, because a denominator nobody reads is a denominator that
   was not stated. It is the difference between "0 actions" meaning nothing needs
   doing and "0 actions" meaning nothing the corpus supports.

   `--ink-mid`, NOT `--ink-faint`: faint is #7a7a7a on #121212, which is 4.36:1,
   and AA wants 4.5.

   THIS LINE WAS ONCE REVERTED BY A DUPLICATE OF ITS OWN FAMILY. Instruction 143
   added a second `.v3-counterrow`/`.v3-counter*` block 120 lines below this one
   and set `--ink-faint` here. Both blocks were live, the later won, and the page
   rendered at 4.36:1 while this comment sat directly above the rule saying not to.
   The comment was read and the cascade was not. There is ONE family now, and a
   second definition of any `.v3-counter*` selector is the defect, not a variant. */
/* 12px / 16.2px / -6px, READ OFF THE DESIGN RATHER THAN INFERRED FROM ITS ROW.
   The design's sub-label row measures 10.20px and I inferred a ~10px font from it --
   wrongly. It is a 12px font on a 16.2px line pulled up 6px: 16.2 - 6 = 10.20. THE
   SHORT ROW IS A NEGATIVE MARGIN, NOT A SMALL FONT, and that is what makes the card
   98px without shrinking the line that stops a bare number lying.

   It is 12 rather than our 12.5, so it does lose half a pixel -- the only thing on
   this card that gets smaller, and it is the design's own value. */
.v3-counter__pop {
  font-size: 12px; color: var(--ink-mid);
  grid-column: 2; grid-row: 3; line-height: 16.2px; margin-top: -6px;
}

/* --- shared text ----------------------------------------------------------- */
.v3-dim { color: var(--ink-mid); font-size: 12.5px; display: inline-flex; align-items: center; gap: 5px; }
.v3-note {
  display: flex; align-items: flex-start; gap: 8px;
  font-size: 13px; color: var(--ink-mid); margin: 0 0 14px; max-width: 88ch;
}
.v3-note--table {
  background: var(--raised); border: 1px solid var(--rule); border-radius: 8px;
  padding: 10px 13px;
}
.v3-empty {
  background: var(--raised); border: 1px dashed var(--rule-firm); border-radius: 10px;
  padding: 18px 20px; color: var(--ink-mid); font-size: 14px; margin: 0;
}
.v3-crumb { margin: 10px 0 0; font-size: 13px; }
.v3-hasmoment { color: var(--settled); font-weight: 650; }

/* --- the "review these first" list ----------------------------------------- */
.v3-first { margin-top: 26px; }
.v3-videolist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.v3-videolist__item {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  background: var(--paper); border: 1px solid var(--rule); border-radius: 10px;
  padding: 11px 14px; flex-wrap: wrap;
}
.v3-videolist__meta { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
/* NOT UNDERLINED, AND NOT LINK-COLOURED. The design's queue title is plain bold
   dark text. After 148 the ROW selects rather than navigates, so an underline
   promises a navigation that no longer happens -- `Review video` is what leaves the
   page. The element stays an `<a>` because it still carries a real destination the
   row handler skips, and because "open in a new tab" on a title is worth keeping. */
.v3-videolink { display: inline-flex; align-items: center; gap: 8px; min-width: 0;
                max-width: 100%; font-weight: 650;
                text-decoration: none; color: var(--navy); }
.v3-videolink:hover .v3-videolink__title { text-decoration: underline; }
/* TRIMMED TO THE COLUMN, NEVER TO A CHARACTER COUNT. Two rules, and they take
   opposite instructions about `max-width`, so read which is which before editing
   either.

   `.v3-videolink` (above) CARRIES `max-width: 100%`. That is a percentage of the
   table cell -- a question about a BOX -- and it is what trims the title to the
   column. See below for what happened without it.

   `.v3-videolink__title` (below) HAS NO `max-width` AND MUST NOT GAIN ONE IN `ch`.
   `ch` is a character count, and a character cap in that rule would be the thing
   this comment forbids.

   AND THAT IS ABOUT TRUNCATION, NOT ABOUT `ch`. Read alone the sentence above
   condemns the unit, which would be wrong: `ch` is the CORRECT unit for a prose
   measure, because line length for readability genuinely is counted in characters --
   the 45-75 range is characters per line, not pixels. It is the wrong unit for
   clipping, because clipping is a question about a BOX.
   
   So `.v3-note { max-width: 88ch }` and `.v3-nowhere__body { max-width: 62ch }` are
   right and must not be "fixed" by anybody applying this comment's reasoning to them.
   The distinction is what is being measured, never the unit: a paragraph asks how many
   characters should sit on a line, a title asks how much room it has.
   
   `max-width: 100%` ON `.v3-videolink` IS WHAT MAKES THE HEADING ABOVE TRUE, and
   until instruction 139 it was not there, so the title was trimmed to nothing at all. `inline-flex` SHRINK-WRAPS ITS CONTENT: the box
   sized to the title rather than to the cell, so `overflow: hidden` on the title had
   nothing to hide -- the box already contained the text. Measured on the queue:

       cell 315px    .v3-videolink 774px    .v3-videolink__title 750px
       54 of 69 rows overflowed their cell, 34 of them Cyrillic
       rows where the ellipsis actually engaged: ZERO

   `min-width: 0` DOES NOT FIX IT AND IS NOT THE DEFECT. Measured, injected one at a
   time: on `.v3-videolink__title`, no change; on `.v3-videolink`, no change; on
   both, no change -- 54 rows overflowing in every case. The
   item was never the thing refusing to shrink -- `overflow: hidden` already resolves
   its `min-width: auto` to zero. What was missing was an UPPER BOUND on the
   container. One property, and 54 rows became 0 with 60 ellipses appearing.

   THE FIRST DRAFT'S `max-width: 46ch` WAS STILL WRONG, and deleting it was right --
   but the sentence that justified leaving nothing in its place was false. It read:
   "the title cell is 283.5px and 46ch at 14px Inter is 406.5px, so the column bound
   and `text-overflow` did all the work." THE CELL WAS MEASURED AND THE CONTENT BOX
   WAS NOT. They are different boxes and the whole disposition rested on their being
   one. Instruction 113 examined this rule and cleared it on that premise: it asked
   whether `ch` was the wrong unit and answered correctly, and never asked whether
   the mechanism it preferred actually ran.

   The first draft carried `max-width: 46ch` and it did not bite: the title cell is
   283.5px and 46ch at 14px Inter is 406.5px, so the CAP never engaged and `text-overflow`
   did all the work. That made it a trap rather than a bug -- the column needs to get
   ~43% wider for the cap to engage, and on that day the rule silently starts cutting
   32 of 37 Cyrillic titles against 12 of 32 Latin.
   
   WHY A CHARACTER BUDGET HITS CYRILLIC TWICE, measured over the 69 queue titles:
   Cyrillic glyphs are 14.1% wider AND Cyrillic titles are 78% longer in characters
   (median 91 against 51). The second effect is the larger one. `text-overflow`
   measures the box, so one rule is correct in every script -- and the full title
   stays reachable through the `title` attribute the markup already sets. */
.v3-videolink__title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --- the v3 dashboard, against the prototype (instruction 143) --------------

   A WORKING SURFACE RATHER THAN A SUMMARY. The design carries four counter cards,
   a filter strip, a queue table and a Selected-video pane; the page carried five
   counters and a list of links.

   NOTHING HERE FORKS AN EXISTING FAMILY. The chips are `.fchip` with design-v2's
   own `.fchip.is-on` state, the severity chips are the existing tone family
   (`.chip--material` and its five siblings), the table is `.grid`, the drawer is
   `.cdrawer`, the
   button is `.btn`. Only the LAYOUT is new, which is what the `v3-` prefix is for.
*/
/* THE COUNTERS ARE NOT HERE. `.v3-counter*` already existed and lives with the
   rest of its family above; this section adds only what the dashboard did not
   have. Adding a second copy here is what reverted the population colour once. */

/* THE FILTER STRIP WRAPS, DELIBERATELY. The design drew six chips for two
   channels; this account has nine, and a cap would hide channels a manager cannot
   then filter to. Two lines beats a hidden channel. */
/* THE STRIP ENDS WHERE THE QUEUE CARD ENDS, and it is computed from the grid's own
   tokens rather than typed. The owner asked for the channel control "inline with
   Moments only but right aligned with the queue table": inline is this row, and the
   right edge is the card's -- 1253, not the strip's natural 1617.

   NARROWED RATHER THAN POSITIONED WITHIN. Pushing the control right inside a
   full-width strip would need the pane's width in a second place; narrowing the
   strip makes `margin-left: auto` land on the card's edge BY CONSTRUCTION, so the
   two agree because they are the same subtraction and not because the numbers match
   today. Same reasoning as the panelhead that aligned the two headings in 150.

   THE CAP LIFTS WHERE THE GRID COLLAPSES. Below 1100 there is no second column, so
   there is no pane to subtract and the strip takes the full width again. */
.v3-chipstrip {
  display: flex; flex-wrap: wrap; gap: 7px; margin: 18px 0 0; align-items: center;
  max-width: calc(100% - var(--v3-pane) - var(--v3-gap));
}
@media (max-width: 1100px) { .v3-chipstrip { max-width: none; } }

/* THE FILTER CHIP, FORKED FROM `.fchip` ON PURPOSE. design-v2's `.fchip` is a
   TAB -- `border-radius: 0` with a 2px bottom border -- and it ships on thirteen
   templates. The design draws a PILL, and v3 was built to replace design-v2, so
   consistency with a surface being retired is not a reason to diverge from the
   one replacing it. `.fchip` is untouched; those thirteen pages keep their tabs.

   THE PROTOTYPE'S OWN PAIRING FAILS AA IN DARK AND THIS IS NOT IT. Its `.chip.on`
   is `color: var(--green)` on `background: var(--green-2)`, and `--green` is
   `#c0073f` -- a magenta with a misleading name, which is `--gold` here to the
   byte. Translated literally, `--gold` on `--gold-soft` measures:

       light  #c0073f on #f7dbe4   4.85:1   passes
       dark   #f61e6f on #8e0733   2.39:1   FAILS, and AA wants 4.5

   The prototype is `color-scheme: light` and never had to answer for the dark
   value. So the ground here is `--gold-wash`, which is the estate's OWN tested
   pattern -- every tone chip is `--x` text on `--x-wash` -- and measures 5.74:1
   light, 4.58:1 dark. The border carries the saturated `--gold` at 6.07:1 and
   5.03:1 against the ground, which is a border and needs 3.0. */
.v3-fchip {
  display: inline-flex; align-items: center; white-space: nowrap;
  border: 1px solid var(--rule); background: var(--raised); border-radius: 999px;
  padding: 6px 12px; font-size: 12px; font-weight: 600;
  color: var(--ink); text-decoration: none;
}
.v3-fchip:hover { border-color: var(--rule-firm); }
.v3-fchip.is-on {
  background: var(--gold-wash); color: var(--gold); border-color: var(--gold);
  font-weight: 750;
}

/* TWO COLUMNS, AND THE PANE IS THE NARROW ONE. `minmax(0, ...)` on the queue
   column because a table of long titles otherwise refuses to shrink and pushes
   the pane off the grid -- the defect instruction 139 repaired one level down. */
/* THE GRID'S OWN `gap` DOES THE SPACING INSIDE IT, and that has to be said out loud
   because a design-v2 global was doing something else. `section { margin-top: 56px }`
   at the top of this file is a bare element selector, so `<section class="v3-queue">`
   inherited 56px and `<aside class="v3-selected">` did not -- THE TWO COLUMNS OF A
   TWO-COLUMN GRID STARTED 56px APART, measured at 504 and 448.

   That is also the owner's "gaps around Current review queue": 74px above the heading
   where the design has 13, of which 56 came from a rule that has nothing to do with
   this page and 18 from the grid's own margin.

   NOT FIXED BY TUNING `.v3-queue`. The margin is inherited by whatever grid child
   happens to be a `<section>`, so a fix on one child leaves the next one wrong. It is
   neutralised for every child here, and the global is untouched -- changing
   `section {}` would move all 28 design-v2 pages, which is the mistake 943e46b made
   with the rail's breakpoint. */
/* THE PANE WIDTH AND THE GAP ARE NAMED ONCE, because two rules now depend on them:
   the grid that creates the queue column, and the chip strip that has to end where
   that column ends. A second copy of `340px` would be two numbers to keep in step,
   and the alignment the owner asked for would hold until somebody changed one. */
body.v3-surface { --v3-pane: 340px; --v3-gap: 24px; }

.v3-dashgrid {
  display: grid; grid-template-columns: minmax(0, 1fr) var(--v3-pane); gap: var(--v3-gap);
  align-items: start; margin-top: 13px;
}
/* NEUTRALISED ON THE CARDS THEMSELVES, NOT ON THE GRID'S CHILDREN, and that is the
   whole repair. `section { margin-top: 56px }` is a bare element rule near the top of
   this file, and `<section class="v3-queue">` matches it.

   148 KILLED IT WITH `.v3-dashgrid > * { margin-top: 0 }` -- scoped to DIRECT CHILDREN
   OF THE GRID. `f1b4680` wrapped the card in `.v3-queuecol` to put the filter above
   it, so the card stopped being a grid child and the 56px came back: the queue card at
   415 against the Selected-video card at 359, and a 69px gap where the design has 13.
   THE FIX WAS SCOPED TO A STRUCTURE AND THE STRUCTURE CHANGED UNDERNEATH IT.

   THIS VERSION CANNOT BREAK THAT WAY because it names no ancestor at all. `.v3-queue`
   and `.v3-selected` carry their own margin, so wrapping them, nesting them, or
   switching the container between grid, flex and block leaves it at 0. There is a
   control for exactly that in `test_the_v3_cards_do_not_inherit_the_section_margin`.

   SECOND TIME THIS RULE HAS PRODUCED A VISIBLE DEFECT AND SECOND TIME THE OWNER FOUND
   IT. If you are laying out a new v3 column: a bare `section {}` rule is waiting for
   you, and it shows up as a gap AND as two columns that will not line up. */
/* Depth-independent inside the grid too, for a block that is not one of those two. */
.v3-dashgrid section, .v3-dashgrid aside { margin-top: 0; }
@media (max-width: 1100px) { .v3-dashgrid { grid-template-columns: minmax(0, 1fr); } }

/* THE QUEUE IS A CARD, AND THAT IS WHAT MAKES THE HEADINGS LINE UP. The design
   draws both columns as cards -- 1px `--rule`, radius 8, `--paper`, padding 0 --
   and ours had one card and one bare page. So `Selected video` took its card's
   padding and `Current review queue` did not, and the two headings sat 17px apart
   while the COLUMNS were already level at y=356.

   NOT FIXED BY NUDGING A MARGIN. Two elements meeting because both take the same
   panelhead padding is a fact about the structure; two meeting because someone
   added 17px is a coincidence that drifts the next time either padding changes.

   `padding: 0` AND `overflow: hidden`: the panelhead and the body carry their own
   padding, and the table sits flush to the inner edge, which is why the design's
   column-header band reaches the card border on both sides. `overflow: hidden`
   clips the band to the radius so the border does not double against it.

   NO SHADOW. The design has none on either card, and we have removed an inherited
   one twice today already. */
.v3-queue {
  min-width: 0; padding: 0; overflow: hidden; margin-top: 0;
  border: 1px solid var(--rule); border-radius: 8px;
  background: var(--paper); box-shadow: none;
}
/* The panelhead band: title left, badge right, its own padding, and NO bottom
   margin -- inside a card, that margin was the gap between the head and the table. */
.v3-queue > .sec-head,
.v3-selected > .sec-head { margin: 0; padding: 14px 18px; align-items: center; }
.v3-queue > .v3-empty { padding: 0 18px 18px; }
.v3-selected__body { padding: 0 18px 18px; }
.v3-selected > .v3-empty { padding: 0 18px 18px; }
/* THE CARD IS MARKED STALE WHILE A FETCH IS IN FLIGHT. 202 decision 5.
   There is no loading convention on this estate and no `@keyframes` in this sheet at
   all, so an animation would be the first one and is a design decision nobody has
   made. Blanking the card would lose the reader's place; leaving the previous video's
   card unmarked would be a false statement about which row is highlighted. So: dimmed
   and non-interactive, with `aria-busy` on the same element saying it to a screen
   reader. 0.55 rather than a lighter touch because it has to read as "not this yet"
   at a glance, and `pointer-events: none` stops a click on a control describing the
   video the reader has just navigated away from. */
.v3-selected.is-stale { opacity: 0.55; pointer-events: none; }

/* THE SELECTED ROW, AND IT WAS INDISTINGUISHABLE FROM A HOVERED ONE. 201 §3(a).
   This read `background: var(--raised)` -- the SAME TOKEN as
   `table.grid tbody tr.rowlink:hover` at :875 -- so a reader whose pointer rested on
   any row could not tell which one the Selected card was describing. Measured at
   1.14:1 between the two states, which is no difference a reader can use.

   AND THE OLD RULE WAS ALREADY LOSING ON HOVER, WHICH THE IDENTICAL TOKENS HID.
   `.v3-queuerow--on > td` is (0,1,1); `table.grid tbody tr.rowlink:hover` is (0,2,3)
   and wins. So hovering the selected row has always replaced its tint with the hover
   tint -- invisible only because the two were the same colour. A distinct selected
   tint at the old specificity would therefore VANISH under the pointer, which is the
   defect being fixed, wearing the fix's clothes. Hence the explicit hover selector
   below rather than reliance on source order.

   THREE CARRIERS, WHICH IS THE RAIL'S OWN IDIOM. `.rail__group a[aria-current]` at
   :573 pairs `background: var(--raised)` with `border-left-color: var(--gold)` and its
   comment says why: "three carriers, because a colour is never the only one." This
   pairs `--gold-wash` with a gold edge, which is already the estate's selection
   pattern at :1091 -- so no new custom property, which the token guards forbid.

   `box-shadow: inset` AND NOT `border-left`: a border on a `td` shifts the cell's
   content and moves the column boundary, so the table would reflow on selection.
   An inset shadow paints inside the existing box and changes no geometry. */
table.grid tbody tr.rowlink.v3-queuerow--on > td,
table.grid tbody tr.rowlink.v3-queuerow--on:hover > td { background: var(--gold-wash); }
table.grid tbody tr.rowlink.v3-queuerow--on > td:first-child { box-shadow: inset 3px 0 0 var(--gold); }

/* RADIUS 8, NOT 12, AND THE SAME AS THE QUEUE'S. The design's two cards share a
   corner; two panels side by side with different corners reads as wrong before
   anybody can name why. Padding moves off the card and onto the panelhead and the
   body, so both cards are built the same way rather than looking the same by two
   different means. */
.v3-selected {
  border: 1px solid var(--rule); border-radius: 8px; background: var(--paper);
  padding: 0; position: sticky; top: 84px; box-shadow: none; margin-top: 0;
}
/* 152.1: THE CARD'S BADGE IS A PLAIN PILL. The design draws `span.chip` with a
   grey ground and ink text -- no tone, no icon, no colour. The ROW's severity keeps
   its shield and its brown-gold; these are two elements and 151 conflated them.
   `--ink` on `--raised`: 15.28:1 light, 16.15:1 dark. */
.chip.v3-cardsev {
  background: var(--raised); color: var(--ink); border: 1px solid var(--rule);
  border-radius: 999px; font-weight: 400; padding: 3px 10px;
}
/* The card's tiles sit under the meta line rather than in a cell. */
.v3-selected__signals { margin: 0 0 14px; }
/* 152.1: the descriptor. `p.meta` in the design -- 12px, `--ink-mid`. 6.59:1 light,
   10.18:1 dark on the card's `--paper` ground. */
.v3-selected__desc { font-size: 12px; color: var(--ink-mid); margin: 0 0 12px; }

/* 152.2: THE QUEUE COLUMN. The card is the card; this wraps it with the filter above
   it, so the dropdown sits over the "N shown" pill without spanning into the right
   column. `min-width: 0` moves here from `.v3-queue` because the grid item is now
   this wrapper -- without it a long title sets the column's minimum width and the
   whole grid grows, which is the shrink-wrapping failure 139 repaired one level in. */
.v3-queuecol { min-width: 0; display: flex; flex-direction: column; }

/* THE SAME DEFECT, UNREPORTED ONLY BECAUSE OF WHICH VIDEO WAS SELECTED (191). This
   panel takes `trim_title`'s 40-character cap with no box rule, exactly as the review
   hero did; it has never been seen wrapping because the video that happened to be
   selected had a Latin title. Fixed with the hero rather than after it. */
.v3-selected__title {
  font-size: 15.5px; font-weight: 700; color: var(--navy); margin: 10px 0 4px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* FIVE RULES FOR THE SELECTED CARD'S PRIVATE FINDING LIST WERE DELETED HERE, NOT
   ORPHANED. 201 §2 replaced that list with the review page's own summary sentence, so
   they lost their only subject; verified before removing at zero uses across
   `designv2/templates/`. No guard fires on an unused rule, so nothing would have
   reported them and a later CSS audit would have read them as new damage.

   THE CLASS NAMES ARE IN THE COMMIT MESSAGE AND DELIBERATELY NOT HERE.
   `test_no_comment_only_class_is_one_a_guard_checks` refused the first version of this
   note, and its reason is the better one: a dead component name written as a selector
   in prose is a name a class-presence guard will FIND, and be satisfied by the sentence
   explaining the rule is gone. Naming them here would have made this comment the thing
   that hides their absence. */
/* THE WRAPPER, NOT THE BUTTON -- see the template comment. `.btn--primary`'s
   flipping border on a stable ground is a registered deferral, and adding a class
   to that element would have created an unregistered second shape of it. */
.v3-selected__go { margin: 0; }
.v3-selected__go .btn { display: block; text-align: center; }
/* 146.4: the design's `.help` is crimson at 13px with a 7px radius. `.cdrawer`
   gave it ink at 12.5px and a 10px radius. The accent is the point -- it is the
   one control on the page that admits the page needs explaining, and the design
   colours it like a link rather than like body text. */
/* `About this page`, AND IT MUST NOT DEPEND ON WHAT ELSE IS ON THE ELEMENT. The
   same designed control renders three ways across v3 today: the dashboard carries
   `cdrawer cdrawer--about v3-about` and takes `.cdrawer`'s shadow and its 4px left
   border; the review page carries bare `v3-about` and gets neither; `/videos` has
   no drawer at all. One element, three treatments, and the owner saw the loudest.

   So this rule supplies the WHOLE visual treatment rather than adjusting whatever
   `.cdrawer` happened to give it. Measured against the design's `details.help`:
   `#fff` ground, a 1px `--rule` border, 7px radius, 8px/11px padding, NO shadow.
   `.cdrawer` is design-v2's and ships elsewhere -- it is not touched. */
.v3-about { position: relative; margin-top: 10px; }

/* THE BUTTON KEEPS THE DISCLOSURE'S LOOK. It was a `<summary>` and is now a
   `<button>`, but the design's `details.help` is what it is drawn from either
   way: 13px, weight 400, the accent -- a quiet link, not a button label.
   `--gold` on `--ground` measures 6.07:1 light and 5.03:1 dark. */
.v3-aboutbtn {
  cursor: pointer; font: inherit; font-size: 13px; font-weight: 400;
  color: var(--gold); background: none; border: 0; padding: 0;
}
.v3-aboutbtn:hover { color: var(--ink); }

/* THE PANEL IS `.acctmenu` AND NOTHING IS RE-SPECIFIED. Absolute, right-anchored,
   `--paper`, a 1px `--rule-firm` edge, radius 10, that shadow, `z-index: 95` --
   all of it already correct, and already correct IN DARK, which the literal
   `#fff`/`#b6b3aa` this was specified as would not have been.

   WIDTH IS THE ONE THING PROSE NEEDS AND A MENU DOES NOT, and 420 is measured,
   not estimated: it puts the text box at 390px and the first paragraph at 52
   characters a line on the dashboard, 55 on the review page -- inside the 45-75
   that prose is set to. `.acctmenu`'s own 280px is sized for menu rows and would
   run these paragraphs at about 35. It is deliberately NOT `--measure`: that
   token is 1500px, a page width, so the `max-width` the old body rule carried
   bounded nothing at all. */
.v3-aboutmenu { width: 420px; padding: 12px 14px; }
.v3-aboutmenu p {
  margin: 0 0 10px; max-width: none; font-size: 13px;
  line-height: 1.55; color: var(--ink-mid);
}
.v3-aboutmenu p:last-child { margin-bottom: 0; }
.v3-aboutmenu b { color: var(--ink); }

/* --- the review workspace -------------------------------------------------- */
.v3-summary { margin-top: 6px; }
.v3-summary__count { font-size: 15px; color: var(--ink); margin: 0 0 18px; }
.v3-where { margin-top: 22px; }
/* THE RAIL STACKS EARLIER WHEN THE QUEUE IS PRESENT, and the breakpoint has to
   know about the queue because the grid it lives in does not.

   `.video-grid` is `460px 1fr` and stacks at 1080px -- a rule written for a page
   whose work area IS the viewport. On v3 the queue takes 300px plus a 28px gap.

   THE CONTAINER, NAMED, BECAUSE THE TWO HALVES OF THIS COMMENT USED TO ASSUME
   DIFFERENT ONES:

       work  = viewport - rail(240) - .wrap padding(64) - queue(300) - .v3-split gap(28)
       aside = work - player(460) - .video-grid gap(32)

   So aside = viewport - 1124. That model predicts the deployed page to the pixel at
   every width measured -- 648, 684, 768, 769, 808 for the work track at 1280, 1316,
   1400, 1401, 1440 -- and it reproduces this comment's own history: with the 264px
   queue it puts the aside at 192px at a 1280px viewport, which is what was measured
   before this rule, alongside 26 elements overflowing (4 without the queue) and THE
   BODY SCROLLING HORIZONTALLY.

   1400px, AND THE FLOOR IS MEASURED RATHER THAN DERIVED. This line used to read
   "1400px is where 460 + 28 + 264 + 28 still leaves the rail over 600px", which
   subtracted neither the 240px rail nor the 64px `.wrap` padding that the paragraph
   above correctly subtracts, and spent the 28px gap twice while never using
   `.video-grid`'s 32px. 1400 - 780 = 620 describes no container on a page with a
   rail: by the model above, the aside at 1400 with a 264px queue was 312px, not
   620px. **The model was wrong from the start, not merely stale.**

   THE REAL CRITERION IS "COLLAPSE BEFORE THE ASIDE REACHES THE WIDTH THAT SCROLLS
   THE PAGE", and that width is now measured:

       aside floor            238px      scrolls at 237px
       crush viewport         1362px     (300px queue; 1326 with the old 264px one)
       breakpoint             1400px
       margin                 38px of viewport, 39px of aside

   AND 192px IS NOT THE FLOOR. It was the aside's width at a viewport where the body
   was OBSERVED scrolling -- a failing state, not the boundary -- so it is consistent
   with any floor above 192. Reading it as the floor overstated the margin by 46px,
   and it did so from the day this was written: with the original 264px queue the
   margin was 75px, never the 120px that figure implies. So 194 §2's queue widening
   did not halve the margin; the margin was always thinner than this comment said,
   and widening the queue took what was left.

   TO RE-MEASURE THE FLOOR YOU MUST DEFEAT THIS RULE FIRST, or you measure the
   protection instead of the failure: override `grid-template-columns: 460px 1fr` on
   the selector below, ASSERT THE OVERRIDE TOOK, then bisect the viewport until
   `documentElement.scrollWidth > clientWidth` first goes true. **Valid range is
   1101-1400 only.** Below 1101 the `@media (max-width: 1100px)` block collapses
   `.v3-split` itself and the rail becomes an off-canvas drawer, so the 240px offset
   disappears and the queue stacks -- the aside is WIDER at 900 (344px) than at 1300
   (176px), and a bisection across that boundary silently compares three layouts.

   `tests/test_the_review_aside_keeps_its_floor.py` holds the margin now. A
   constraint held only by a comment is not held, and 194 §2 spent 36px of this one
   without anything going red.

   Scoped to `.v3-work` because `.video-grid` is design-v2's too: `real_video.html`
   uses it on a page with no queue, where the 1080px rule is correct and must stay. */
@media (max-width: 1400px) {
  .v3-split:has(.v3-rqueue) .v3-work .video-grid { grid-template-columns: minmax(0, 1fr); }
  .v3-split:has(.v3-rqueue) .v3-work .video-grid > div:first-child { position: static; }
}

.v3-aside { display: flex; flex-direction: column; gap: 10px; }
.v3-facts { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.v3-facts li { display: flex; align-items: center; gap: 8px; font-size: 13.5px; color: var(--ink-mid); }

/* THE TWO NON-TIMELINE STATES. Bordered and labelled rather than blank, because an
   empty box where a timeline would go says the video was searched and found clear.
   `--nowhere--limit` is the coverage tone deliberately: CONSTRAINTS 3 forbids a limit
   on the look from reading as a verdict, and `--coverage` is the token that means
   "a limit on what we can say" rather than a severity. */
.v3-nowhere {
  display: flex; align-items: flex-start; gap: 14px; margin-top: 14px;
  background: var(--raised); border: 1px solid var(--rule); border-radius: 12px;
  padding: 18px 20px;
}
.v3-nowhere--limit {
  background: var(--coverage-wash); border-color: var(--coverage-line); color: var(--coverage);
}
.v3-nowhere__head { font-size: 14.5px; font-weight: 700; color: var(--ink); margin: 0 0 5px; }
.v3-nowhere__body { font-size: 13.5px; line-height: 1.6; color: var(--ink-mid); margin: 0; max-width: 62ch; }
.v3-nowhere--limit .v3-nowhere__head { color: var(--coverage); }

/* --- the findings list ----------------------------------------------------- */
.v3-findings { margin-top: 26px; }
/* THE SEVERITY WORD SET THE ROW'S INDENT HERE TOO. Each row was its own flex line,
   so a title began at (pill width + 12px) and the pill is sized by the WORD. The
   chips are wider on this surface than on the dashboard because this one renders
   the mark glyph inside the chip: `Low` 59.48px against 46.09px. Measured across
   all 69 review pages, the titles stepped 21.96px in and out; the three pages that
   did not are the same three all-Low videos, which is one value, not a case.

   SUBGRID, AND IT IS THE FIRST IN THIS ESTATE -- zero occurrences across all three
   source stylesheets before this. That is a decision, not a reach, and the
   condition that forces it is specific: THE ROW HERE IS A `<button>` THAT IS A
   VISIBLE CARD. The dashboard's why-list solved the same defect with the `.kv`
   idiom, which spends the row into the parent grid with `display: contents` -- and
   `display: contents` on THIS row deletes the card, the padding, the hover, the
   focus ring and the click target along with the box.

   AND IT DELETES THEM INVISIBLY TO THE OBVIOUS CHECK. Measured with
   `display: contents` on the row: `getBoundingClientRect()` returns [0, 0] and 0 of
   4 rows keep a box, while `getComputedStyle` still reports border 1px, radius 9px,
   padding 13px and a white background. A check that read the computed style would
   have passed a page with no rows on it. Assert `getClientRects().length`, and
   click the row -- `data-open-live` is the drawer's hook and a boxless button
   cannot be clicked.

   So the `<li>` takes `display: contents` -- it has no rule and draws nothing -- and
   the button stays a box that borrows the list's three tracks. */
/* A STANDING EXPLANATION, ONE CLICK AWAY (190 §1). Small and quiet: it is a rule
   for reading the thing below it, not a finding, and it must not read as one. */
/* A CLOSED DRAWER FOR HEAVY CONTENT (192 §1). Distinct from `.v3-secnote`, which
   hides a standing EXPLANATION: this hides the thing itself, so its summary reads as
   a control rather than as a footnote. */
.v3-drawer > summary {
  font-size: 13px; font-weight: 650; color: var(--ink); cursor: pointer;
  display: inline-block; padding: 7px 13px; margin-top: 4px;
  border: 1px solid var(--rule); border-radius: 8px; background: var(--paper);
}
.v3-drawer > summary:hover { background: var(--raised); border-color: var(--rule-firm); }
.v3-drawer > summary:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
.v3-drawer[open] > summary { margin-bottom: 14px; }

.v3-secnote > summary {
  font-size: 12.5px; color: var(--ink-mid); cursor: pointer; margin: 2px 0 0;
}
.v3-secnote > summary:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
.v3-secnote[open] > summary { margin-bottom: 6px; }

.v3-findinglist {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: max-content 1fr max-content; gap: 7px 0;
}
/* THE THIRD TRACK FLIPS THE SCOPE LABEL FROM RIGHT-ALIGNED TO LEFT-ALIGNED, and
   that is a visible change nobody asked for. Under flex the labels were flush to
   the row's right edge and ragged on the left; in a `max-content` track they all
   start at the same x and are ragged on the right. Measured on a mixed page:

       before   ink left spread 55.83   ink right spread 0
       after    ink left spread 0       ink right spread 55.83

   THE RAGGEDNESS MOVES, IT DOES NOT GO AWAY -- worth stating because the obvious
   reading of "the labels now line up" is that 55.83 became 0, and it did not. 29
   of the 69 review pages carried a non-zero spread and all 29 now read 0 on the
   left. Reported to the owner rather than corrected: `justify-self: end` on the
   label would restore the old right edge and is one line, if they want it. */
/* THE `<li>` IS THE CARD NOW (196 §2). It was `display: contents` so the button
   could borrow the list's tracks and draw the box itself; the signals have moved OUT
   of the button -- `tabindex="0"` inside a `<button>` nests focusable content in an
   interactive element -- so the box has to belong to something that contains both.
   The li takes the tracks and the box; the button keeps the subgrid and loses the
   chrome. Alignment is unchanged because the tracks are still the list's. */
.v3-findinglist > li {
  display: grid; grid-template-columns: subgrid; grid-column: span 3;
  align-items: center; gap: 4px 12px;
  background: var(--paper); border: 1px solid var(--rule); border-radius: 9px;
  padding: 10px 13px;
}
.v3-findinglist > li:hover { background: var(--raised); border-color: var(--rule-firm); }
.v3-findinglist > li:has(.v3-findingrow:focus-visible) { outline: 3px solid var(--gold); outline-offset: 2px; }

/* `gap: 7px 0` ABOVE, AND THE 12px BELOW IS WHAT SEPARATES CHIP FROM TITLE.
   A subgrid inherits the parent's TRACKS and supplies its own GUTTERS, so the two
   gaps do not compose the way the shorthand suggests. Measured, both directions:

       list gap 7px 0  -> 7px 12px    IDENTICAL -- the column gap does nothing
       button gap 12px -> 0           title moves 671.44 -> 659.44, i.e. -12px

   So the parent's column gap is IGNORED here, not added twice, and the `0` is the
   honest value rather than a load-bearing one -- writing 12 would work and would
   read as though it were doing the spacing that the button is actually doing. The
   row gap belongs to the list because the list is what stacks the rows, and it is
   the only half of this shorthand that has any effect. */
.v3-findingrow {
  display: grid; grid-template-columns: subgrid; grid-column: span 3;
  align-items: center; gap: 4px 12px; width: 100%; text-align: left;
  /* THE BOX MOVED TO THE `<li>` above; this keeps the tracks and the pointer. */
  background: none; border: 0; padding: 0; cursor: pointer; font: inherit; color: var(--ink);
}

/* `min-width: 0` STAYS AND THE FLEX SIZING GOES. The title is the `1fr` track's
   item now, so the growth it used to ask for is the track's job; what it still
   needs is permission to go under its own min-content, or one long unbroken word
   sets the track minimum and pushes the row past the panel. */
.v3-findingrow__title { font-weight: 650; min-width: 0; grid-column: 2; }

/* WITHOUT THIS EVERY PILL TAKES THE WIDEST PILL'S WIDTH. A grid item defaults to
   `justify-self: stretch`, which measured [81.44, 81.44, 81.44, 81.44] against the
   [59.48, 81.44, 59.48, 59.48] these actually are -- names aligned either way,
   which is what makes it the failure that looks like success. */
.v3-findingrow > .chip { justify-self: start; }

/* THE THIRD TRACK AND ITS SCOPE LABEL ARE BOTH GONE (190 §3), and with them the
   `justify-self: end` that argued about which edge of that column was ragged. The
   label was "Across the examined sample" on all eleven rows -- a property of the
   RUN, not of the finding -- and it is now one icon carrying its own words. The
   alignment lesson the tracks were protecting survives: chip and title still share
   the list's tracks through a nested subgrid, so titles line up across rows.

   `.v3-findingrow__main` IS A SUBGRID INSIDE A SUBGRID. The row borrows the list's
   two tracks; `__main` borrows them again so the chip stays `max-content` and the
   title `1fr`, and the signal row spans both underneath. */
.v3-findingrow__main {
  display: grid; grid-template-columns: subgrid; grid-column: span 3;
  align-items: center; gap: 12px;
}
.v3-findingrow__chev { justify-self: end; color: var(--ink-mid); font-size: 17px; line-height: 1; }

/* THE SIGNAL ROW. Icons, each carrying its words in a `.vh` span -- an icon with no
   name is a deletion, not a reduction.

   THIS COMMENT DESCRIBED THE PRE-196 SHAPE UNTIL 199 CORRECTED IT. It said the span
   sits "inside the button", that `title` gives the tooltip, and that the words reach a
   keyboard reader through the button's own name. All three were true before `f6f50d2`
   and none after: 196 removed every `title` and moved `.v3-fsigs` OUT of the button,
   because `tabindex="0"` on a span inside a `<button>` nests focusable content in an
   interactive element. The tip is `data-tip`, painted on `:hover` and `:focus`.

   THE INVALID-HTML RULE THE OLD TEXT STATED IS STILL TRUE and is the reason the
   signals are a sibling of the button rather than a child of it. */
/* THE SIGNALS START UNDER THE TITLE, NOT UNDER THE CHIP. Spanning from track 1
   put a lone 15px glyph at the row's left edge, under the severity pill, reading as
   a second severity rather than as the title's own metadata. `2 / 4` hangs them off
   the title they describe. */
.v3-fsigs { grid-column: 2 / 4; display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.v3-fsig {
  display: inline-flex; align-items: center; color: var(--ink-mid); cursor: help;
  /* 194 §6 gave these the five tints, so the base needs the box the tint paints
     into -- a transparent border keeps every state the same size whether it is
     tinted or not, and the radius is the prototype's 6 at finding-row scale. */
  border: 1px solid transparent; border-radius: 6px; padding: 2px 5px;
}
.v3-fsig .v3-icon { width: 15px; height: 15px; }
/* `.v3-findingrow:hover .v3-fsig` WAS HERE AND COULD NOT MATCH (199 inspection 6).
   After 196 `.v3-fsigs` is a SIBLING of `.v3-findingrow`, so a descendant selector
   from the button reaches nothing. Removed rather than left: the hover affordance it
   provided now belongs to `.v3-findinglist > li:hover`, which is the element that
   draws the card. */

/* --- the decision ---------------------------------------------------------- */
.v3-decide { margin-top: 28px; }
.v3-quickdecide {
  display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap;
  background: var(--paper); border: 1px solid var(--rule); border-radius: 12px;
  padding: 15px 18px; margin-bottom: 14px;
}
.v3-quickdecide__label { font-size: 12.5px; font-weight: 650; color: var(--ink); display: block; }
/* THE v3 CHROME IS A POPUP OVER THE PAGE, NOT A TAKEOVER OF IT (188).

   `.viewer` is `position: fixed; inset: 0` with an opaque `var(--ground)`, which
   on v3 replaced the whole viewport -- measured 2007x1096 at z-index 120, so the
   review page a reader had just been reading vanished behind it.

   SCOPED UNDER `.v3-drawerwrap`, WHICH IS WHAT THIS FILE'S OWN WRAPPER ASKED FOR:
   "a modifier class on this wrapper, never a fork of the body".
   `_live_viewer.html` IS INCLUDED BY `real_video.html` TOO, so a bare `.viewer`
   rule would move design-v2's concern viewer as well.

   THE SCRIM IS A BOX-SHADOW SPREAD, NOT A BACKGROUND ON THE WRAPPER, and that is
   not a flourish. The wrapper is `display: contents`, so painting a scrim on it
   needs a state selector -- and `.v3-drawerwrap:has(.viewer:not([hidden]))` is
   read by `test_no_stable_surface_carries_flipping_ink` as a background on
   `.viewer` ITSELF, because its parser takes the class out of the `:has()` rather
   than the subject in front of it. That made a stable `rgb(0 0 0 / .58)` look like
   the surface under every `.vtitle`, `.chip` and `.eyebrow` in BOTH viewers,
   including `_viewer.html`, which is never inside this wrapper. The guard was
   right about what it could see, so the fix is to stop giving it a background to
   misread rather than to add an exemption to it.

   A shadow is not a surface, `.viewer` keeps its own themed `var(--ground)`, and
   the closed state needs no selector at all: `.viewer[hidden]` is `display: none`,
   so the shadow goes with it.

   A DARK SCRIM IN BOTH THEMES, DELIBERATELY. A scrim built from `--ground` follows
   the theme, and in dark that is a LIGHT wash over a dark page -- a flash rather
   than a dimming. Measured behind the card: mean grey 242.4 -> 101.7 with 98
   distinct greys still present, so the page reads as dimmed and not covered. */
.v3-drawerwrap .viewer {
  position: fixed;
  inset: 0;
  margin: auto;
  width: min(940px, calc(100vw - 48px));
  height: max-content;
  max-height: min(86vh, 940px);
  border: 1px solid var(--rule);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 0 0 100vmax rgb(0 0 0 / .58), 0 24px 64px rgb(0 0 0 / .34);
}
/* AND THE SCRIM HAS TO CATCH CLICKS, WHICH A SHADOW DOES NOT.

   `box-shadow` paints but does not hit-test, so with the scrim as a shadow alone
   `elementFromPoint` outside the card returned `rail__scroll` -- the page behind a
   dialog that declares `aria-modal="true"` was live and clickable. The takeover
   this replaces was opaque and full-bleed, so it blocked by accident; the popup
   has to block on purpose.

   A PSEUDO-ELEMENT WITH NO BACKGROUND. It is hit-testable while painting nothing,
   so it restores the block without reintroducing the stable surface the theme
   guard reads off a wrapper background. `z-index: -1` puts it behind the card's
   own background inside `.viewer`'s stacking context, so it covers the page and
   not the card, and it disappears with `.viewer[hidden]`. */
.v3-drawerwrap .viewer::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
}
/* The bar was a page-wide header; inside a 940px card its `.wrap` measure and the
   scroll region's page-sized padding are both too generous. */
.v3-drawerwrap .viewer .wrap { padding: 0 26px; }
.v3-drawerwrap .viewer .viewer-scroll { padding: 26px 0 34px; }
.v3-drawerwrap .viewer .vhead { grid-template-columns: 1fr; gap: 22px; }

.v3-drawerwrap { display: contents; }

/* --- the review workspace's two columns, instruction 142 ------------------- *
 *
 * THE DESIGN'S `div.split`: the queue beside the work. The prototype's own
 * sentence is the reason -- the pager gives the reviewer arrows, the design gives
 * them the map -- and a reviewer walking 69 videos needs to see where they are in
 * the set, not only that they can move.
 *
 * THE QUEUE COLUMN IS FIXED AND THE WORK COLUMN IS `minmax(0, 1fr)`. Without the
 * `0` a long video title in the work column sets the grid's minimum content width
 * and the whole page grows a horizontal scrollbar -- the same shrink-wrapping
 * failure 139 measured on the queue's titles, one layer out. `1fr` alone means
 * `minmax(auto, 1fr)`, and `auto` is the content.
 *
 * ONE COLUMN BELOW 1100px, and the queue goes FIRST there rather than being
 * dropped: on a narrow screen the map is still the thing that orients a reader,
 * and it is capped at eight rows so it costs them one scroll rather than a page.
 */
.v3-split {
  display: grid;
  /* 300px, NOT 264, SO THE PAGER FITS ON ONE LINE (194 §2). 192 put Previous/Next at
     the panel's foot and they wrapped: measured at the widest label the pager can
     produce -- "Video 71 of 71", not the "Video 8 of 71" a casual look gives you --
     the three children need 254px and a 264px panel offers 236px inside its padding.
     300px is also the prototype's own queue width, so this closes a gap rather than
     opening one.

     THIS NARROWS THE WORK COLUMN, which holds 190's above-the-fold guarantee. Measured
     after: the first finding row moved 671 -> see the commit; the ceiling is 726. */
  grid-template-columns: 300px minmax(0, 1fr);
  gap: 28px;
  align-items: start;
  margin-top: 18px;
}
.v3-work { min-width: 0; grid-column: 2; }

/* THE WORK COLUMN IS PLACED, AND THE GUTTER COLLAPSE IS AN ENHANCEMENT ON TOP.
 *
 * Opened WITHOUT a `set`, this page rendered its whole contents in the 264px
 * track with 1144px of empty grid beside it: "What was examined" one word per
 * line, and `.video-grid`'s 460px column overflowing a 264px parent. Measured
 * 2026-09-01, direct entry against `?set=attention`. THE TRACK IS 300px NOW -- 194
 * §2 widened it the same evening -- and these two figures are left as the dated
 * measurement they are rather than restated, because overwriting a recorded
 * measurement to keep a comment tidy loses the reader's ability to tell a stale
 * claim from a historical one.
 *
 * `v3_review.html` renders `aside.v3-rqueue` only when the reader arrives
 * carrying a set, and THAT CONDITION IS CORRECT -- 32 queries without the queue
 * against 287 with, and a panel listing a set the reader is not in would be a map
 * of somewhere else. With one child, grid put `.v3-work` in track 1.
 *
 * (a) `grid-column` ON THE TWO CHILDREN IS THE GUARANTEE. Queue 1, work 2. With
 *     the queue absent the work column is correct on its own, and only an empty
 *     300px gutter is left -- the first track's width, so it follows the queue.
 *     No `:has()` involved, so it holds everywhere.
 *
 * (b) THE `:has()` RULE BELOW COLLAPSES THAT GUTTER, and it reads the DOM rather
 *     than re-deciding the template's condition -- so it cannot drift from it.
 *     A modifier class set by `{% if queue_excludes_this or queue_rows %}` would
 *     have been the same judgement written twice in two places, which is the
 *     defect 179 spent an evening removing.
 *
 * (b) IS AN ENHANCEMENT AND NOT A DEPENDENCY: where `:has()` is unsupported the
 * page is still laid out correctly by (a), with a blank gutter as the only cost.
 */
.v3-split:not(:has(.v3-rqueue)) { grid-template-columns: minmax(0, 1fr); }
/* AND THE PLACEMENT HAS TO BE RELEASED WITH IT, or (a) defeats (b). Measured by
 * injecting both into the live page: collapsing the tracks alone left
 * `338px 1070px` -- one explicit track, and `grid-column: 2` creating an IMPLICIT
 * second one for the work to sit in. The page was correct and the gutter was
 * still there, 338px instead of 264px. (Both figures are that measurement's, taken
 * while the first track was 264px; it is 300px since 194 §2. Left as measured.) */
.v3-split:not(:has(.v3-rqueue)) > .v3-work { grid-column: auto; }

@media (max-width: 1100px) {
  .v3-split { grid-template-columns: minmax(0, 1fr); gap: 20px; }
  /* PLACEMENT RESET, OR (a) WOULD BREAK THE NARROW LAYOUT IT IS MEANT TO PROTECT.
   * This block is ONE column, and `grid-column: 2` in a one-column explicit grid
   * creates an IMPLICIT second track -- so the work would sit beside the queue
   * again at exactly the widths where the comment above says the queue must come
   * first and the layout must stack. */
  .v3-rqueue,
  .v3-work { grid-column: auto; }
}

/* THE QUEUE PANEL -- `rqueue`, the REVIEW workspace's, and deliberately not
 * `.v3-queue`, which is the DASHBOARD's block. One name for a dashboard region
 * and a narrow capped side panel is one rule two pages pull in opposite
 * directions; the prefix keeps them apart. Do not merge them. */
.v3-rqueue {
  /* Track 1. See (a) at `.v3-split` above -- placed here rather than in a second
   * `.v3-rqueue` rule, because this sheet has already been bitten by a duplicate
   * selector 180 lines from its twin. 192 added the row unit below for the same
   * reason, and the guard caught the second rule before it shipped. */
  grid-column: 1;
  /* ONE QUEUE ROW: a one-line title plus a one-line meta plus its padding. The
     window below is counted in THESE, not in pixels, so ten rows stay ten rows
     whatever a title does -- which 191 is what makes true, since the title is now
     `nowrap` and a row's height no longer depends on its text. */
  --v3-rqrow: 3.55rem;
  border: 1px solid var(--rule); border-radius: 12px; background: var(--paper);
  padding: 14px 4px 10px; position: sticky; top: 84px;
}
.v3-rqueue__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 0 14px 10px; border-bottom: 1px solid var(--rule);
}
.v3-rqueue__head .sec { margin: 0; }
.v3-rqueue__list {
  list-style: none; margin: 0; padding: 0;
  /* THE LIST HOLDS ALL OF THEM AND SHOWS TEN (192 §3). It used to be capped at eight
     IN THE VIEW and the comment here said so; the cap is now a window, so the panel
     is still the MAP its original comment argued for -- an uncapped 71-row aside above
     the work was the thing that comment refused -- while every row is reachable
     without leaving the page. Raising it cost 0 queries, measured: 307 before and
     307 after, because `walked.rows` was already in memory for the pager. */
  max-height: calc(var(--v3-rqrow) * 10);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.v3-rqueue__item + .v3-rqueue__item { border-top: 1px solid var(--rule); }
.v3-rqueue__item > a {
  display: block; padding: 10px 14px; text-decoration: none;
  border-left: 2px solid transparent;
}
.v3-rqueue__item > a:hover { background: var(--raised); }
/* The reader's own position, marked the way the queue's tabs mark theirs -- an
 * accent edge rather than a filled row, so the panel stays one quiet column.
 * `aria-current` carries it to a reader this cannot reach. */
.v3-rqueue__item.is-on > a {
  border-left-color: var(--gold); background: var(--raised);
}
/* TWO LINES, AND THE TITLE IS ALLOWED TO WRAP. A queue row's title is clipped on
 * the queue page because the column is shared with six others; here the panel has
 * nothing to align to, and a truncated title in the MAP is the one place a
 * reviewer cannot recover the rest by looking left.
 *
 * THE THREE-LINE CLAMP THAT USED TO BE HERE IS GONE, AND IT NEVER FIRED ONCE.
 *
 * It read `display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient:
 * vertical; overflow: hidden`, above a comment claiming it stopped a long title
 * pushing the eighth row off the panel. It could not: the text arrives PRE-CUT by
 * `trim_title`, so the longest thing this box ever receives is 40 characters.
 * Measured in the live font at 224px -- 8 rows, two on one line and six on two,
 * NOTHING at three. A clamp of 3 would need a cap near 75 characters to do any
 * work at all.
 *
 * Dead CSS that reads as a working guard is worse than no guard: the next person
 * sees a clamp, assumes overflow is handled, and does not measure. Removed rather
 * than left with an apology.
 *
 * SO WHAT BOUNDS THE ROW HEIGHT IS THE CAP, NOT THIS RULE, and that coupling is
 * the thing to know: `TITLE_CHARS` in `designv2_extras.py` is 40, which is at most
 * two lines here. If that cap rises or goes, re-measure this box before assuming
 * eight rows still fit -- and if a clamp is the answer then, it should be added
 * with the measurement that justifies it. */
/* The pager sits under the list it walks, inside the panel's own border. */
/* THE ANALYSIS BAR (193 §1). It is a `.v3-drawer` first -- the summary chrome is
   shared with the frames drawer 192 added -- and this modifier is what makes it read
   as the page's own section rather than as a control inside one: full width, its own
   top margin, and a summary that wraps as a sentence rather than sitting as a pill. */
.v3-analysis { display: block; margin-top: 8px; }
.v3-analysis > summary { display: block; white-space: normal; line-height: 1.45; }

/* THE TIMELINE CARD'S HEAD (193 §2). Label and sub-line left, legend right, on one
   line where there is room -- the prototype's `div.timeline-head` is a flex row and
   its legend is 199x15 at the right edge of a 620px card. */
.v3-timeline-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin-bottom: 14px;
}
.v3-timeline-head .sec { margin: 0; }
.v3-timeline-sub { font-size: 12.5px; color: var(--ink-mid); margin: 3px 0 0; }
.v3-timeline-legend {
  list-style: none; margin: 0; padding: 0; display: flex; gap: 12px; flex-wrap: wrap;
  font-size: 11.5px; color: var(--ink-mid);
}
.v3-timeline-legend li { display: inline-flex; align-items: center; gap: 5px; }
/* THE SWATCH TAKES THE MARKER'S OWN CLASS, so the key cannot drift from the track --
   and it is SHAPED LIKE A MARKER for the same reason. `.marker--attention` is
   `linear-gradient(#EDF0F6 0 7px, #E5B45F 7px 100%)`, calibrated for a tall thin bar:
   in a 10x10 square those first 7px are most of the box and the tone read as a pale
   chip with a line under it, which is not what the track draws. At the marker's own
   proportions the gradient lands where it was designed to. `.marker--quiet` is a 6px
   repeating gradient with the same dependency. */
.v3-timeline-swatch { width: 7px; height: 16px; border-radius: 2px; display: inline-block; }

/* EVERY MOMENT IS LISTED, AND THE SELECTED ONE IS MARKED (194 §3, reversing 193 §2d).
   193 hid all but the selection because the prototype draws one row; the owner has
   seen that and asked for the full list back. So selection stops being a filter and
   becomes an emphasis: `[data-current]` still follows the marker clicks, and it now
   says WHICH row the reader just chose rather than which rows they may not see. */
.v3-mkey--all > li[data-current] > .mkey-item {
  border-left-width: 5px;
  background: var(--raised);
}

.v3-rqueue__pager { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--rule); }

.v3-rqueue__title {
  display: block;
  font-size: 13px; font-weight: 650; color: var(--ink); line-height: 1.35;
  /* ONE LINE, BY THE COLUMN RATHER THAN BY A CHARACTER COUNT (190 §4). `trim_title`
     already cuts these to 40 and all eight STILL wrapped to two lines in a 224px
     column, so the owner's "~50 with an ellipsis" would wrap harder, not less. A
     character count cannot settle it: 40 Cyrillic characters and 40 Latin ones are
     different widths, so any count that fits one wraps the other. Truncating at the
     column edge is the only trim that yields one line for every title at every
     width, and the full text stays in the row's `title` attribute.

     `trim_title` ITSELF IS LEFT ALONE -- it is a shared filter and design-v2 uses
     it, so a change there would move pages this instruction does not touch. */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.v3-rqueue__item.is-on .v3-rqueue__title { color: var(--navy); }
.v3-rqueue__meta {
  display: block; margin-top: 3px;
  font-size: 11.5px; color: var(--ink-mid); line-height: 1.4;
}
/* THE CAP, SAID. A list that stops without saying so reads as the whole set. */
.v3-rqueue__cap {
  margin: 10px 0 0; padding: 10px 14px 0; border-top: 1px solid var(--rule);
  font-size: 11.5px; color: var(--ink-mid); line-height: 1.45;
}

/* THE HEADER'S FORMAT AND DURATION, above the title the way the design leads. */
.v3-formatline { margin: 0 0 6px; }

/* `Channel context`. It leaves design-v3 for design-v2's channel page, which is
 * the only page in the product holding a channel profile -- underlined, because a
 * link that leaves the surface should not look like a link that stays on it. */
.v3-ctxlink { color: var(--navy); text-decoration: underline; text-underline-offset: 2px; }
.v3-ctxlink:hover { color: var(--gold); }

/* THE DESCRIPTOR LINE -- the video's character, from the adapter's axis readings.
 * `measure` so six axes wrap as prose rather than one long rule across the page. */
.v3-descriptor {
  margin: 6px 0 0; font-size: 13px; color: var(--ink-mid);
  max-width: var(--measure); line-height: 1.5;
}

/* `Analysis signals`. NO HOVER STATE, DELIBERATELY -- the label is text beside the
 * icon, because a tooltip reaches neither a keyboard nor a touch screen. There is
 * nothing to reveal, so there is no revealed state to style. */
.v3-signals {
  list-style: none; margin: 12px 0 0; padding: 0;
  display: flex; gap: 8px; flex-wrap: wrap;
}
.v3-signal {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; border-radius: 999px;
  background: var(--raised); border: 1px solid var(--rule);
  font-size: 11.5px; font-weight: 600; color: var(--ink-mid);
}
.v3-signal .v3-icon { flex: 0 0 auto; }

/* THE `<summary>` AND `__body` RULES ARE GONE, not orphaned. Both v3 About drawers
   are now button-and-popover, no `<details>` remains on any v3 page, and a rule
   that can never match is a rule the next reader has to disprove. The treatment
   they carried moved to `.v3-aboutbtn` and `.v3-aboutmenu` above. */

/* --- the review workspace's hero and foot, instruction 145 ----------------- *
 *
 * THE PAGE IS NAMED FOR THE JOB AND THE ROW IS THE HERO. The `h1` is the fixed
 * label `Review video`; this block carries the chip, the video title as an `h2`,
 * and the channel. That is the design's `div.videohero`, and it is why its top
 * reads clean -- eight stacked lines became three.
 */
.v3-hero { margin: 0 0 4px; }
/* Not `.display`: that is the h1's size and this sits under it. Large enough to be
 * the row's name, small enough not to compete with the page's. */
.v3-hero__title {
  /* 23px IS THE DESIGN'S OWN VALUE, not a number picked to sit under the new h1.
     The prototype's review hero is `strong` at 23px/700, and its scale is 30 / 23
     / 17 / 14 on all nine screens. 25px was chosen against `.display` at 40; with
     the h1 at 30 the design's own step is what fits. Only the SIZE is taken --
     the prototype's hero is white on a dark thumbnail tile and this one is not. */
  /* 600 AND NOT THE PROTOTYPE'S 700, WHICH IS A DELIBERATE DIVERGENCE (190 §4).
     The owner asked for a lighter weight on video titles "so urgent signals outrank
     metadata", and the prototype cannot settle it: its hero is `strong` at 700
     because it is white on a dark thumbnail tile, where the weight is carrying
     CONTRAST rather than rank. This one sits on the page ground beside severity
     chips that are themselves 700, so at 700 the video's NAME competed with the
     thing the page is warning about. Only the weight moves; the size stays the
     design's 23px. */
  font-size: 23px; font-weight: 600; line-height: 1.22; letter-spacing: -0.01em;
  color: var(--navy); margin: 4px 0 6px; max-width: 32ch;
  /* THE BOX RULE THE CAP WAS ALWAYS RELYING ON (191). `trim_title` is an editorial
     cap and its docstring forbids replacing it with `text-overflow` -- rightly: it
     decides how much title a row should CARRY. This decides how much room the title
     HAS, and both have to be true of the same string.

     THE 32ch ABOVE WAS CALIBRATED AGAINST A 30-CHARACTER CAP AND THE CAP IS NOW 40.
     `7446081` (143/145) wrote it with `TITLE_CHARS = 30`, sizing the box so a
     Cyrillic title -- about 14% wider per character -- still had room; `b6a32e3`
     (148) raised the cap to 40 and did not resize the box. So the hero has been
     overrunning by a third of a title ever since, and it only showed on Cyrillic
     because Latin at 40 still fits. The comment above is true of the day it was
     written, which is why it reads as though this cannot happen.

     ONE LINE, NOT A CLAMP. A two-line clamp gives Cyrillic more room and pushes the
     findings down, and 190 §1 exists to hold the first finding row above the fold.
     A Cyrillic hero therefore shows FEWER characters than the 40-cap allows, because
     fewer fit -- the cap is a maximum, not a target -- and the full title is in
     `title=`. */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* `32ch` AND NOT A PIXEL WIDTH, because the content is a 30-character cap: the box
 * is sized in the same unit the trim is, so a Cyrillic title at 30 characters --
 * about 14% wider than 30 Latin -- still has room rather than wrapping where a
 * Latin one would not. This is the one place `ch` is the RIGHT unit on this page,
 * for exactly the reason it is the wrong one for a box holding untrimmed text. */

/* `Analysis signals` as its own section rather than a chip in the header. OURS,
 * not the design's -- the prototype has no such row; see the template. */
/* THE DETAIL REACHES A KEYBOARD, NOT ONLY A POINTER (194 §5). Painted from
   `data-tip` on hover AND on focus, which is the half a native `title` cannot do.
   The `.vh` span carries the same words as the accessible name, so the figure is
   available to a pointer, a keyboard and a screen reader rather than to one of them. */
.v3-signal[data-tip], .v3-fsig[data-tip] { position: relative; }
/* THE TOOLTIP HAS NO CONTENT UNTIL IT IS ASKED FOR, and that is a layout fix rather
   than a nicety. `position: absolute` inside a `position: relative` parent still
   counts toward the parent's SCROLL area, so a nowrap tooltip 82px wide inside a 36px
   signal reported the signal as overflowing at every width and in both themes --
   opacity 0 hides a box, it does not remove it. Empty content is no box at all. */
.v3-signal[data-tip]::after,
.v3-fsig[data-tip]::after {
  content: "";
  position: absolute; bottom: calc(100% + 7px); left: 50%; transform: translateX(-50%);
  white-space: nowrap; font-size: 12px; line-height: 1.3; padding: 5px 9px;
  background: var(--band); color: var(--band-ink); border-radius: 6px;
  opacity: 0; pointer-events: none; transition: opacity .12s;
  z-index: 20;
}
/* `:focus` AND NOT ONLY `:focus-visible`. The ring may reasonably wait for the
   keyboard heuristic; the DETAIL must not, because it is the only place the count
   exists for a sighted reader and `:focus-visible` does not match a programmatic
   focus at all. */
.v3-signal[data-tip]:hover::after,
.v3-signal[data-tip]:focus::after,
.v3-fsig[data-tip]:hover::after,
.v3-fsig[data-tip]:focus::after { content: attr(data-tip); opacity: 1; }
.v3-signal[data-tip]:focus-visible,
.v3-fsig[data-tip]:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }

/* THE HIDDEN NAME IS PINNED TO THE ORIGIN. `.vh` is `position: absolute`, and giving
   the signal `position: relative` for the tooltip made it the containing block, so an
   unpinned span would sit wherever static flow left it. It is 1x1 with `overflow:
   hidden` so it was not the overflow -- that was the tooltip above -- but it belongs
   at the origin now that this element is its containing block. */
.v3-signal[data-tip] > .vh,
.v3-fsig[data-tip] > .vh { left: 0; top: 0; }

/* THE FIVE SIGNAL TINTS (194 §6). Live rendered 21 icon elements in one background
   and one ink, so every signal looked the same: "source cited" and "no source-backed
   action generated" were the same grey square. 190 and 192 replaced repeated
   sentences with icons, and that trade only pays if the icon carries the state --
   until this rule the words had gone and nothing had replaced them.

   TOKENS, NOT LITERALS PER THEME, AND THE GUARD IS WHY. Written as hardcoded `rgb()`
   inside a dark block these read to `test_no_stable_surface_carries_flipping_ink` as
   STABLE surfaces -- and the base `.v3-signal` border is `var(--rule)` and the focus
   ring `var(--gold)`, both of which flip. A fixed surface under flipping ink is the
   defect that guard exists for, and it was right: mixing the two means one of them
   moves when the theme does and the other does not. Declared here, redefined for
   dark, so the whole family flips together.

   THE TINT IS DECORATION AND THE GLYPH CARRIES THE MEANING, which is why each ink is
   chosen against its own tint rather than against the page. Measured in both themes,
   every pair is 6.59:1 or better.

   DARK IS NOT THE LIGHT PALETTE DIMMED. The prototype's hexes are light-only, and a
   pale wash on a dark page is the flash 188 avoided in its scrim -- so dark takes a
   deep hue of the same family and lifts the ink instead. */
:root {
  --sig-good-bg: rgb(233,246,238); --sig-good-line: rgb(185,221,199); --sig-good-ink: rgb(21,88,53);
  --sig-source-bg: rgb(239,240,255); --sig-source-line: rgb(201,203,239); --sig-source-ink: rgb(52,56,140);
  --sig-warn-bg: rgb(255,247,231); --sig-warn-line: rgb(232,199,132); --sig-warn-ink: rgb(122,79,10);
  --sig-off-bg: rgb(241,240,236); --sig-off-line: rgb(227,225,219); --sig-off-ink: rgb(85,85,78);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --sig-good-bg: rgb(20,46,32); --sig-good-line: rgb(38,86,58); --sig-good-ink: rgb(150,222,178);
    --sig-source-bg: rgb(26,28,58); --sig-source-line: rgb(58,62,110); --sig-source-ink: rgb(174,178,240);
    --sig-warn-bg: rgb(48,38,16); --sig-warn-line: rgb(94,74,30); --sig-warn-ink: rgb(233,190,110);
    --sig-off-bg: rgb(32,32,30); --sig-off-line: rgb(62,62,58); --sig-off-ink: rgb(176,176,168);
  }
}
:root[data-theme="dark"] {
  --sig-good-bg: rgb(20,46,32); --sig-good-line: rgb(38,86,58); --sig-good-ink: rgb(150,222,178);
  --sig-source-bg: rgb(26,28,58); --sig-source-line: rgb(58,62,110); --sig-source-ink: rgb(174,178,240);
  --sig-warn-bg: rgb(48,38,16); --sig-warn-line: rgb(94,74,30); --sig-warn-ink: rgb(233,190,110);
  --sig-off-bg: rgb(32,32,30); --sig-off-line: rgb(62,62,58); --sig-off-ink: rgb(176,176,168);
}

/* `settled` and `coverage` ARE `SIGNAL_TONES`' OWN NAMES, not new ones. The map has
   said `moment`->settled and `evidence`->coverage since 142; these give those two the
   good and source tints rather than renaming a map three call sites read. */
.v3-signal--good, .v3-signal--settled,
.v3-fsig--good {
  background: var(--sig-good-bg); border-color: var(--sig-good-line); color: var(--sig-good-ink);
}
.v3-signal--source, .v3-signal--coverage,
.v3-fsig--source {
  background: var(--sig-source-bg); border-color: var(--sig-source-line); color: var(--sig-source-ink);
}
.v3-signal--warn {
  background: var(--sig-warn-bg); border-color: var(--sig-warn-line); color: var(--sig-warn-ink);
}
.v3-signal--off,
.v3-fsig--off {
  background: var(--sig-off-bg); border-color: var(--sig-off-line); color: var(--sig-off-ink);
}


/* WHY THIS VIDEO IS HERE (199). The first thing in the rail, because it is the
   sentence a manager reads before deciding whether to read anything else. Quiet
   rather than loud: it states facts, and the severity chips on the rows below are
   what carry alarm. */

/* THE CARD IS AN EDGE, NOT A MOVE (200 item 4). The owner asked for the summary to
   "align with the video player on the left" and IT ALREADY DID: `.video-grid` is
   `align-items: start`, and measured on the deployed page the section and the player
   iframe both sat at top 300. With no border, no ground and no padding, the section's
   first visible mark was its heading at 318 -- so a correct alignment looked like an
   18px error. Drawing an edge is what makes the existing alignment visible. MOVING the
   box would break a correct layout to satisfy an appearance, and the bounding-top check
   exists to catch exactly that fix.

   `.v3-rqueue`'s three properties, not a new idiom, so the rail reads as one family and
   both themes come from `--rule` and `--paper` rather than from literals. */
.v3-why {
  margin: 0 0 18px; padding: 16px;
  border: 1px solid var(--rule); border-radius: 12px; background: var(--paper);
}
/* The border stops the heading's margin collapsing, so the first line would otherwise
   sit on a UA margin inside the padding rather than on the padding. */
.v3-why > .sec { margin: 0 0 10px; }
/* 15px, NOT 13.5 (200 item 5). This is the page's main message and it was set in the
   smallest body text on the surface. CONTRAST WAS NEVER THE CAUSE -- 16.83:1 on both
   paragraphs, measured. Four typographic causes were: the size, a 6px gap between two
   61px blocks, the `inline-flex` that broke the wrap, and five bold runs in 351
   characters. Emphasis every few words is emphasis nowhere. */
.v3-why__line { font-size: 15px; line-height: 1.5; color: var(--ink); margin: 0 0 12px; }
.v3-why__line:last-child { margin-bottom: 0; }
.v3-why__line b { font-weight: 650; }

/* The one control in the decision area now (200 items 1-2), quiet by the owner's
   §6: secondary under the summary rather than a call to action. */
.v3-manage { margin: 0 0 18px; }

.v3-signalsec { margin-top: 26px; }
.v3-signalsec .v3-signals { margin-top: 10px; }

/* THE WALK, AT THE FOOT. Removed from the top by 145 and not deleted: 118 built it
 * and a reader who cannot step to the next video has lost the set. */
.v3-walkfoot {
  margin-top: 30px; padding-top: 16px; border-top: 1px solid var(--rule);
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
}
.v3-walkfoot__back { margin: 0; font-size: 12.5px; }
.v3-walkfoot__back a { color: var(--ink-mid); text-decoration: none; }
.v3-walkfoot__back a:hover { color: var(--ink); text-decoration: underline; }

/* --- instruction 146: the nine ruled divergences, forked ------------------- *
 *
 * EVERY RULE HERE IS AN ADDITIVE `v3-` CLASS ALONGSIDE THE DESIGN-V2 ONE, never
 * an edit to it. `.fchip`, `.grid`, `.btn--primary` and `.chip--attention` ship
 * on design-v2 and are not ours to change; each rule below raises specificity by
 * pairing the v2 class with a v3 one, so only elements that opt in move.
 *
 * THE RIDER ON ALL OF THEM: matching the design is the target, EXCEPT where the
 * design would ship a contrast pair it never had to answer for -- it declares
 * `color-scheme: light` and has no dark value. Where a substitution was made the
 * rejected ratio is recorded beside the shipped one.
 */

/* 146.1 THE TABLE HEADER. The design's is a quiet band -- `--raised` ground,
   `--ink-mid` text. design-v2's `.grid th` inverts to `--band`/`--band-ink`,
   which is a strong horizontal rule across the page and the first thing a reader
   sees. Measured 6.59:1 light, 10.18:1 dark -- no substitution needed. */
/* `table.grid.v3-grid thead th`, because design-v2's is `table.grid thead th` --
   one class plus two elements. `.v3-grid th` is one class plus one element and
   lost silently; the header stayed `--band` navy and the diff looked applied. */
/* 151.1: the design's header band carries a 1px rule under it and ours carried
   none. Same ground on both; only the line was missing. */
table.grid.v3-grid thead th {
  background: var(--raised); color: var(--ink-mid);
  border-bottom: 1px solid var(--rule);
}

/* THE ROW'S OWN METRICS, FROM A PROPERTY DIFF OF THE TWO PAGES rather than from
   looking. The design is 13px on 17.55px with 10px/11px padding; ours was 14px on
   22.4px with 7px/12px.

   THE LINE-HEIGHT IS THE ONE THAT MATTERS -- 4.85px on every line of every row --
   and the reason nobody spotted it by eye is that the two differences pull opposite
   ways: our padding is SMALLER and our line-height LARGER, so a row looks nearly
   right while being built from two wrong numbers.

   `table.grid.v3-grid td`, not `table.grid td`: the design-v2 rule ships on 28
   pages and is not ours. */
table.grid.v3-grid tbody td {
  font-size: 13px; line-height: 1.35; padding: 10px 11px;
}

/* 151.2: EXPLICIT WIDTHS, CHOSEN FROM THE CONTENT. Measured at 1512px, the widest
   VISIBLE content plus padding in each column:

       Video    345px    a 40-character Cyrillic title
       Channel  284px    `HUSMUT : ХАСМУТ / ХАСки и малаМУТ /`
       Signals   86px    two 26px tiles and a gap
       Status    91px    `Not started`

   SIGNALS AND STATUS WERE HOLDING 30% AND 24% FOR 10% OF CONTENT -- over 400px on
   eleven characters and two icons -- while Channel had 117px and wrapped to three
   lines. The pattern is `.grid--queue`'s, twelve hundred lines up in this file.

   THE `vh` TEXT IS NOT CONTENT FOR THIS PURPOSE and measuring it said Signals
   needed 230px. `.vh` is `position: absolute` with a 1px box, so it occupies no
   column width; a first pass counted it and would have kept Signals three times
   wider than it needs to be. */
/* TWO TABLES WEAR `.v3-grid` AND THEY DO NOT HAVE THE SAME COLUMNS. Everything
   below turns on that, and `nth-child` alone cannot express it.

     v3_dashboard  .grid.grid--queue.v3-grid   Video | Channel | Signals | Status
     v3_videos     .grid.v3-grid               Video | Channel | Worst severity |
                                               Where in the video | Review

   So column 4 is STATUS on one page and WHERE IN THE VIDEO on the other, and a
   single `nth-child(4)` rule governed both. That is the whole defect: the width and
   `nowrap` that Status needs were applied to a column holding an icon, whose header
   is 134px of text. Measured before the fix, the videos header box was 4px at 1280,
   17px at 1401 and 33px at 1512, and the heading painted 146px, 133px and 117px
   PAST its own cell -- interleaving with `Review` into `WEHREEVIEWN TH`. The
   document never scrolled horizontally, so no page-level overflow check could see
   it: the text escapes its cell, not the page.

   THE COMMENT THAT USED TO STAND HERE WAS WORSE EVIDENCE THAN THE CODE. It was
   headed "STATUS TAKES WHAT IT NEEDS AND NEVER WRAPS" and reasoned about
   `review.label` and `Not started` -- and it was RIGHT, for the dashboard, where
   column 4 IS Status. It was simply also governing a different column on another
   page. A reader repairing the width without opening both templates re-derives the
   same mistake, because the comment tells them column 4 is Status.

   The tables are separated by `:not(.grid--queue)` / `.grid--queue` rather than by
   count, and each block below NAMES its column. `nth-child` is a position, and here
   the position means two different things at once. */

/* Shared by both tables: Video, Channel, and the third column (Worst severity on
   videos, Signals on the dashboard). */
table.grid.v3-grid th:nth-child(2), table.grid.v3-grid td:nth-child(2) { width: 31%; }
table.grid.v3-grid th:nth-child(3), table.grid.v3-grid td:nth-child(3) { width: 12%; }

/* COLUMN 1, VIDEO. 43% on the dashboard, which has four columns and no pinned last
   column, so its remainder is ample. 34% on the videos table, and THIS IS WHERE THE
   MISSING WIDTH COMES FROM: 43 + 31 + 12 = 86%, plus the shared 136px last column,
   left 4px for column 4 at 1280. Nine points off the widest column buys column 4
   roughly 90px at 1280 and 141px at 1512.

   Taken from Video and not from Channel deliberately. `.v3-chancell` already clips
   at 31% at every real width -- that trade is argued below and narrowing it further
   would deepen a cost the file has already accepted once. `.v3-videolink__title`
   trims to its column by ellipsis, so Video spends the nine points on tail
   characters that were already being trimmed. */
table.grid.v3-grid.grid--queue th:nth-child(1),
table.grid.v3-grid.grid--queue td:nth-child(1) { width: 43%; }
table.grid.v3-grid:not(.grid--queue) th:nth-child(1),
table.grid.v3-grid:not(.grid--queue) td:nth-child(1) { width: 34%; }

/* COLUMN 4 ON THE DASHBOARD IS STATUS, and this is the reasoning that used to sit
   on the shared rule. It is unchanged and it was always correct about this column.

   STATUS TAKES WHAT IT NEEDS AND NEVER WRAPS, and it is `auto` rather than a
   percentage because the label vocabulary is not bounded here -- `review.label` is
   built elsewhere and `Not started` is only the untouched case. A percentage sized to
   one string is a guess about the others.

   IT WRAPPED AT 13%. Measured: the column was 79px at 1280 and the cell 57px inside
   its padding, so `Not started` broke over three lines and the row grew. THE WIDTH
   CHECK THAT CHOSE 13% MEASURED THE TEXT AT 91px AND NOT THE RING AND GAP BESIDE IT
   -- part of the content, taken for the content.

   AND `white-space` HAD TO BE RESTATED. The cell carries `.nowrap`, but design-v2's
   `table.grid.grid--queue td:last-child` sets `white-space: normal` at 0,2,2 and beat
   it. This selector is higher. */
table.grid.v3-grid.grid--queue th:nth-child(4),
table.grid.v3-grid.grid--queue td:nth-child(4) {
  width: auto; white-space: nowrap;
}

/* COLUMN 4 ON THE VIDEOS TABLE IS WHERE IN THE VIDEO, whose cells hold an icon and
   whose header holds three words. A COLUMN SIZED TO ITS CELLS AND NOT ITS HEADER is
   the defect, and it recurs on any column whose heading is longer than its data.

   THE HEADING WRAPS RATHER THAN THE COLUMN GROWING TO 134px. Three short words, and
   the header row already has the vertical room -- `Worst severity` beside it wraps
   to two lines at 1280 today. Buying a full 134px would have cost another four
   points off Video for a header that reads perfectly well over two lines.

   NOT `overflow: hidden`. That clips the heading to "Where in th..." and trades a
   collision for a truncation no reader can widen, which is the trade instruction
   190 §3 already refused for the signal labels. A heading nobody can read the end of
   is not a fixed column. */
table.grid.v3-grid:not(.grid--queue) th:nth-child(4),
table.grid.v3-grid:not(.grid--queue) td:nth-child(4) {
  width: auto; white-space: normal;
}

/* AND THE CHANNEL CLIPS RATHER THAN WRAPS. 284px is what the longest name needs
   and 31% is 261px at 1512 and 189px at 1280 -- so at every real width there is a
   name that does not fit, and no allocation fixes that without starving the title.
   Wrapping tripled that row to 172px; clipping costs the tail of one name, which
   the channel filter and the row itself both still carry. Same trade instruction
   139 made for the title, for the same reason. */
.v3-chancell {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 0;
}

/* 146.2 THE PRIMARY CALL TO ACTION, AND THE FILL NEEDED NO FORK AT ALL.
   `.btn--gold` has been `var(--gold)` with a `var(--gold)` border since long
   before design-v3, on nineteen templates, and it IS the design's button --
   `--gold` is the prototype's `--green` to the byte. The divergence was simply
   that v3's dashboard and review reached for `.btn--primary`, which is `--band`
   navy. So the CTA becomes `btn btn--gold v3-cta`, and this rule adds only what
   the design draws on top of it: radius 7 rather than 8, weight 700 rather
   than 650.

   THE LABEL IS THE RIDER'S CASE, AND NO INK ON THAT FILL REACHES AA IN DARK:

       light   #ffffff on #c0073f    6.28:1   ok
       dark    #ffffff on #f61e6f    3.94:1   FAILS the 4.5 floor
       dark    --ink   #fafafa       3.77:1   FAILS
       dark    --navy  #E8EBF2       3.30:1   FAILS

   The prototype declares `color-scheme: light` and never rendered this button on
   a dark ground. Rather than dull the accent -- pinning the light `#c0073f` fill
   passes at 6.28:1 but leaves the button at 3.15:1 against the dark page, which
   is a button you have to look for -- the LABEL inverts. `--gold-ink` is white in
   light and near-black in dark: 5.03:1 on the untouched accent.

   Rejected 3.94:1, shipped 5.03:1. `--gold` / `--gold-ink` is the same paired
   surface `--band` / `--band-ink` already is in this file.

   REPORTED AND NOT FIXED: `.btn--gold` itself still hard-codes `color: #ffffff`,
   so every one of those nineteen design-v2 templates renders a gold button at
   3.94:1 in dark. That is a pre-existing design-v2 defect, not one 146 creates,
   and design-v2 is out of scope here. This rule overrides the label for v3 only. */
.btn.v3-cta { color: var(--gold-ink); border-radius: 7px; font-weight: 700; }

/* 146.3 THE SEVERITY LEVEL, UNBOXED. The tone class still supplies the colour;
   this removes the box the design does not draw. The word carries the meaning,
   so this was never a colour-only signal and nothing is lost to a screen reader
   or a colour-blind reader. Measured across all five tones, both themes, on
   every row ground including the SELECTED row -- worst pair 6.26:1 against the
   4.5 floor, better than the 6.66:1 the wash gave. Rejected nothing. */
.chip.v3-sev {
  background: transparent; border-color: transparent; box-shadow: none;
  padding: 0; border-radius: 0; font-size: 13px; font-weight: 760;
}

/* 146.6 THE COUNT BADGE takes the design's chip SHAPE and keeps our sentence.
   `69 shown` is a number a reader can check against the rows in front of them;
   `live sample shape` describes a prototype. The design specifies how the
   product looks, not a worse sentence than the one we have. */
/* 15.28:1 light, 16.15:1 dark -- `--ink` on `--raised`. */
.badge.v3-badge {
  background: var(--raised); color: var(--ink); border: 1px solid var(--rule);
  border-radius: 999px; padding: 5px 10px; font-size: 12px; font-weight: 600;
}

/* 146.7 / 146.8 THE PAGE-HEAD TYPE SCALE, one change across three pages.
   `.display` is 40/800 and `.lede` 16.5; the design is 30/700 and 14 on all nine
   of its screens. Those two classes ship on design-v2, so these are additive and
   only the v3 page heads take them. `--ink` and not `--navy`: the design's h1 is
   `#1a1a18`, which is `--ink` to the byte. */
/* `h1.` AND NOT `.v3-display` ALONE. design-v2's rule is `h1.display`, which is
   one class plus one element; a bare `.v3-display` is one class and loses on
   specificity however far down the file it sits. Measured before this: the h1
   stayed 40px/800 and the change was silently inert. */
h1.v3-display { font-size: 30px; font-weight: 700; color: var(--ink); margin-bottom: 4px; }
.v3-lede { font-size: 14px; }

/* --- instruction 147 -------------------------------------------------------- */

/* THE CHANNEL PICKER. Nine chips wrapped; the owner ruled them into a select. The
   label is VISIBLE and not `.vh` as it is on `v3_videos`: there the select sits in
   a bar of six labelled controls, here it sits beside four state chips, where a
   bare list of channel names is a control that does not say what it does. */
/* `margin-left: auto` INSIDE THE NARROWED STRIP puts the control's right edge on the
   strip's, which is the card's. Nothing here names a pixel. */
.v3-chanpick { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.v3-chanpick__label { font-size: 12px; font-weight: 650; color: var(--ink-mid); }

/* THE SIGNALS CELL, TEXT UNTIL THE DESIGN FILE LANDS. The design draws tinted
   chips with a count badge overlapping the first; six of its properties do not
   survive a prose description and were not guessed. What is here is the same
   information as text -- which is what a screen reader gets from the finished
   chips too, because the reason is the label and the glyph is `aria-hidden`. */
/* THE FLEX IS ON A SPAN INSIDE THE CELL, NOT ON THE CELL. `display: flex` on a
   `<td>` takes it out of table layout entirely: it stops participating in the
   column width algorithm, so the cell shrink-wrapped to 78px while its own `<th>`
   was 326px, the reasons stacked one per line, and the median row went from 60px
   to 181px -- the page from 5,270px to 14,474px. Measured before and after.

   SAME FAMILY AS INSTRUCTION 139, one element further out. There an inline-flex
   container sized to its text instead of to the cell; here a flex cell sized to
   its text instead of to the column. A box that follows its content rather than
   its container is the recurring shape, and `display` is where it starts. */
/* `overflow: visible` SO THE TOOLTIP IS NOT CLIPPED by the cell it sits in -- the
   panel rises above a 52px row and the `td` would cut it. Declared HERE and not in a
   second rule at the end of the file: the duplicate-selector guard from 149 caught
   exactly that, which is the seventh one-name-two-things today and the second my own
   guard has caught rather than a reviewer. */
.v3-sigcell { vertical-align: middle; overflow: visible; }
.v3-sigrow { display: flex; flex-wrap: nowrap; align-items: center; gap: 6px; overflow: visible; }
.v3-sig {
  font-size: 12px; color: var(--ink-mid);
  background: var(--raised); border: 1px solid var(--rule);
  border-radius: 6px; padding: 2px 8px;
}
/* THE SIGNAL TILE. A rounded square about a line tall, a 1px border in its tone, a
   tinted ground in the same tone, one icon in the tone's ink. Each tone pair is the
   estate's tested `--x` / `--x-wash` / `--x-line` triple: measured 6.43 to 7.66:1
   in both themes, so no substitution was needed for any of them.

   ONE TILE PER REASON THE ROW ACTUALLY HAS. The design draws four; this estate
   produces one on 61 of 69 rows and two on the other eight, because five of the
   seven reasons have never occurred -- four are manager decisions nobody has
   recorded yet. Four fixed slots would render three empty boxes on most rows,
   which is the placeholder check F of 143 forbids. The row grows to four the day
   somebody starts reviewing. */
.v3-tile {
  position: relative; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 7px;
  border: 1px solid var(--rule); background: var(--raised); color: var(--ink-mid);
}
.v3-tile--coverage { border-color: var(--coverage-line); background: var(--coverage-wash); color: var(--coverage); }
.v3-tile--settled  { border-color: var(--settled-line);  background: var(--settled-wash);  color: var(--settled); }
/* TWO TONES, BECAUSE TWO REASONS OCCUR. `attention` and `material` variants were
   written here and removed: `SIGNAL_TONES` cannot produce them, so they would be
   rules for a case that never renders -- the weight `_v3_icons.html` argues against
   carrying, and the tone guard would have had nothing to check them against. They
   arrive with the reason that needs them. */

/* THE COUNT, OVERLAPPING THE FIRST TILE'S TOP-RIGHT, and the ink is not white.
   The design draws a white numeral on the crimson fill; measured, that is 6.28:1
   in light and 3.94:1 in DARK, and a badge numeral is ~10.5px so the 4.5 floor
   applies rather than the 3.0 large-text one. `--gold-ink` -- white in light,
   near-black in dark -- gives 5.03:1 on the untouched fill. Rejected 3.94,
   shipped 5.03. Same token, same pairing, same reason as the primary button. */
.v3-tilecount {
  position: absolute; top: -6px; right: -7px;
  min-width: 17px; height: 17px; padding: 0 4px; border-radius: 999px;
  background: var(--gold); color: var(--gold-ink);
  font-size: 10.5px; font-weight: 700; line-height: 15px; text-align: center;
  font-variant-numeric: tabular-nums; border: 1px solid var(--ground);
}

/* THE STATUS RING IS A BORDER, NOT A SYMBOL. The sprite holds ten glyphs and none
   is a ring; `_v3_icons.html` was out of 147's scope, and the nearest borrowable
   symbol is `v3i-check`, which that file documents as LOW SEVERITY. A severity
   glyph standing in for a workflow state is the defect 25's sprite test exists to
   catch, and it has caught it once already. A circle is a border-radius. */
.v3-status { display: inline-flex; align-items: center; gap: 7px; }
/* 151.5: a THIN ring with a CENTRE DOT, muted rather than bright. Ours was a 2px
   ring in `--gold` crimson and no dot. `--material` is the estate's muted dark red
   -- 151 names the design's colour as "muted dark red" and gives no hex, so this is
   the nearest token rather than a sampled pixel, and it is reported as a choice. */
.v3-status__ring {
  position: relative; width: 12px; height: 12px; flex: none;
  border: 1px solid var(--material); border-radius: 999px;
}
.v3-status__ring::after {
  content: ""; position: absolute; inset: 3px;
  background: var(--material); border-radius: 999px;
}

/* The way back to design-v2, set off from the nine. Instruction 144: it is the
 * product's front door rather than one of its pages, which is a different thing to
 * want, so it sits below a rule instead of inside a labelled group.
 *
 * `v3-railfoot` AND NOT `rail__group--foot`. Every other class in `_v3_rail.html`
 * is an existing v2 class reused on purpose, so the file reads as if `rail__*` is
 * the family to extend -- but this one is NEW, and a new class in a v2 family
 * collides with design-v2 in both directions. The prefix guard caught it, which is
 * what it is for. */
/* ALSO KNOWINGLY RETAINED, AND ITS COMMENT ABOVE IS HISTORY NOW. "Set off from the
 * nine" describes a rail that had nine entries before 167 and eight after it; 195 left
 * two, and the way back this styled has been in the masthead since 167. So the class
 * has no subject. It stays for the same reason as `.v3-railout` above -- if a labelled
 * way out ever returns to the rail, this is its rule, argued -- and the count in the
 * paragraph above is left as the record of what was true when it was written. 198. */
.v3-railfoot { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--rule); }

/* --- instruction 151: the masthead ------------------------------------------ *
 *
 * v3 ONLY. `.acctinit`, `.acctsub` and `.acctname` are on `base.html`'s masthead
 * too, so every rule here pairs the design-v2 class with a `v3-` one and design-v2
 * is untouched.
 */

/* `Dark mode`, a second door onto `set_theme` and not a second mechanism. */
.v3-themeform { display: flex; }
.v3-themebtn {
  border: 1px solid var(--rule); background: var(--paper); color: var(--ink);
  border-radius: 7px; padding: 7px 12px; font-size: 12.5px; font-weight: 650;
  cursor: pointer; white-space: nowrap;
}
.v3-themebtn:hover { border-color: var(--rule-firm); }

/* THE AVATAR IS A ROSE CIRCLE WITH CRIMSON INITIALS. `--gold-soft` is the design's
   rgb(247,219,228) to the byte and `--gold` its rgb(192,7,63); measured 4.85:1,
   which clears AA for the 12px initials. Ours was navy with light ink at radius 7. */
/* `--gold-tile`, NOT `--gold-soft`, AND HERE IT MATTERS MORE THAN ON THE COUNTER.
   The initials are TEXT at 12px, so the floor is 4.5 rather than the 3.0 a glyph
   gets. `--gold` on `--gold-soft` is 4.85:1 in light and 2.39:1 in dark; the paired
   token gives 4.85 and 4.58. Light is the design's rose exactly. */
.acctinit.v3-acctinit {
  width: 30px; height: 30px; border-radius: 50%;
  background: var(--gold-tile); color: var(--gold);
}
/* ONE LINE, NOT STACKED. `.acctsub` is `display: block` on design-v2's masthead,
   which is right where it sits under a name in a menu and wrong here. */
/* `flex-direction: row` IS THE PROPERTY THAT DOES IT, and the first version of this
   rule set `display` and not direction. `.acctname` is `display: flex;
   flex-direction: column`, so overriding only the display left the column intact and
   the name stayed stacked -- measured, "Centurion" at y=13 and "Live output" at y=35
   with the rule apparently applied. */
.v3-acctname {
  display: inline-flex; flex-direction: row; align-items: baseline;
  gap: 5px; white-space: nowrap;
}
.v3-acctname .v3-acctsub { display: inline; }

/* 151.8: THE SIGNAL TOOLTIP. The design draws a dark rounded panel with white text;
   a native `title` cannot be styled, so this replaces it rather than sitting beside
   it -- both would render.

   `--band` / `--band-ink` AND NOT A NEW COLOUR. That pair is this file's pinned
   inverted surface, the same in light and dark by design, which is exactly what a
   tooltip panel needs: 13.09:1 either way, and no second definition to keep in step.

   HOVER AND FOCUS-VISIBLE BOTH. Hover reaches neither a keyboard nor a touch screen
   and that is why the tooltip was removed once already; the tile carries
   `tabindex="0"` so focus can reach it. NO JAVASCRIPT -- this is two selectors. */
.v3-tip {
  position: absolute; bottom: calc(100% + 7px); left: 50%; transform: translateX(-50%);
  z-index: 5; white-space: nowrap; pointer-events: none;
  background: var(--band); color: var(--band-ink);
  border-radius: 7px; padding: 6px 9px; font-size: 12px; font-weight: 600;
  opacity: 0; visibility: hidden; transition: opacity .09s ease;
}
.v3-tile:hover .v3-tip,
.v3-tile:focus-visible .v3-tip { opacity: 1; visibility: visible; }
.v3-tile:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* --- the page top, taking the design's four values ------------------------- *
 *
 *     masthead -> counters   design 87px    live 127px
 *
 *     main padding-top       18   was 44    <- the bulk of it
 *     .eyebrow margin-bottom  8   was 10
 *     h1 margin-bottom        4   was 14    <- folded into `h1.v3-display`
 *     h1 -> counters         16   was 22
 *
 * ALL FOUR ARE SHARED AND ALL FOUR ARE SCOPED TO v3. `main { padding: 44px 0 90px }`
 * is a BARE ELEMENT RULE and `.eyebrow` and `h1.display` are design-v2 classes -- so
 * every one of these would have moved 28 design-v2 pages, which is what `943e46b`
 * did with the rail and what `section {}` has now done twice.
 *
 * THE BOTTOM PADDING IS NOT TOUCHED. Only the top is measured against the design and
 * only the top changes; 90px at the foot is design-v2's and there is no reading for
 * it here. */
body.v3-surface main { padding-top: 18px; }
body.v3-surface .eyebrow { margin-bottom: 8px; }
