/* ControlBridge — the Control Operations Desk.
 *
 * Vanilla CSS, one file, no build step. This is the whole visual
 * system: [DESIGN.md](../../devdocs/design/DESIGN.md) is the
 * contract and this file is its implementation. §5's palette,
 * §6's type scale and §7's spacing/shape/elevation/density are
 * *checked* against this file by
 * [design_tokens_test.py](../server/design_tokens_test.py), so a
 * value that disagrees with the doc is a failing test, not a
 * matter of taste.
 *
 * Three rules that shape everything below, all from §4:
 *
 *   1. **Flat.** Content surfaces are separated by hairline
 *      borders, never by shadow. A shadow means "this floats
 *      above the workspace" — a drawer, a dialog, a menu, a
 *      popover — and nothing else may claim it.
 *   2. **No gradients, no emoji, no decorative AI styling.**
 *      Icons come from the Lucide registry
 *      ([core/icons.js](core/icons.js)) and nowhere else.
 *   3. **Colour never carries meaning alone.** Every status has
 *      words; the tint reinforces the sentence, it does not
 *      replace it.
 *
 * All colour lives in the token block below. If you are adding a
 * colour literal to a rule, stop: add or reuse a token instead.
 * See devdocs/design/brand-tokens.md. */

/* ===================== DESIGN TOKENS =====================
   Source of truth: DESIGN.md §5 (colour), §6 (type), §7
   (spacing/shape/elevation). brand-tokens.md records *why* these
   values — their provenance in ControlBridge's own UI_STANDARDS,
   the two accessibility deviations, and the rules for changing
   them.

   The LEGACY alias layer (--ink/--accent/--good/…) is gone as of
   2026-07-28. It existed so one restyle did not have to rewrite
   260 call sites; this restyle rewrote them, and keeping a second
   vocabulary alive for a stylesheet that no longer speaks it is
   how two names for one colour drift apart. */
:root {
  /* ---- Palette (DESIGN.md §5) ---- */
  --brand: #2563eb;
  --brand-dark: #1d4ed8;
  --brand-tint: #eff6ff;
  /* The tint's own border. --line reads as a divider against
     white and disappears against --brand-tint, so a selected row
     or an informational callout had no edge at all. */
  --brand-line: #bfdbfe;
  --nav-bg: #0f172a;
  --nav-text: #f8fafc;
  --danger: #ef4444;
  --success: #16a34a;
  --warn: #f59e0b;
  --info: #2563eb;
  --text-primary: #0f172a;
  /* The standards say #64748b. Darkened by one notch, and only
     for a measured reason: #64748b clears AA on WHITE (4.76) but
     lands at 4.34 on --background #f1f5f9 — and --background, not
     white, is what sits behind most secondary text (page subtitles,
     breadcrumbs, field labels). It also failed on --warn-tint
     (4.32). #5a6a7e is the same slate hue and clears 4.5 on
     white, --background, --surface-subtle and all four tints
     (worst case 4.54 on --danger-tint). Found by measuring the
     rendered DOM, not the token table — the table looked fine.
     Held there by design_tokens_test.py, which recomputes every
     ground rather than the ones text happens to sit on today. */
  --text-secondary: #5a6a7e;
  /* Placeholder and disabled only — 2.56 on white, which WCAG
     exempts for disabled controls. Never use it for content. */
  --text-muted: #94a3b8;

  /* ---- AA-safe variants of the three semantic hues ----
     The standards' --danger/--success/--warn are correct as FILLS
     but fail WCAG 2.2 AA as small text on white: 3.76, 3.30 and
     2.15 against a 4.5 requirement. This project makes AA a
     per-view acceptance criterion, so each hue gets a darkened
     `-ink` for text and a `-tint` for its background. Same hue,
     legible: every -ink clears 4.5 on BOTH white and its own tint.
     The base hues stay untouched for fills, borders and icons. */
  --danger-ink: #ca2525;   --danger-tint: #fce3e3;
  --success-ink: #1f8143;  --success-tint: #e4fbed;
  --warn-ink: #966513;     --warn-tint: #fcf3e3;
  /* Tint borders, for the same reason as --brand-line: a pill or
     a callout needs an edge its own tint does not swallow. */
  --danger-line: #efc6c6;
  --success-line: #b8e8ca;
  --warn-line: #eedcae;

  /* ---- Neutrals ---- */
  --background: #f1f5f9;
  --surface: #ffffff;
  --surface-subtle: #f8fafc;
  --line: #e2e8f0;
  /* Emphasised boundary / control border (DESIGN.md §5).
     Distinct from --line: this is the one a control's own
     edge uses, so an input reads as interactive against a
     surface that --line merely divides. */
  --line-strong: #cbd5e1;

  /* The Copilot's assistant bubble. Neutral on purpose: the other
     three bubbles carry meaning in their colour (you = brand
     fill, tool = warn tint, error = danger tint); the assistant's
     turn is the ordinary case and DESIGN.md §4 forbids
     "AI everywhere" styling, so it takes the plain grouped
     surface. */
  --msg-ai: var(--surface-subtle);

  /* ---- Nav surface. Every colour here is nav-specific:
          --text-* are tuned for light grounds and are unreadable
          on #0f172a, which is why the nav carries its own small
          set rather than borrowing the page's. ---- */
  --nav-muted: #94a3b8;
  --nav-hover: #1e293b;
  --nav-sel-bg: #1e3a8a;
  --nav-sel-ink: #ffffff;
  --nav-item-ink: #cbd5e1;
  --nav-link: #93c5fd;
  --nav-rule: #334155;

  /* ---- Shell geometry (DESIGN.md §3) ----
     Set as tokens because three things move them: the collapse
     toggle, the Copilot's resize, and the breakpoints. A grid
     template written out per state would be four templates to
     keep in step. */
  --topbar-h: 56px;
  --nav-w: 224px;
  --nav-w-rail: 56px;
  --copilot-rail: 44px;
  --copilot-w: 384px;
  --gutter: 24px;

  /* ---- Shape (§7): 6px controls, 8px panels, no more ---- */
  --radius: 6px;
  --radius-panel: 8px;

  /* ---- Elevation (§7). ONE shadow, for things that physically
          overlay the workspace. Content is flat. ---- */
  --shadow-overlay: 0 8px 24px rgba(15, 23, 42, .16);

  /* ---- Motion (§12). 80 feedback, 160 selection, 240 panel. -- */
  --t-fast: 80ms;
  --t-mid: 160ms;
  --t-slow: 240ms;

  /* UI_STANDARDS "Typography": system stack, NO external font
     load. The previous stack led with 'ITC Avant Garde Gothic',
     which has no @font-face anywhere in this repo — so it
     rendered only for people who happened to have that licensed
     face installed, and everyone else silently got a different
     typeface. */
  --font-sans: ui-sans-serif, system-ui, -apple-system, 'Segoe UI',
    Roboto, 'Helvetica Neue', Arial, sans-serif;
  --font-mono: SFMono-Regular, Consolas, 'Liberation Mono', Menlo,
    monospace;
}

* { box-sizing: border-box; }

/* The `hidden` ATTRIBUTE must always win.
 *
 * The browser's own `[hidden] { display: none }` is specificity
 * (0,1,0), so any rule of ours that sets `display` and names an
 * element plus a class — `button.btn`, `.card`, `.pane` — beats
 * it and the element stays on screen. That is not hypothetical:
 * the moment buttons gained `display: inline-flex`, the workflow
 * bar showed Run, Resume, Restart, Stop AND Kill at once, because
 * `wfControls()` hides four of them with `.hidden = true`.
 *
 * It matters beyond looking wrong. `hidden` is this app's chosen
 * way to hide things that must ALSO be hidden when the stylesheet
 * is removed — the global search's filtered-out `.card`s, the
 * workspace pane a narrow viewport is not showing, the run-detail
 * holder. view_check.py reads every view twice and compares; a
 * `hidden` that only works sometimes breaks that check in the
 * direction nobody notices, by showing MORE in the styled read. */
[hidden] { display: none !important; }

html { color-scheme: light; }
html, body { margin: 0; height: 100%; }
body {
  color: var(--text-primary);
  font: 14px/1.45 var(--font-sans);
  background: var(--background);
}
button, input, select, textarea { font: inherit; }
a { color: var(--brand-dark); text-decoration: none; }
a:hover { text-decoration: underline; }
svg { display: block; }

/* ONE focus indicator for the whole application (§14). Named
   once here so no component has to remember it, and so removing
   it anywhere is a visible deletion rather than an omission. */
:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 1px;
}

/* Lucide icons (core/icons.js). One size and stroke for the whole
   app — a set reads as a set only if nothing varies. 16px on a
   24-unit grid, and stroke-width scaled to 1.75 because lucide's
   default 2 goes chunky below 20px. `currentColor` is inherited,
   so hover and selected states recolour the icon with no second
   asset. */
.ico-svg {
  width: 16px; height: 16px; display: block;
  stroke-width: 1.75; flex: none;
}
.ico {
  display: inline-flex; align-items: center;
  justify-content: center; flex: none;
}

/* Copilot highlight: mark the target the agent is pointing at.
   A solid ring, not a glow or a pulse — §4 forbids glowing
   borders and §12 forbids pulsing status. It reads as
   "this one", which is the whole job. */
.ui-glow {
  outline: 3px solid var(--brand) !important;
  outline-offset: 2px;
  border-radius: var(--radius);
}

/* ===================== UTILITY BAR =====================
   §3: 56px, search + context + account. `.topbar` and
   `.topbar button` are agent targets (cb_adapter SEL) — the
   class name is a contract, not a description. */
.topbar {
  display: flex; align-items: center; gap: 12px;
  height: var(--topbar-h); padding: 0 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 20;
}
.brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
/* width:auto is load-bearing, not tidiness. index.html's width/height
   are a presentational hint, which keeps applying to width when a rule
   overrides only height — so the 20px narrow override below stretched
   the mark to 178x20 (8.9:1 against a 7.4:1 file) until this line
   existed. Let width follow the aspect ratio at every height. */
.brand-logo { height: 24px; width: auto; display: block; }
.search {
  flex: 1; display: flex; align-items: center; gap: 8px;
  max-width: 560px; min-width: 0;
  height: 36px; padding: 0 4px 0 11px;
  background: var(--surface-subtle);
  border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--text-secondary);
}
/* The ring moves from the input to the WRAPPER — the input has
   no border of its own, so a ring on it would draw inside the
   field. It has to be a real outline and not a border colour:
   finding #6 of the 2026-07-27 guidelines audit was exactly
   this, `outline: none` replaced by a colour change, which is
   invisible to a colour-blind user and is what
   scripts/keyboard_check.py measures rather than trusts. */
.search:focus-within {
  border-color: var(--brand); background: var(--surface);
  outline: 2px solid var(--brand); outline-offset: 1px;
}
.search input {
  flex: 1; min-width: 0; height: 100%;
  border: 0; background: none; color: var(--text-primary);
  font-size: 13px;
}
/* Suppressed only because the wrapper above paints one; two
   concentric rings on one control read as a rendering bug. */
.search input:focus-visible { outline: none; }
.search input::placeholder { color: var(--text-muted); }
.topbar-right {
  display: flex; align-items: center; gap: 8px;
  margin-left: auto;
}
/* ---- The account control: initials in a circle ---------------
   Replaced a button whose visible text was the session's ROLES
   ("control_owner, program_manager") and whose only action was an
   immediate sign-out — a destructive act one click from the corner
   of every page, wearing a label that named neither itself nor its
   consequence.

   Now: the person's initials, opening a menu with their profile,
   their settings and sign-out. The tint is HASHED from the identity
   so the circle is recognisable rather than decorative, and stable
   across reloads. `hsl` with a computed hue is the one place in this
   stylesheet a colour is not a token — a per-person colour cannot
   be, and it is deliberately low-saturation so it reads as a
   surface rather than a status. */
.acct-wrap { position: relative; }
.acct {
  width: 32px; height: 32px; padding: 0;
  display: grid; place-items: center;
  border: 1px solid transparent; border-radius: 50%;
  background: none; color: var(--text-secondary);
  font-size: 12px; font-weight: 700; cursor: pointer;
  letter-spacing: .02em;
}
.acct.has-user {
  background: hsl(var(--tint, 220deg) 42% 92%);
  color: hsl(var(--tint, 220deg) 45% 30%);
  border-color: hsl(var(--tint, 220deg) 38% 82%);
}
.acct:hover { color: var(--text-primary); background: var(--surface-subtle); }
.acct.has-user:hover { border-color: var(--brand); }
/* Not `has-user` yet: the signed-out state is the word "Sign in",
   which needs a box rather than a circle. */
.acct:not(.has-user) {
  width: auto; height: 30px; padding: 0 9px;
  border-radius: var(--radius); font-weight: 400;
}

.acct-menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 40;
  min-width: 224px; padding: 6px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-panel); box-shadow: var(--shadow-overlay);
  display: grid; gap: 1px;
}
.acct-menu[hidden] { display: none; }
.acct-who {
  margin: 0; padding: 6px 10px 0;
  font-size: 13px; font-weight: 700; color: var(--text-primary);
}
.acct-roles {
  margin: 0; padding: 0 10px 6px; font-size: 12px;
  color: var(--text-secondary);
  border-bottom: 1px solid var(--line); margin-bottom: 5px;
}
.acct-link {
  display: flex; align-items: center; min-height: 40px;
  padding: 8px 10px; border: 0; border-radius: var(--radius);
  background: none; color: var(--text-primary);
  font: inherit; font-size: 13px; text-align: left; cursor: pointer;
  width: 100%;
}
.acct-link:hover {
  background: var(--surface-subtle); text-decoration: none;
}
/* Sign out is the one destructive item, so it sits below a rule and
   takes the danger ink on hover — not by default, which would make
   the menu look like a warning every time it opened. */
.acct-out {
  border-top: 1px solid var(--line); border-radius: 0 0
  var(--radius) var(--radius); margin-top: 5px;
}
.acct-out:hover { color: var(--danger-ink); background: var(--danger-tint); }

/* An icon-only control. Every one of these carries an
   aria-label — §14, and there is a browser check. */
.icon-btn {
  display: inline-flex; align-items: center;
  justify-content: center; gap: 7px;
  height: 36px; min-width: 36px; padding: 0 8px;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--surface); color: var(--text-secondary);
  cursor: pointer; transition: color var(--t-fast) ease-out,
    border-color var(--t-fast) ease-out;
}
.icon-btn:hover { color: var(--brand-dark); border-color: var(--brand); }
.icon-btn.quiet { border-color: transparent; background: none; }
.icon-btn.quiet:hover { background: var(--surface-subtle); }

/* ===================== LAYOUT =====================
   Three columns whose widths are tokens, so the collapse toggle,
   the Copilot resize and every breakpoint move the same two
   numbers instead of rewriting the template. */
.layout {
  display: grid;
  grid-template-columns:
    var(--nav-w) minmax(0, 1fr) var(--copilot-rail);
  height: calc(100vh - var(--topbar-h));
  transition: grid-template-columns var(--t-mid) ease-out;
}
/* The nav's two desktop states (§8): 224px expanded, 56px rail.
   `nav-rail`, not `nav-collapsed`: the rail is not a collapse,
   it is a second legible state, and the old name went with a
   template that set the column to 0 and hid the navigation
   outright.

   **Two classes, one presentation.** `nav-rail` is the user's
   stored preference; `nav-rail-auto` is the same state forced by
   a viewport between 720 and 1279px, where §11 prefers the icon
   navigation. They are separate so the forced one can be added
   and removed without touching the preference (§11: shell
   preferences degrade, they are not overwritten) — and they share
   every rule below, because a rail that is 56px wide while still
   painting 224px of group labels is not a state, it is a bug.
   That was the bug: the width was in a media query and the
   presentation was on the class. */
.layout.nav-rail, .layout.nav-rail-auto { --nav-w: var(--nav-w-rail); }
/* Copilot closed is the DEFAULT (§9) — the rail is 44px and the
   work area gets the rest. Open widens to the user's width. */
.layout.copilot-open { --copilot-rail: var(--copilot-w); }

/* ===================== PRIMARY NAVIGATION =====================
   Dark navy per §4. Structure, classes and ids are agent targets
   (cb_adapter SEL): `#sidebar`, `.nav-item`, `.nav-head`,
   `[data-view]`.

   REDESIGNED 2026-07-29 for progressive disclosure. The problem was
   not font size: every hierarchy signal was shouting at once —
   uppercase tracked headings, icons at both levels, every group
   expanded, hover underlines, chevrons, badges and selection
   backgrounds all competing, with roughly twenty rows visible. The
   fix is mostly the ACCORDION (one section open, in nav.js); the
   rules below stop the remaining signals fighting.

   Four deliberate reversals of what was here:

     * headings are TITLE CASE at 13px semibold, not uppercase at
       12px with .08em tracking. §6 permits uppercase for "short
       structural labels" — permits, not requires — and its stated
       default is sentence case. Tracked uppercase on ten headings
       is the loudest thing on the page;
     * child rows carry NO icon in this state. §8 wants icons as
       "supplemental cues"; at two levels in a 224px column they
       stop supplementing and start competing. Indentation and a
       left rule carry the hierarchy instead. The icons still exist
       and the 56px rail still draws them — see that block below;
     * 44px rows, up from 36. Dense-by-default (D-22) argued for 36
       when twenty rows were visible at once; with one section open
       the whole menu is shorter at 44 than it used to be at 36, and
       44 is the target floor §14 asks for;
     * no hover underline. `a:hover` underlines every link in the
       app, and a nav row is not a document link — it was the fourth
       treatment competing on the same element. */
.sidebar {
  background: var(--nav-bg);
  border-right: 1px solid var(--nav-hover);
  overflow-y: auto; overflow-x: hidden;
  padding: 10px 8px;
  display: flex; flex-direction: column;
}
/* 8px between sections, not a blank row. */
.nav-group + .nav-group,
.nav-group + .nav-solo, .nav-solo + .nav-group,
.nav-solo + .nav-solo { margin-top: 4px; }

/* A section heading and a solo destination are the SAME row shape.
   Home, My Work and the Agent Console are one-destination groups
   rendered as flat rows, and they must not look like a different
   species of thing from the sections above and below them. */
.nav-group > .nav-head, .nav-item.nav-solo {
  display: flex; align-items: center; gap: 10px; width: 100%;
  min-height: 44px;
  background: none; border: 0; cursor: pointer; text-align: left;
  padding: 8px 10px; border-radius: var(--radius);
  font-size: 13px; line-height: 18px; font-weight: 600;
  color: var(--nav-item-ink);
  letter-spacing: normal; text-transform: none;
}
.nav-head:hover, .nav-item.nav-solo:hover {
  color: var(--nav-text); background: var(--nav-hover);
  text-decoration: none;
}
.nav-head .lbl, .nav-item.nav-solo .lbl { flex: 1; min-width: 0; }
/* One chevron, quiet, and only where there is something to open. */
.nav-head .chev {
  color: var(--nav-muted); flex: none;
  transition: transform var(--t-mid);
}
.nav-head .chev svg { width: 16px; height: 16px; }
.nav-group:not(.collapsed) .chev { transform: rotate(90deg); }
.nav-group.collapsed .nav-children { display: none; }
/* The open section's children hang off a rule, which is the only
   thing carrying the nesting once the icons are gone. */
.nav-group:not(.collapsed) > .nav-children {
  margin: 2px 0 2px 21px; padding-left: 10px;
  border-left: 1px solid var(--nav-rule);
  display: grid; gap: 1px;
}

.nav-item {
  display: flex; align-items: center; gap: 10px;
  width: 100%; min-height: 44px;
  background: none; border: 0; cursor: pointer; text-align: left;
  padding: 7px 10px; border-radius: var(--radius);
  font-size: 14px; line-height: 20px; color: var(--nav-item-ink);
}
.nav-item .lbl { flex: 1; min-width: 0; }
.nav-item:hover {
  background: var(--nav-hover); color: var(--nav-text);
  text-decoration: none;
}
/* CHILD icons are hidden in this state and restored in the rail.
   `font-size: 0` is not used here — an icon contributes no text, so
   there is nothing to keep in the accessible tree, unlike a label. */
.nav-group .nav-children .nav-item > .ico { display: none; }

/* Selected: a tint and a 3px structural marker. §8 asks for "blue,
   text, and a structural marker" — the tint and ink are the blue,
   the weight is the text, the inset bar is the marker. No underline
   and no second background. */
.nav-item.sel {
  color: var(--nav-sel-ink); font-weight: 700;
  background: var(--nav-sel-bg);
  box-shadow: inset 3px 0 0 var(--brand);
}
/* ONE badge style, on a section and on a leaf alike: compact, low
   contrast, hard right. A section carries the sum of its children so
   a closed section still reports what is inside it. */
.nav-badge-slot { display: flex; flex: none; }
.badge {
  margin-left: auto; padding: 1px 7px; border-radius: 999px;
  background: var(--nav-hover); color: var(--nav-link);
  font-size: 12px; line-height: 16px; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.nav-item.sel .badge { background: var(--brand); color: var(--nav-text); }

/* ---- The footer utilities -----------------------------------
   `margin-top: auto` floats them to the bottom of the column. The
   Agent Console is here: the product's differentiator, kept
   permanently reachable and no longer presented as the
   application's purpose. It keeps a subtle accent — the link ink
   and a slightly brighter icon — and loses the card, the underline
   and the duplicate "ControlBridge Agent" heading above it. */
.nav-foot {
  margin-top: auto; padding-top: 8px;
  border-top: 1px solid var(--nav-rule);
}
.nav-item.nav-ext { color: var(--nav-link); font-weight: 600; }
.nav-item.nav-ext:hover {
  background: var(--nav-hover); color: var(--nav-text);
}
.nav-foot .nav-item > .ico { display: flex; }

/* ---- The 56px icon rail (§8) --------------------------------
   Labels are hidden VISUALLY and restored on hover and keyboard
   focus as a flyout. `font-size: 0` rather than `display: none`
   on the label span: the text stays in the accessible tree and
   in the element's textContent, which is what cb_adapter reads
   to name an agent target. Collapsing the rail must not change
   what the agent can reach. */
.layout.nav-rail .sidebar,
.layout.nav-rail-auto .sidebar { padding-inline: 6px; }
.layout.nav-rail .nav-head,
.layout.nav-rail-auto .nav-head {
  position: relative;
  /* A 1px rule cannot be hovered. The BOX stays 12px so the
     name is reachable; `background-clip: content-box` with
     symmetric padding paints only the middle 1px, so it still
     reads as a hairline. `overflow: visible` because the flyout
     escapes the box. */
  height: 12px; margin: 8px 6px; padding: 5px 0 6px;
  overflow: visible; font-size: 0;
  background: var(--nav-rule); background-clip: content-box;
}
/* The heading's own icon and chevron are hidden EXPLICITLY, not
   by clipping. This box used to be `height: 1px; overflow:
   hidden`, which hid them as a side effect — so the moment it
   grew to 12px with `overflow: visible` so the flyout could
   escape, both SVGs reappeared and the rail showed two icons per
   group with a hairline struck through them. `font-size: 0` does
   not shrink an SVG that carries its own width and height, and
   icons/icons.test.js requires that they always do (D-21). */
.layout.nav-rail .nav-head > .ico,
.layout.nav-rail-auto .nav-head > .ico { display: none; }
.layout.nav-rail .nav-group.collapsed .nav-children,
.layout.nav-rail-auto .nav-group.collapsed .nav-children { display: block; }
/* The child icons come BACK here, and this override is the whole
   reason the expanded tree can drop them: the 56px rail is an icon
   rail (§8) and children are all it has to draw. The hide rule
   above is more specific than the rail block, so restoring them
   needs saying explicitly rather than relying on order. */
.layout.nav-rail .nav-group .nav-children .nav-item > .ico,
.layout.nav-rail-auto .nav-group .nav-children .nav-item > .ico {
  display: flex;
}
.layout.nav-rail .nav-item,
.layout.nav-rail-auto .nav-item {
  position: relative;
  justify-content: center; gap: 0; padding: 7px 0; font-size: 0;
}
.layout.nav-rail .nav-item .lbl,
.layout.nav-rail-auto .nav-item .lbl { flex: none; }
/* The rail's corner badge was 9px, which is the same §6
   violation in the place hardest to read it. 12px fits: a
   two-digit count is ~14px of glyph plus 6px padding inside a
   56px rail. */
.layout.nav-rail .nav-item .badge,
.layout.nav-rail-auto .nav-item .badge {
  position: absolute; top: 0; right: 1px;
  min-width: 18px; padding: 0 4px;
  font-size: 12px; line-height: 16px; text-align: center;
}
/* The flyout label — §8 requires that a collapsed destination
   expose its full label on hover AND on keyboard focus.
   Headings get one too: a heading reduced to a hairline is
   otherwise a line between two runs of icons and which group
   you are in is a guess.

   **It is a real element on `<body>`, not a `::after`.** The
   first version was a pseudo-element positioned at
   `left: calc(100% + 8px)`, and it never appeared once: the
   `.sidebar` scrolls, so it carries `overflow-x: hidden`, and a
   scroll container clips its descendants no matter how they are
   positioned. `overflow-x: visible` is not available either — a
   box with `overflow-y: auto` computes the other axis to `auto`
   as well. So `shell.js` positions one fixed element outside the
   clip, which also keeps the label attached while the rail
   scrolls. Text is set with `textContent`, so the nav's own
   labels cannot become markup. */
.nav-flyout {
  position: fixed; z-index: 900;
  width: max-content; max-width: 220px;
  padding: 7px 9px;
  border: 1px solid var(--nav-rule); border-radius: var(--radius);
  color: var(--nav-text); background: var(--nav-bg);
  box-shadow: var(--shadow-overlay);
  font-size: 12px; font-weight: 600; line-height: 1.3;
  pointer-events: none;
  opacity: 0; transform: translate(4px, -50%);
  transition: opacity var(--t-fast) ease-out,
    transform var(--t-fast) ease-out;
}
.nav-flyout.on { opacity: 1; transform: translate(0, -50%); }

/* ===================== WORK AREA ===================== */
.main {
  overflow-y: auto;
  padding: 16px var(--gutter) 40px;
  min-width: 0;
}
.breadcrumb {
  color: var(--text-secondary); font-size: 12px;
  margin-bottom: 8px;
}
.breadcrumb b { color: var(--text-primary); font-weight: 600; }

/* ---- Page head (§3's hierarchy) -----------------------------
   Title, one-sentence state, and the primary action on the same
   line. The action belongs HERE and not at the end of the page:
   §2 puts the next action near the task. */
.page-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 16px; margin: 0 0 16px;
}
.page-head-text { min-width: 0; }
.page-head h1 {
  margin: 0; font-size: 20px; line-height: 28px; font-weight: 700;
  letter-spacing: -.02em;
}
.page-head p {
  margin: 3px 0 0; color: var(--text-secondary); font-size: 13px;
}
.page-head-actions { display: flex; gap: 8px; flex: none; }
.page-head-actions:empty { display: none; }

/* A section inside a page. §6: 16px/24px, 700 — a heading that
   does not compete with the page title for the working space. */
.section {
  margin: 24px 0 0;
}
.section > h2 {
  margin: 0 0 8px; font-size: 16px; line-height: 24px;
  font-weight: 700;
}
.section > p.section-sub {
  margin: -4px 0 10px; color: var(--text-secondary); font-size: 13px;
}
/* A short structural label. §6 limits uppercase to 12px with
   letter spacing, and this is the only place it is used. */
.section-label {
  margin: 16px 0 6px;
  color: var(--text-secondary);
  font-size: 12px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase;
}

/* ===================== OPERATIONAL SUMMARY =====================
   §3: a compact strip of four or five cells, not a mosaic of
   floating cards. One bordered surface, hairline-divided; every
   value drills into the records behind it, so each cell is a
   real <a>. */
.metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
  margin: 0 0 16px;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-panel);
  overflow: hidden;
}
.metric {
  display: block; padding: 10px 13px;
  border-right: 1px solid var(--line);
  color: inherit; text-decoration: none;
}
.metric:last-child { border-right: 0; }
a.metric:hover { background: var(--surface-subtle); text-decoration: none; }
a.metric:hover .metric-v { color: var(--brand-dark); }
.metric-v {
  display: block; font-size: 18px; line-height: 1.15;
  font-weight: 700; font-variant-numeric: tabular-nums;
}
.metric-l {
  display: block; margin-top: 2px;
  color: var(--text-secondary); font-size: 12px;
}
.metric.attention .metric-v { color: var(--warn-ink); }
.metric.problem .metric-v { color: var(--danger-ink); }

/* ===================== QUEUE-DETAIL WORKSPACE =====================
   §3: queue ~40%, detail ~60%, minimum 320px and 520px. One
   bordered surface with an internal divider rather than two
   floating panels — the selection has to stay visible while the
   detail is read, and two cards with a gap between them read as
   two pages. */
.workspace {
  display: grid;
  grid-template-columns: minmax(320px, .4fr) minmax(520px, .6fr);
  align-items: stretch;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-panel);
  overflow: hidden;
}
/* The mirror ratio, for Document Intelligence review (§10):
   results lead, source supports. Same primitive, opposite
   weighting — check which surface you are building. */
.workspace.mirrored {
  grid-template-columns: minmax(520px, .6fr) minmax(320px, .4fr);
}
/* Three panes, for account mapping (§ "Three-pane mapping
   workspace"). 40/35/25 — Original's ResizablePanelGroup ratio as
   a plain grid, because the ratio reflects how much text each
   pane carries and was never a thing a reader needed to drag.
   Below 1100px they stack, register first: it is the only one of
   the three that is useful on its own. */
.workspace.triptych {
  grid-template-columns:
    minmax(380px, .40fr) minmax(300px, .35fr) minmax(260px, .25fr);
}
@media (max-width: 1100px) {
  .workspace.triptych { grid-template-columns: 1fr; }
  .workspace.triptych .pane.queue-pane {
    border-right: 0; border-bottom: 1px solid var(--line);
  }
}
/* The register row: identifier, then name over its type and the
   two claims, then the figure. The sub-lines are blocks so the
   name keeps the column's full width — five columns in a 40%
   pane is what broke "Accounts Receivable" into four lines. */
.acct-name { display: block; font-weight: 600; }
.acct-claims { display: block; }
.workspace.triptych .tbl td { vertical-align: top; }
.workspace.triptych .tbl .num { white-space: nowrap; }
.pick { display: flex; align-items: center; gap: 8px; }
.bucket-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px;
}
.bucket-head h3, .bucket-head h4 { margin: 0; }
.fsli-statement {
  margin: 4px 0 8px; font-size: 13px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-secondary);
}
.fsli-total { font-weight: 600; margin: 4px 0; }
/* The tie-out, inside a statement-line card. Three figures on
   one row and a sentence beneath: the sentence is load-bearing,
   because a mis-signed line shows a difference of ZERO and the
   words are all that distinguish it from a clean row. */
.tie-out {
  margin-top: 8px; padding-top: 8px;
  border-top: 1px solid var(--line);
}
.tie-note { margin: 6px 0 0; font-size: 13px; }
.setline { display: flex; align-items: center; gap: 8px;
  flex-wrap: wrap; margin: 0 0 12px; }
.pane { min-width: 0; display: flex; flex-direction: column; }
.pane.queue-pane { border-right: 1px solid var(--line); }
.pane-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; min-height: 48px; padding: 8px 13px;
  background: var(--surface-subtle);
  border-bottom: 1px solid var(--line);
}
/* 16/24 — §6's section-title role, the same as `.section > h2`.
   At 14/20 the app had two sizes of section heading depending on
   which primitive drew it, which is a scale with a hole in it
   rather than a smaller heading. */
.pane-head h2, .pane-head h3 {
  margin: 0; font-size: 16px; line-height: 24px; font-weight: 700;
}
.pane-body { min-width: 0; padding: 14px 16px; overflow-wrap: anywhere; }
.pane-body.flush { padding: 0; }

/* ---- One queue row (§3) ------------------------------------
   Reading order is fixed by the contract: action-oriented title,
   identifier, due state or severity in words, reason, next
   action. A <button>, so the keyboard and the agent's DOM
   adapter both reach it. */
.queue { display: block; }
.queue-row {
  display: grid;
  grid-template-columns: 4px minmax(0, 1fr) auto;
  gap: 0 11px; align-items: center;
  width: 100%; min-height: 56px;
  padding: 8px 12px 8px 0;
  background: var(--surface); border: 0;
  border-bottom: 1px solid var(--line);
  text-align: left; color: var(--text-primary);
  cursor: pointer; font: inherit;
  transition: background var(--t-mid) ease-out;
}
.queue-row:hover { background: var(--surface-subtle); }
.queue-row.sel { background: var(--brand-tint); }
.queue-row:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
/* Selection is a tint AND a marker; focus is the ring above.
   §5 keeps the two distinct on purpose. */
.queue-mark { align-self: stretch; background: transparent; }
.queue-row.sel .queue-mark { background: var(--brand); }
.queue-main { min-width: 0; }
.queue-title {
  font-weight: 700; font-size: 13px; line-height: 18px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.queue-meta {
  display: flex; flex-wrap: wrap; gap: 4px 10px; margin-top: 2px;
  color: var(--text-secondary); font-size: 12px;
}
.queue-meta .id { font-variant-numeric: tabular-nums; }
.queue-next {
  align-self: center; flex: none;
  color: var(--brand-dark); font-size: 12px; font-weight: 700;
}

/* ===================== STATUS PILL =====================
   §8: a pill holds a short TEXT state. An icon or a colour may
   reinforce it; neither may replace it. */
.pill {
  display: inline-flex; align-items: center; gap: 5px;
  min-height: 22px; padding: 2px 8px; border-radius: 999px;
  border: 1px solid transparent;
  font-size: 12px; font-weight: 700; white-space: nowrap;
}
.pill.info { color: var(--brand-dark); background: var(--brand-tint);
  border-color: var(--brand-line); }
.pill.warn { color: var(--warn-ink); background: var(--warn-tint);
  border-color: var(--warn-line); }
.pill.danger { color: var(--danger-ink); background: var(--danger-tint);
  border-color: var(--danger-line); }
.pill.success { color: var(--success-ink); background: var(--success-tint);
  border-color: var(--success-line); }
.pill.neutral { color: var(--text-secondary); background: var(--surface-subtle);
  border-color: var(--line); }

/* `.tier` keeps its four class names. The standards use a
   `.pill-*` vocabulary and renaming would touch dom.js and every
   view that emits a tier — code churn dressed as styling. The
   mapping, for when it is done: eff→success, exc→danger,
   untested→warn, key→info. */
.tier {
  display: inline-flex; align-items: center;
  align-self: flex-start;
  min-height: 22px; padding: 2px 8px; border-radius: 999px;
  border: 1px solid transparent;
  font-size: 12px; font-weight: 700; white-space: nowrap;
}
.tier.eff { color: var(--success-ink); background: var(--success-tint);
  border-color: var(--success-line); }
.tier.exc { color: var(--danger-ink); background: var(--danger-tint);
  border-color: var(--danger-line); }
.tier.untested { color: var(--warn-ink); background: var(--warn-tint);
  border-color: var(--warn-line); }
.tier.key { color: var(--brand-dark); background: var(--brand-tint);
  border-color: var(--brand-line); }

/* A provenance mark (§9, §10): extracted / deterministic /
   AI-proposed / human-confirmed. Deliberately NOT colour-coded
   by "AI vs not" — §4 forbids AI-everywhere styling, and the
   distinction that matters is whether a human has confirmed it,
   which is the only one that gets the success tone. */
.prov {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 1px 7px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface-subtle);
  color: var(--text-secondary);
  font-size: 12px; font-weight: 600; white-space: nowrap;
}
.prov.confirmed { color: var(--success-ink); background: var(--success-tint);
  border-color: var(--success-line); }
.prov.proposed { color: var(--warn-ink); background: var(--warn-tint);
  border-color: var(--warn-line); }

/* ===================== DECISION CALLOUT =====================
   §8: heading, plain-language reason, consequence or deadline,
   the primary decision action, and the way through to the basis.
   Amber rather than red by default — the record is valid and a
   person is being asked to decide, not to clear an error. */
.decision {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: 12px; align-items: center;
  margin: 0 0 14px; padding: 12px 13px;
  background: var(--warn-tint);
  border: 1px solid var(--warn-line);
  border-left: 4px solid var(--warn);
  border-radius: var(--radius-panel);
}
.decision.blocking {
  background: var(--danger-tint); border-color: var(--danger-line);
  border-left-color: var(--danger);
}
.decision h2, .decision h3 {
  margin: 0 0 3px; font-size: 14px; line-height: 20px; font-weight: 700;
}
.decision p { margin: 0; color: var(--text-secondary); font-size: 13px; }
.decision-acts { display: flex; flex-direction: column; gap: 6px; flex: none; }

/* ===================== RECORD SUMMARY =====================
   §8: owner, due state, lifecycle state, evidence completeness,
   material identifiers — and nothing else. One bordered strip of
   labelled facts, so the label travels with its value when the
   stylesheet is gone. */
.facts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  margin: 0 0 4px;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-panel);
  overflow: hidden;
}
.fact {
  padding: 9px 11px; border-right: 1px solid var(--line);
  min-width: 0;
}
.fact:last-child { border-right: 0; }
.fact dt, .fact .k {
  display: block; color: var(--text-secondary);
  font-size: 12px; font-weight: 500;
}
.fact dd, .fact .v {
  display: block; margin: 3px 0 0;
  font-size: 13px; font-weight: 600; overflow-wrap: anywhere;
}

/* ---- The basis (§2: status, basis and action stay together) --
   A definition list, because that is what it is: the rule, the
   evidence or the event that produced the state, each named. */
.basis { margin: 0; }
.basis > div {
  display: grid; grid-template-columns: 116px minmax(0, 1fr);
  gap: 6px 14px; padding: 9px 0;
  border-top: 1px solid var(--line); font-size: 13px;
}
.basis > div:first-child { border-top: 0; }
.basis dt { color: var(--text-secondary); }
.basis dd { margin: 0; overflow-wrap: anywhere; }

/* ===================== BUTTONS =====================
   §8: one primary per decision region. Labels begin with a verb;
   the CSS cannot enforce that but the string catalogue can, and
   does. */
button.btn, a.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 7px; min-height: 36px; padding: 7px 13px;
  border: 1px solid transparent; border-radius: var(--radius);
  background: var(--brand); color: var(--surface);
  font-size: 13px; font-weight: 700; cursor: pointer;
  text-decoration: none;
  transition: background var(--t-fast) ease-out,
    border-color var(--t-fast) ease-out, color var(--t-fast) ease-out;
}
button.btn:hover, a.btn:hover {
  background: var(--brand-dark); text-decoration: none;
}
button.btn:disabled { opacity: .55; cursor: default; }
/* Secondary — equal safety, lower priority. */
button.btn.ghost, a.btn.ghost {
  background: var(--surface); color: var(--text-primary);
  border-color: var(--line-strong);
}
button.btn.ghost:hover, a.btn.ghost:hover {
  background: var(--surface-subtle); color: var(--brand-dark);
  border-color: var(--brand);
}
/* Quiet / link — inspect, reveal, navigate. */
button.btn.quiet {
  background: none; color: var(--brand-dark);
  border-color: transparent; font-weight: 700;
}
button.btn.quiet:hover { background: var(--brand-tint); }
/* Danger — destructive or materially consequential. Outlined,
   not filled: a filled red button invites the accidental press
   it exists to warn about. */
button.btn.danger {
  background: var(--surface); color: var(--danger-ink);
  border-color: var(--danger-line);
}
button.btn.danger:hover {
  background: var(--danger-tint); border-color: var(--danger);
}
button.btn.sm { min-height: 30px; padding: 4px 10px; font-size: 12px; }
button.btn.lg { min-height: 40px; padding: 9px 16px; font-size: 14px; }

/* ---- Filters and facets ------------------------------------
   §8: filtering must be predictable and the active filter must
   stay visible. The count travels inside the label so it
   survives with the stylesheet off (D-21). */
.facets, .toolbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin: 0 0 12px; padding: 0;
}
.pane-head .facets, .pane-head .toolbar { margin: 0; }
.facet, .chip {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 30px; padding: 4px 10px;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--surface); color: var(--text-secondary);
  font-size: 12px; cursor: pointer;
}
.facet:hover, .chip:hover {
  border-color: var(--brand); color: var(--brand-dark);
}
.facet.on, .chip.on {
  color: var(--brand-dark); background: var(--brand-tint);
  border-color: var(--brand-line); font-weight: 700;
}
.facet-n { font-variant-numeric: tabular-nums; opacity: .75; }
.facet.on .facet-n { opacity: .9; }

select.filter, input.filter {
  min-height: 36px; padding: 6px 10px;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--surface); color: var(--text-primary);
  font-size: 13px;
}
select.filter:hover, input.filter:hover { border-color: var(--brand); }

/* ===================== PANELS =====================
   `.panel-box` is an agent target (cb_adapter SEL). Flat: a
   border, 8px, no shadow (§7). */
.panel-box {
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-panel);
  padding: 14px 16px; margin: 0 0 12px;
}
.panel-box > h3 {
  margin: 0 0 10px; font-size: 14px; line-height: 20px; font-weight: 700;
}
.panel-box > :last-child { margin-bottom: 0; }
.kv { display: grid; grid-template-columns: 150px minmax(0, 1fr);
  gap: 7px 14px; font-size: 13px; }
.kv .k { color: var(--text-secondary); }

/* ---- Record cards -------------------------------------------
   `.grid` + `.card` are agent targets: cb_adapter's DYNAMIC
   search boundary reads `#view .card`, and main.js's global
   search filters them. So the DOM stays and the LOOK changes —
   these are now full-width flat record rows in a single column,
   not the 240px mosaic §4 forbids. One column at every width, on
   purpose: a two-up grid of records is a mosaic with better
   manners. */
.grid { display: grid; gap: 0; grid-template-columns: 1fr;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-panel);
  overflow: hidden; }
.grid:empty { display: none; }
.card {
  display: grid; grid-template-columns: 22px minmax(0, 1fr) auto;
  gap: 12px; align-items: center;
  padding: 11px 14px;
  background: var(--surface);
  border: 0; border-bottom: 1px solid var(--line);
}
.card:last-child { border-bottom: 0; }
.card:hover { background: var(--surface-subtle); }
.card .thumb {
  display: flex; align-items: center; justify-content: center;
  color: var(--text-secondary);
}
.card .body { min-width: 0; display: block; }
.card h3 {
  margin: 0; font-size: 13px; line-height: 18px; font-weight: 700;
  overflow-wrap: anywhere;
}
.card .sub {
  color: var(--text-secondary); font-size: 12px; margin-top: 2px;
}
.card .tier { margin: 0 0 4px; }
.card .foot { display: flex; align-items: center; gap: 8px; flex: none; }

/* ===================== FORMS =====================
   §8's Forms contract, rendered by [core/form.js](core/form.js).
   Six rules, and the ones this stylesheet is responsible for:

   * the LABEL is a block above its control and always visible —
     never a placeholder, which disappears exactly when the
     constraint is needed;
   * the HINT is permanent body text under the control, so a
     constraint or example is readable before submission and does
     not move the layout when it becomes an error;
   * an error recolours the hint IN PLACE. Nothing is inserted,
     so a field turning invalid does not shift every field below
     it — the reflow is what makes people lose their place;
   * a max width on the control, because a 900px-wide text input
     for a two-character answer reads as the wrong field.

   No new token values (§5/§6 are held to this file token by
   token by design_tokens_test.py) — every colour here is an
   existing custom property. */
.form { max-width: 720px; }
.field-group {
  border: 1px solid var(--line); border-radius: var(--radius-panel);
  background: var(--surface); padding: 4px 16px 14px;
  margin: 0 0 12px; min-width: 0;
}
.field-group > legend {
  padding: 0 6px; font-size: 14px; line-height: 20px;
  font-weight: 700; color: var(--text-primary);
}
.group-note {
  margin: 6px 0 12px; font-size: 13px; line-height: 19px;
  color: var(--text-secondary);
}
.field-plain { min-width: 0; }
.field { margin: 0 0 14px; }
.field > label {
  display: block; font-size: 13px; line-height: 18px;
  font-weight: 600; margin-bottom: 4px;
}
.field .req { color: var(--danger-ink); }
.field input, .field select, .field textarea {
  width: 100%; max-width: 420px; box-sizing: border-box;
  font: inherit; font-size: 13px; padding: 7px 9px;
  color: var(--text-primary); background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  transition: border-color var(--t-fast) ease-out;
}
.field textarea { max-width: 640px; resize: vertical; }
.field input:hover, .field select:hover, .field textarea:hover {
  border-color: var(--brand);
}
.field input[readonly], .field textarea[readonly] {
  background: var(--surface-subtle); color: var(--text-secondary);
}
.field .hint {
  margin: 4px 0 0; font-size: 12px; line-height: 17px;
  color: var(--text-secondary);
}
/* The error IS the hint, recoloured — see the section note. */
.field .hint.err { color: var(--danger-ink); font-weight: 600; }
.field.bad input, .field.bad select, .field.bad textarea {
  border-color: var(--danger-ink);
}
/* Rule 6: a value a machine proposed must not look like one a
   person asserted. */
.field .proposed {
  display: flex; align-items: center; gap: 5px;
  margin: 5px 0 0; font-size: 12px; line-height: 17px;
  color: var(--text-secondary);
}
.field .proposed .ico { display: inline-flex; }
.field .proposed .ico svg { width: 13px; height: 13px; }
.form-errors {
  margin: 0 0 12px; padding: 10px 12px; font-size: 13px;
  line-height: 19px; font-weight: 600;
  color: var(--danger-ink); background: var(--danger-tint);
  border: 1px solid var(--danger-line);
  border-radius: var(--radius-panel);
}
.form-foot { display: flex; gap: 8px; flex-wrap: wrap; }

/* A multi-select, as checkboxes. Two columns on a wide viewport
   because ten single-file rows push the rest of the step below the
   fold — and one column below 640px, where two would each be too
   narrow to read. */
.checkgroup { border: 0; padding: 0; margin: 0 0 14px; min-width: 0; }
.checkgroup > legend {
  padding: 0; font-size: 13px; line-height: 18px; font-weight: 600;
  margin-bottom: 4px;
}
.checkgroup > .hint { margin: 0 0 8px; }
.check {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  gap: 8px; align-items: start;
  padding: 7px 9px; margin: 0 0 4px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); font-size: 13px; line-height: 18px;
  cursor: pointer; transition: border-color var(--t-fast) ease-out;
}
.check:hover { border-color: var(--brand); }
/* `accent-color` rather than a hand-drawn box: the native control
   keeps its platform focus ring and its 44px touch target.

   `width: auto` is load-bearing and not a tidy-up. A checkgroup
   also carries `.field`, so `.field input { width: 100% }` applied
   here and made every checkbox a full-width row — which pushed its
   own label onto the next line and centred the box above the text.
   The rule is right for a text input and wrong for a checkbox. */
.check input {
  accent-color: var(--brand); margin: 1px 0 0;
  width: auto; max-width: none;
}
.check-note {
  display: block; color: var(--text-secondary); font-size: 12px;
  line-height: 17px;
}
@media (min-width: 641px) {
  .checkgroup { display: grid; grid-template-columns: 1fr 1fr;
    gap: 0 10px; }
  .checkgroup > legend, .checkgroup > .hint,
  .checkgroup > .proposed { grid-column: 1 / -1; }
}

/* ===================== SETUP WIZARD =====================
   M-10 f2. The "guided discovery" the personas doc names as the
   dominant need in a first controls build.

   The rail is a real <ol> of buttons, not a decorative stepper:
   somebody who realises on step 5 that they mistyped the fiscal
   year end must not have to press Back four times. `aria-current`
   carries which step is live, so the state is not colour alone. */
.wiz-progress {
  display: flex; align-items: center; gap: 12px; margin: 0 0 10px;
}
.wiz-count {
  margin: 0; font-size: 13px; font-weight: 600; flex: none;
  color: var(--text-secondary); font-variant-numeric: tabular-nums;
}
.wiz-bar {
  flex: 1; height: 6px; min-width: 80px;
  background: var(--line); border-radius: 999px; overflow: hidden;
}
.wiz-bar > span {
  display: block; height: 100%; background: var(--brand);
  transition: width var(--t-mid) ease-out;
}
.dirty {
  margin: 0 0 12px; padding: 7px 11px;
  font-size: 12px; line-height: 17px; font-weight: 600;
  color: var(--warn-ink); background: var(--warn-tint);
  border: 1px solid var(--warn-line); border-radius: var(--radius);
}
.wiz-rail { margin: 0 0 16px; }
/* ---- The phase rail (four chapters, six screens) ------------
   Nested ordered lists: a sequence of phases each holding a
   sequence of screens, which is what this is. Every row carries its
   state as TEXT — visibly for a phase, screen-reader-only for a
   screen — so nothing here means anything by colour alone (§14).
   The left border is the only decorative signal and it is
   redundant with that text by design. */
.wiz-rail ol { list-style: none; margin: 0; padding: 0; }
.ph { padding: 0 0 4px 10px; margin: 0 0 10px;
  border-left: 3px solid var(--line); }
.ph.current { border-left-color: var(--brand); }
.ph.complete { border-left-color: var(--success); }
.ph.attention { border-left-color: var(--warn); }
.ph-head { display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 4px 8px; margin: 0 0 6px; }
.ph-n { font-size: 11px; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-muted);
  flex: 0 0 100%; }
.ph-name { font-size: 13px; line-height: 18px; font-weight: 700; }
.ph-state { font-size: 11px; color: var(--text-secondary); }
.ph.attention .ph-state { color: var(--warn-ink); font-weight: 600; }
.ph.complete .ph-state { color: var(--success-ink); }
.ph-screens { display: grid; gap: 3px; }

/* A screen that is not yet available stays VISIBLE and disabled —
   one that vanished would hide what is still coming, and one with
   no stated reason reads as a bug. The reason is in `title` and in
   the screen-reader text. */
.wiz-step:disabled {
  opacity: .55; cursor: not-allowed; border-style: dashed;
}
.wiz-step:disabled:hover { border-color: var(--line); }

/* The compact header, below 720px: phase of how many, its name,
   this screen and its place within the phase. */
.wiz-here { display: none; margin: 0 0 12px; }
.wiz-here p { margin: 0; }
.wh-phase { font-size: 11px; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-muted); }
.wh-name { font-size: 16px; line-height: 22px; font-weight: 700; }
.wh-screen { font-size: 13px; color: var(--text-secondary); }
.wh-within { font-variant-numeric: tabular-nums; }
.wiz-here .btn { margin-top: 8px; }
@media (min-width: 961px) {
  /* A vertical rail, so six meaningful labels cannot wrap with one
     orphaned on a second line — which is what the horizontal pills
     did at 1280px. The answer region keeps §8's readable measure. */
  .wiz-rail ol { flex-direction: column; gap: 4px; }
  .wiz-step { width: 100%; text-align: left;
    border-radius: var(--radius); }
  .wiz-cols {
    display: grid; grid-template-columns: 232px minmax(0, 1fr);
    gap: 24px; align-items: start;
  }
  .wiz-cols > .wiz-rail { margin: 0; }
  /* §8's readable measure. Without the cap the answer column grows
     to the window and a one-line label sits 900px from its field. */
  .wiz-cols > .wiz-body { max-width: 720px; }
  /* Forms keep the readable measure above. The document branch is
     a record workspace: its repeated filenames, provenance and
     filing status need the width the shell already provides. */
  .wiz-cols > .wiz-body.is-document-workspace { max-width: none; }
}
.wiz-rail ol {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 6px;
}
.wiz-step {
  font: inherit; font-size: 12px; line-height: 17px;
  padding: 5px 10px; cursor: pointer;
  color: var(--text-secondary); background: var(--surface);
  border: 1px solid var(--line); border-radius: 999px;
  transition: border-color var(--t-fast) ease-out;
}
.wiz-step:hover { border-color: var(--brand); }
/* The four states, renamed with the phase model. These selectors
   went stale when `done`/`on` became `complete`/`current` and the
   rail rendered four identical-looking rows for a while — the
   states were correct in the DOM and invisible on screen. Colour is
   never the only signal: every row also carries its state as
   screen-reader text, and a phase shows it visibly. */
.wiz-step.complete {
  color: var(--success-ink); border-color: var(--success-line);
}
.wiz-step.attention {
  color: var(--warn-ink); border-color: var(--warn-line);
  font-weight: 600;
}
.wiz-step.current {
  color: var(--nav-sel-ink); background: var(--brand);
  border-color: var(--brand); font-weight: 700;
}
.wiz-step.todo { color: var(--text-secondary); }
/* The two paths at the fork. Equal weight in layout — the
   recommendation is carried by the copy and the button emphasis,
   not by making one card larger. */
.wiz-paths {
  display: grid; gap: 12px; grid-template-columns: 1fr;
  margin: 0 0 14px;
}
@media (min-width: 641px) {
  .wiz-paths { grid-template-columns: 1fr 1fr; }
}
/* The SEC path is a PEER of the other two but sits in its own
   grid, one column wide, because it carries an input and a
   confirmation step that the other cards do not. Side by side at
   equal width it would either be cramped or force the other two
   to match its height for content they do not have. Stacked
   above, tinted, it reads as the recommended route without
   pretending to be the same shape. */
.wiz-paths-lead { grid-template-columns: 1fr; }
@media (min-width: 641px) {
  .wiz-paths-lead { grid-template-columns: 1fr; }
}
.wiz-paths-lead .wiz-path {
  background: var(--brand-tint); border-color: var(--brand-line);
}
/* Label, input and button on one line, wrapping to stacked on a
   narrow viewport rather than overflowing. */
.row-inline {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  margin-top: auto;
}
.row-inline label { font-size: 13px; }
.inp-sm {
  width: 8em; padding: 6px 9px; font: inherit; font-size: 13px;
  color: var(--text-primary); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius);
}
.inp-sm:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 1px;
}
.sec-found {
  margin-top: 10px; padding: 10px 12px; background: var(--surface);
  border: 1px solid var(--brand-line);
  border-radius: var(--radius-panel);
}
.sec-found p { margin: 0 0 6px; }
/* Column layout so the two cards' buttons sit on one line: the
   grid already stretches both cards to the taller one's height,
   and `margin-top: auto` on the button drops it to the bottom of
   each. Without it the button follows its own paragraph, so the
   longer body text pushes one button below the other. */
.wiz-path {
  background: var(--surface); padding: 14px 16px;
  border: 1px solid var(--line); border-radius: var(--radius-panel);
  display: flex; flex-direction: column; align-items: flex-start;
}
.wiz-path h3 {
  margin: 0 0 6px; font-size: 14px; line-height: 20px; font-weight: 700;
}
.wiz-path p {
  margin: 0 0 12px; font-size: 13px; line-height: 19px;
  color: var(--text-secondary);
}
.wiz-path .btn { margin-top: auto; }

/* ============================================================
   THE DISCLOSURE — core/collapse.js
   ============================================================
   Replaces Original's Radix Accordion/Collapsible. A button and a
   region; the chevron rotates and the region is `hidden`, so
   nothing here is doing the hiding — CSS cannot leave content in
   the tab order by accident. */
.cl {
  border: 1px solid var(--line); border-radius: var(--radius-panel);
  background: var(--surface); margin: 0 0 8px;
}
.cl-head { margin: 0; font-size: inherit; font-weight: inherit; }
.cl-trigger {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 11px 13px; border: 0; border-radius: var(--radius-panel);
  background: none; color: var(--text-primary); cursor: pointer;
  font: inherit; text-align: left;
  transition: background var(--t-fast) ease-out;
}
.cl-trigger:hover { background: var(--surface-subtle); }
.cl-title { flex: 1; font-size: 14px; font-weight: 700; }
/* The count is the reason the accordion works: "have I done this
   one?" is answerable without opening it. Tabular figures so a
   column of them does not jitter. */
.cl-count {
  font-size: 12px; color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
.cl-badge {
  font-size: 11px; line-height: 16px; padding: 1px 7px;
  border-radius: 999px; border: 1px solid var(--line);
  color: var(--text-secondary); background: var(--surface-subtle);
}
.cl-badge.bad {
  color: var(--danger-ink); background: var(--danger-tint);
  border-color: var(--danger-line);
}
.cl-chev {
  display: inline-flex; color: var(--text-secondary);
  transition: transform var(--t-fast) ease-out;
}
.cl.open > .cl-head .cl-chev { transform: rotate(180deg); }
.cl-region { padding: 0 13px 13px; }

/* ============================================================
   THE MULTISELECT — core/form.js
   ============================================================
   Original's MultiSystemPicker: a summary, a searchable list, a
   write-in, and the chosen names as chips OUTSIDE the panel — the
   chips are what make the answer readable without opening it. */
.ms { display: grid; gap: 8px; }
.ms-trigger {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; width: 100%; padding: 8px 10px;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--surface); color: var(--text-primary);
  font: inherit; font-size: 13px; cursor: pointer; text-align: left;
}
.ms-trigger:hover { border-color: var(--brand); }
.ms-summary { overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.ms-chev { display: inline-flex; color: var(--text-secondary); }
.ms-panel {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface-subtle); padding: 9px;
}
.ms-search { display: block; margin: 0 0 8px; }
.ms-q, .ms-new {
  width: 100%; padding: 7px 9px; font: inherit; font-size: 13px;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--surface); color: var(--text-primary);
}
/* Capped and scrollable: a category with a hundred and twenty
   vendors must not push the application cards below it off the
   page. This is the whole reason the kind exists — the list is too
   long to show, so `checkboxes` would not do. */
.ms-list { max-height: 232px; overflow-y: auto; }
.ms-empty {
  margin: 6px 0 0; font-size: 12px; color: var(--text-secondary);
}
.ms-add {
  display: flex; gap: 6px; align-items: center;
  margin-top: 9px; padding-top: 9px;
  border-top: 1px solid var(--line);
}
.ms-add .btn { flex-shrink: 0; }
.ms-chips { display: flex; flex-wrap: wrap; gap: 5px; }
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 8px; font: inherit; font-size: 12px; line-height: 18px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface-subtle); color: var(--text-secondary);
  cursor: pointer;
}
.chip:hover {
  color: var(--danger-ink); background: var(--danger-tint);
  border-color: var(--danger-line);
}
.chip svg { width: 12px; height: 12px; }

/* The "Other" write-in beside a select. DISABLED rather than
   hidden — see core/form.js — so it needs a disabled look rather
   than a display rule. */
.field-other { margin-top: 6px; }
.field-other:disabled {
  background: var(--surface-subtle); color: var(--text-secondary);
  cursor: not-allowed;
}

/* ============================================================
   THE APPLICATIONS SCREEN — views/company/landscape.js
   ============================================================ */
.ls-cat {
  padding: 11px 0 3px; border-top: 1px solid var(--line);
}
.ls-cat:first-of-type { border-top: 0; }
.ls-cat h4 {
  margin: 0 0 8px; font-size: 13px; line-height: 18px;
  font-weight: 700;
}
.ls-cat .form-foot { display: none; }
/* The cards and the save bar are redrawn on their own; the picker
   above them never is. That is what keeps the panel open, the
   search text intact and focus where the person put it while three
   banks are ticked — see landscape.js's showCategory. */
.ls-cards, .ls-foot { display: block; }
/* One card per application, indented so it reads as belonging to
   the category above rather than sitting beside it. */
.ls-sys {
  margin: 8px 0 0; padding: 10px 12px;
  border: 1px solid var(--line); border-left: 3px solid var(--line-strong);
  border-radius: var(--radius); background: var(--surface-subtle);
}
.ls-sys.bad {
  border-color: var(--danger-line);
  border-left-color: var(--danger);
  background: var(--danger-tint);
}
.ls-sys-name {
  margin: 0 0 8px; font-size: 13px; line-height: 18px;
  font-weight: 700;
}
.ls-save {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin: 10px 0 0; padding: 9px 11px;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--surface);
}
.ls-save p {
  margin: 0; flex: 1; font-size: 12px; color: var(--text-secondary);
}
.ls-save .form-errors { flex-basis: 100%; }

/* ============================================================
   THE LOGO — views/company/logo.js
   ============================================================
   The monogram is ALWAYS rendered and the image sits over it, so
   an image that fails to load degrades to two initials rather
   than to an empty box that reads as a bug. */
.logo-field { margin-bottom: 14px; }
.logo-label {
  margin: 0 0 6px; font-size: 13px; line-height: 18px;
  font-weight: 600; color: var(--text-primary);
}
.logo-row { display: flex; align-items: center; gap: 12px; }
.logo-avatar {
  position: relative; flex-shrink: 0;
  display: inline-grid; place-items: center;
  width: 56px; height: 56px; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--radius-panel);
  background: var(--surface-subtle);
}
.logo-mono {
  font-size: 18px; font-weight: 700; letter-spacing: .02em;
  color: var(--text-secondary);
}
.logo-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: contain;
  background: var(--surface);
}
.logo-acts { display: flex; flex-wrap: wrap; gap: 6px; }

/* The context-quality meter. A real measurement of a stored row —
   Original's recomputed from browser state it never persisted, so
   it read ~25% on every second visit. */
.quality {
  background: var(--surface); padding: 12px 14px; margin: 0 0 12px;
  border: 1px solid var(--line); border-radius: var(--radius-panel);
}
.quality-head {
  display: flex; align-items: baseline; gap: 8px; margin-bottom: 8px;
  font-size: 13px; color: var(--text-secondary);
}
.quality-n {
  font-size: 20px; line-height: 24px; font-weight: 700;
  color: var(--text-primary); font-variant-numeric: tabular-nums;
}
.quality-parts {
  list-style: none; margin: 10px 0 0; padding: 0;
  display: grid; gap: 3px 14px; font-size: 12px; line-height: 18px;
  color: var(--text-secondary);
}
@media (min-width: 641px) {
  .quality-parts { grid-template-columns: 1fr 1fr; }
}
.quality-parts .done { color: var(--success-ink); }

/* ===================== COLLAPSIBLE CATEGORIES =====================
   M-10 f3. `<details>`/`<summary>`, not a JS accordion: native gets
   keyboard operation, the correct disclosure announcement, and
   readability with the stylesheet removed (D-21) for free — and a
   scripted one has to earn all three back.

   `list-style: none` + the drawn marker because the default
   triangle sits outside the row and cannot be aligned with the
   text; `::marker` is unstyleable across engines. */
.cat-list { display: grid; gap: 8px; }
.cat {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-panel); overflow: hidden;
}
.cat > summary {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 14px; cursor: pointer; list-style: none;
  font-size: 14px; line-height: 20px;
  transition: background var(--t-fast) ease-out;
}
.cat > summary::-webkit-details-marker { display: none; }
.cat > summary::before {
  content: ""; flex: none; width: 8px; height: 8px;
  border-right: 2px solid var(--text-secondary);
  border-bottom: 2px solid var(--text-secondary);
  transform: rotate(-45deg);
  transition: transform var(--t-fast) ease-out;
}
.cat[open] > summary::before { transform: rotate(45deg); }
.cat > summary:hover { background: var(--surface-subtle); }
.cat-name { font-weight: 700; flex: 1; min-width: 0; }
.cat-count {
  flex: none; font-size: 12px; color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
.cat-body { padding: 0 14px 14px; }
.cat-note {
  margin: 0 0 10px; font-size: 13px; line-height: 19px;
  color: var(--text-secondary);
}

/* ---- The review's read-back (CS-03) -------------------------
   One section per wizard step, each with its own Edit. The heading
   and the action share a row: the action belongs TO the section,
   and stacked above the fields it read as a page-level button. */
.ans {
  background: var(--surface); padding: 12px 14px; margin: 0 0 10px;
  border: 1px solid var(--line); border-radius: var(--radius-panel);
}
.ans-head {
  display: flex; align-items: center; gap: 12px; margin-bottom: 10px;
}
.ans-head h3 {
  margin: 0; flex: 1; min-width: 0;
  font-size: 14px; line-height: 20px; font-weight: 700;
}
.ans-head .btn { flex: none; }
.ans .kv-list { margin: 0; }

/* ---- The switch ---------------------------------------------
   A `role="switch"` button, so Space and Enter work and a screen
   reader announces two states. Drawn rather than a styled
   checkbox because the knob has to travel, and `appearance: none`
   on a checkbox loses the platform focus ring this keeps. */
.sw {
  display: inline-flex; align-items: center; gap: 0;
  padding: 4px; margin: 0; border: 0; background: none;
  cursor: pointer; border-radius: 999px;
}
.sw-track {
  display: block; position: relative;
  width: 36px; height: 20px; border-radius: 999px;
  background: var(--line-strong);
  transition: background var(--t-mid) ease-out;
}
.sw-knob {
  position: absolute; top: 2px; left: 2px;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--surface); box-shadow: var(--shadow-panel, none);
  transition: transform var(--t-mid) ease-out;
}
.sw[aria-checked="true"] .sw-track { background: var(--success); }
.sw[aria-checked="true"] .sw-knob { transform: translateX(16px); }
.sw:hover .sw-track { outline: 2px solid var(--brand-line); }
/* The state must not be colour alone (§14). The row's Status pill
   carries the word, and the switch carries an sr-only label. */
.sw-static { font-size: 12px; color: var(--text-secondary); }

/* ===================== TABLES =====================
   §8: repeated comparable records only. The primary identifier is
   visually strongest, numbers align, row actions sit in a
   predictable place. Dense by default (D-22). */
.tbl-wrap { margin: 0; }
.tbl-actions {
  display: flex; gap: 8px; flex-wrap: wrap;
  justify-content: flex-end; margin-bottom: 8px;
}
table.tbl {
  width: 100%; border-collapse: collapse; font-size: 13px;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-panel);
  overflow: hidden;
}
table.tbl caption {
  text-align: left; padding: 0 0 8px;
  font-size: 12px; color: var(--text-secondary);
}
/* §8: "retain row actions in a predictable final column". Right
   aligned so the column reads as a column even when some rows
   have no action — and `width: 1%` lets the browser shrink it to
   its content instead of stealing space from the record. */
table.tbl .row-act { text-align: right; white-space: nowrap; }
table.tbl:not(.fixed) td.row-act { width: 1%; }

/* ---- Aligned columns across SEPARATE tables ------------------
   §8 wants the primary identifier strongest, numbers aligned and
   row actions in a predictable final column. On one table the
   browser's auto-sizing gets there by itself; across FOURTEEN
   category tables it does not — each measures its own content, so
   "Vendor" lands in fourteen different places and the eye has to
   re-find every column each time a category is opened.

   `table-layout: fixed` plus a shared `<colgroup>` fixes the edges
   once. The percentages total 100 and are ordered widest-content
   first, so `Description` gets the room and the two narrow columns
   do not stretch. */
table.tbl.fixed { table-layout: fixed; }
table.tbl.fixed .c-name { width: 22%; }
table.tbl.fixed .c-vendor { width: 12%; }
table.tbl.fixed .c-desc { width: 40%; }
table.tbl.fixed .c-date { width: 14%; }
table.tbl.fixed .c-act { width: 12%; }
/* A fixed layout will not shrink a header to fit, so a column
   narrower than its own uppercase, letter-spaced heading overflows
   INTO THE NEXT ONE — which rendered as "ACTIVATION DATBSYSTEM".
   Column headers therefore wrap, like the body cells already do. */
table.tbl.fixed th[scope="col"] {
  overflow-wrap: anywhere; white-space: normal;
}
/* And the ROW header must wrap too. `tbody th[scope="row"]` is
   `nowrap` by default — right on an auto-sized table where the
   column grows to fit, wrong on a fixed one, where "Microsoft
   Dynamics 365" simply ran into the Vendor column beside it. */
table.tbl.fixed tbody th[scope="row"] {
  white-space: normal; overflow-wrap: anywhere;
}
/* §8: numbers align. A date column is a number column for this
   purpose — right-aligned and tabular so the years form a line. */
table.tbl td.num, table.tbl th.num {
  text-align: right; font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* The connector brand mark (core/logo.js, ADR-M10-logos-restored).

   Two children in one 26px box, deliberately stacked rather than
   swapped: `.logo-mono` is the monogram and `.logo-img` is the
   fetched brand mark, and the image is HIDDEN until it has
   actually loaded. That ordering is what keeps all three states
   presentable — no token configured (no <img> is emitted at all),
   fetch failed (the <img> is removed), loaded (`has-logo` hides
   the monogram) — and is why nothing here ever shows a browser's
   broken-image glyph.

   `object-fit: contain` because brand marks are not square: a
   wordmark stretched to a 26px box is a defaced logo.

   **The image is hidden with `opacity`, NEVER with
   `display: none`** — and that is not a style preference, it is
   the difference between working and not. An image with no
   layout box is one the browser will not fetch, so it never
   loads, so `has-logo` is never added, so it stays hidden: a
   deadlock that presents as "the logos silently never appear".
   Verified in Chromium — the first cut of this rule used
   `display: none` and the page made **zero** requests with a
   token configured. `opacity: 0` keeps the box.

   **That fix was necessary and not sufficient, which is worth
   knowing before trusting this comment.** The page still made
   zero requests, because the same no-box condition also comes
   from the 13 CLOSED `<details>` the catalogue renders, and
   `loading="lazy"` never recovered when one opened. As of
   2026-08-12 the loading is no longer the browser's to schedule:
   the markup carries `data-src` and no `src`, and
   [core/logo.js](core/logo.js)'s `wireLogos()` starts each fetch
   from an `IntersectionObserver` plus a `toggle` listener on
   each `<details>`. A closed category still costs nothing —
   that saving is now enforced there rather than hoped for
   here. */
.conn { display: flex; align-items: center; gap: 8px; }
.conn-name { min-width: 0; overflow-wrap: anywhere; }
.logo {
  position: relative; flex: 0 0 auto; width: 26px; height: 26px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius); background: var(--surface-subtle);
  border: 1px solid var(--line); overflow: hidden;
}
.logo-mono {
  font-size: 11px; font-weight: 700; letter-spacing: .02em;
  color: var(--text-secondary);
}
.logo-img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; opacity: 0;
}
.logo.has-logo { background: var(--surface); }
.logo.has-logo .logo-mono { visibility: hidden; }
.logo.has-logo .logo-img { opacity: 1; }

/* `06` b2: a status alone is incomplete — the basis sits beside
   it. Rendered as quiet secondary text on the same line, because
   promoting it would make the basis compete with the status it
   explains. */
.basis-line {
  display: block; margin-top: 2px; font-weight: 400;
  color: var(--text-secondary); font-size: 12px; line-height: 17px;
  overflow-wrap: anywhere;
}
/* An error is the one basis that must be findable while scanning
   65 rows, so it takes the danger ink. Never colour alone: it is
   prefixed with the word "Error". */
.basis-line.bad { color: var(--danger-ink); font-weight: 600; }

table.tbl th, table.tbl td {
  text-align: left; padding: 10px 12px; vertical-align: top;
  border-bottom: 1px solid var(--line);
}
table.tbl thead th {
  font-size: 12px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text-secondary);
  background: var(--surface-subtle); white-space: nowrap;
}
table.tbl tbody tr:last-child th,
table.tbl tbody tr:last-child td { border-bottom: 0; }
table.tbl tbody th[scope="row"] { font-weight: 700; white-space: nowrap; }
table.tbl tbody tr:hover td, table.tbl tbody tr:hover th { background: var(--surface-subtle); }
.tbl-row-open { cursor: pointer; }
.tbl-row-open:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.tbl-empty { color: var(--text-secondary); text-align: center; }
/* Long user content must not blow out a cell, and numbers read
   down a column only with tabular figures. */
table.tbl td, table.tbl th[scope="row"] { overflow-wrap: anywhere; }
table.tbl td, table.tbl th[scope="row"],
.metric-v, .fact dd { font-variant-numeric: tabular-nums; }

/* `table-layout: fixed` only when a view declares column widths,
   so existing tables are untouched. Long values wrap instead of
   starving the columns to their right — the RCM's risk titles
   pushed two columns off-screen before this existed. */
table.tbl-fixed { table-layout: fixed; }
table.tbl-fixed td, table.tbl-fixed th { overflow-wrap: anywhere; }
/* And the ROW HEADER must wrap once the column can no longer
   widen. `th[scope="row"]` is `nowrap` above, which is right on
   an AUTO-sized table — an identifier stays on one line and the
   browser gives the column the room. A FIXED column cannot take
   the room, and `overflow-wrap` above is powerless against
   `nowrap`, so a long identifier simply ran into the column
   beside it: a control config's `removal_sla_bdays` was drawn
   on top of its own Type cell. The same fix already exists for
   `.doc-table`; it was never applied to the shared primitive. */
table.tbl-fixed tbody th[scope="row"] { white-space: normal; }
/* `tiny` is for an ORDINAL, not a value: the control config's
   decision table numbers its outcomes because first-match-wins
   makes the order the semantics, and a 116px column for "1"
   takes the room the condition beside it needs. */
col.col-tiny { width: 48px; }
col.col-code { width: 92px; }
col.col-narrow { width: 116px; }
col.col-mid { width: 200px; }

/* ===================== STATES =====================
   §8's full set, as one vocabulary: first-use empty, complete,
   filtered-empty, loading, partial, recoverable failure,
   permission-limited, blocked. Every one of them is a SENTENCE
   plus, where there is one, an action — never an icon and a
   shrug. */
.state {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 8px; padding: 24px;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-panel);
  color: var(--text-secondary);
}
.state.inset { border: 0; border-radius: 0; }
.state-ico { color: var(--text-secondary); }
.state h2, .state h3 {
  margin: 0; font-size: 14px; line-height: 20px; font-weight: 700;
  color: var(--text-primary);
}
.state p { margin: 0; font-size: 13px; max-width: 62ch; }
.state .btn { margin-top: 4px; }
.state.blocked { background: var(--warn-tint); border-color: var(--warn-line); }
.state.blocked .state-ico { color: var(--warn-ink); }
.state.failure { background: var(--danger-tint); border-color: var(--danger-line); }
.state.failure .state-ico { color: var(--danger-ink); }
.state.complete { background: var(--success-tint); border-color: var(--success-line); }
.state.complete .state-ico { color: var(--success-ink); }
.state.partial { background: var(--warn-tint); border-color: var(--warn-line); }
.state.partial .state-ico { color: var(--warn-ink); }
.state.permission .state-ico { color: var(--text-secondary); }

/* `.empty` is kept as an alias of the neutral state: it is used
   by a dozen call sites and by the drawer/table empties, and
   renaming it buys nothing. */
.empty {
  padding: 24px; text-align: center;
  color: var(--text-secondary); font-size: 13px;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-panel);
}

/* ---- Loading skeleton --------------------------------------
   §8: it should resemble the final structure, and it must not
   animate when the user asks for reduced motion (honoured
   globally at the foot of this file). The pulse is opacity only
   — no sweeping highlight, which is decoration. */
.skeleton { display: flex; flex-direction: column; gap: 10px; }
.sk-row {
  height: 14px; border-radius: var(--radius);
  background: var(--line); animation: sk-pulse 1.4s ease-in-out infinite;
}
.sk-row.wide { width: 100%; }
.sk-row.mid { width: 62%; }
.sk-row.narrow { width: 34%; }
.sk-block {
  height: 56px; border-radius: var(--radius);
  background: var(--surface-subtle); border: 1px solid var(--line);
  animation: sk-pulse 1.4s ease-in-out infinite;
}
@keyframes sk-pulse { 50% { opacity: .55; } }

/* ===================== DRAWERS AND DIALOGS =====================
   §8: a dialog for a short focused decision that blocks other
   work; a drawer for supporting evidence, history or
   configuration that must preserve the selected record. These
   are the two things allowed a shadow (§7). */
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0;
  width: 460px; max-width: 94vw; z-index: 900;
  overflow-y: auto;
  background: var(--surface); border-left: 1px solid var(--line);
  box-shadow: var(--shadow-overlay);
  padding: 16px 20px 28px;
}
/* The drawer takes focus programmatically on open so the next
   Tab lands inside it. It is a container, not a control, so it
   shows no ring — the ring belongs to the close button, which is
   the first thing Tab reaches. */
.drawer:focus { outline: none; }
.drawer-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 12px;
  position: sticky; top: -16px; padding: 12px 0 10px;
  background: var(--surface); border-bottom: 1px solid var(--line);
}
.drawer-head h2 { margin: 0; font-size: 16px; line-height: 24px; }
.detail p { margin: 0 0 12px; line-height: 1.5; }
.detail h3 {
  margin: 16px 0 6px; font-size: 12px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-secondary);
}
.detail-list { margin: 0 0 12px; padding-left: 18px; line-height: 1.6; }
.muted { color: var(--text-secondary); }

/* The modal (log / package / config viewer). `.cc-modal*` names
   are kept: dom.js, the workflow channel and the Command Center
   all reach for them. */
.cc-modal {
  position: fixed; inset: 0; z-index: 1000;
  display: flex; align-items: center; justify-content: center;
  padding: 24px; background: rgba(15, 23, 42, .45);
}
.cc-modal[hidden] { display: none; }
.cc-modal-card {
  display: flex; flex-direction: column;
  width: min(860px, 100%); max-height: 88vh; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius-panel); box-shadow: var(--shadow-overlay);
}
.cc-modal-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 12px 16px;
  background: var(--surface-subtle); border-bottom: 1px solid var(--line);
}
.cc-modal-title { font-size: 14px; font-weight: 700; }
.cc-modal-body { padding: 16px; overflow-y: auto; }

/* ===================== COPILOT =====================
   §9: contextual assistance, not the page architecture. Closed
   to a 44px rail by DEFAULT, opening to ~384px, resizable
   between 320 and 560 while capped at ~45% of the viewport —
   whichever is tighter. Nothing here is styled as "AI": no
   sparkle, no glow, no gradient (§4). */
.assistant {
  position: relative; min-width: 0;
  display: flex; flex-direction: column;
  background: var(--surface); border-left: 1px solid var(--line);
  overflow: hidden;
}
/* Closed: the whole rail is the opener, labelled vertically, so
   the entry point is bound to the record on screen rather than
   parked in a menu. */
.asst-rail {
  display: flex; width: 100%; height: 100%;
  align-items: center; justify-content: center;
  border: 0; background: none; color: var(--brand-dark);
  cursor: pointer; padding: 8px 0;
}
.asst-rail:hover { background: var(--brand-tint); }
.asst-rail:focus-visible { outline: 2px solid var(--brand); outline-offset: -3px; }
.asst-rail-label {
  writing-mode: vertical-rl; transform: rotate(180deg);
  font-size: 12px; font-weight: 700; letter-spacing: .02em;
  white-space: nowrap;
}
.asst-panel { display: none; }
.layout.copilot-open .asst-rail { display: none; }
.layout.copilot-open .asst-panel {
  display: flex; flex-direction: column;
  height: 100%; min-width: 0; min-height: 0;
}
/* A visible separator with pointer AND keyboard operation (§9).
   `role="separator"` with aria-valuenow/min/max is set in the
   markup; this is only its appearance. */
.asst-resizer {
  position: absolute; z-index: 3; inset: 0 auto 0 -3px;
  width: 9px; cursor: col-resize; touch-action: none;
  border: 0; background: none; padding: 0;
  display: none;
}
.layout.copilot-open .asst-resizer { display: block; }
.asst-resizer::after {
  content: ""; position: absolute; inset: 0 auto 0 3px;
  width: 3px; background: transparent;
  transition: background var(--t-fast) ease-out;
}
.asst-resizer:hover::after, .asst-resizer:focus-visible::after,
.asst-resizer.dragging::after { background: var(--brand); }
.asst-resizer:focus-visible { outline: none; }

.asst-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; min-height: var(--topbar-h); padding: 8px 12px;
  border-bottom: 1px solid var(--line);
}
/* `min-width: 0` on BOTH the flex row's growing child and the
   text block inside it. Without the inner one the title and
   subtitle refuse to shrink below their content width and slide
   underneath the model picker — which is what a 384px panel does
   to any head that assumes it has room. */
.asst-brand {
  display: flex; align-items: center; gap: 9px;
  min-width: 0; flex: 1 1 auto;
}
.asst-brand-text { min-width: 0; }
.asst-brand .ico { color: var(--text-secondary); }
/* Two lines, and the break falls at the SPACE. Product-owner
   decision, 2026-07-28.
   Three separate requirements, and each needs its own property:
     * it must not truncate — "Compliance Co…" is not a shorter
       version of the panel's name, it is a different word;
     * it must not break INSIDE a word, which is what the first
       version's `overflow-wrap: anywhere` allowed as soon as the
       panel narrowed ("Complianc / e Copilot");
     * it must be two lines at every panel width between 320 and
       560px, not one line when there happens to be room.
   `max-width: 12ch` is what makes the third deterministic
   without a forced `<br>`: "Compliance" is 10ch so it stays on
   line one, "Copilot" plus the dot is about 9.5ch so it fits on
   line two, and the 18ch single line cannot. */
.asst-title {
  font-size: 13px; line-height: 1.3; font-weight: 700;
  max-width: 12ch; overflow-wrap: normal;
  /* A FLOOR as well as a ceiling. At a 320px panel the model
     picker and two buttons left the title 64px, and since it may
     not break inside a word it went to three lines instead of
     two. 10ch is what "Compliance" needs; the picker shrinks
     instead, which is the right thing to give up — it is a
     control whose current value is also written in the reply. */
  min-width: 10ch;
}
.asst-ctl {
  display: flex; align-items: center; gap: 6px;
  flex: 0 1 auto; min-width: 0;
}
.asst-model {
  /* Wide enough that the native dropdown arrow does not sit on
     top of the last character — "Claude Opus 4.8" plus an arrow
     needs the room, and the head has it now that the framework
     sub-line is gone. */
  /* `padding-right` keeps the native dropdown arrow off the last
     glyph — a select does not ellipsize, it clips, and an arrow
     sitting on top of a half-drawn character reads as a
     rendering fault. The floor is what the arrow plus a few
     characters need; below that the button is not a picker, it is
     a puzzle. */
  height: 30px; max-width: 148px; min-width: 92px;
  padding: 0 20px 0 7px;
  font-size: 12px; color: var(--text-primary);
  background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  cursor: pointer;
}
.asst-model:hover { border-color: var(--brand); }
/* Connection state: a coloured dot, with the word on hover
   (`title`) and in the accessibility tree (an `.sr-only` span
   inside the same `role="status"`). Product-owner decision,
   2026-07-28.
   §14's rule is against meaning carried by colour ALONE, which
   this is not — the word is present twice and only the dot is
   visual. The dot takes a darker border as well as a fill, so it
   stays a distinct mark for someone who cannot separate the
   hues. */
/* Inline, so it follows the title's last letter rather than
   occupying a slot of its own. `vertical-align` centres it on the
   text's x-height instead of its baseline, where a circle sits
   visibly low. */
.asst-conn {
  display: inline-flex; align-items: center;
  margin-left: 6px; vertical-align: middle; cursor: default;
}
.asst-dot {
  width: 9px; height: 9px; border-radius: 50%; flex: none;
  background: var(--text-muted);
  border: 1px solid var(--line-strong);
}
.asst-conn.ok .asst-dot {
  background: var(--success); border-color: var(--success-ink);
}
.asst-conn.err .asst-dot {
  background: var(--danger); border-color: var(--danger-ink);
}

.asst-log {
  flex: 1; min-height: 0; overflow-y: auto;
  padding: 14px; display: flex; flex-direction: column; gap: 10px;
}
.msg {
  max-width: 94%; padding: 9px 11px; border-radius: var(--radius-panel);
  font-size: 13px; line-height: 1.5;
  white-space: pre-wrap; word-break: break-word;
}
.msg.you {
  align-self: flex-end; background: var(--brand); color: var(--surface);
  border-bottom-right-radius: 3px;
}
.msg.bot {
  align-self: flex-start; background: var(--msg-ai);
  color: var(--text-primary); border: 1px solid var(--line);
  border-bottom-left-radius: 3px; white-space: normal;
}
.msg.tool {
  align-self: flex-start; background: var(--warn-tint);
  color: var(--warn-ink); border: 1px solid var(--warn-line);
  font-size: 12px;
}
.msg.err {
  align-self: flex-start; background: var(--danger-tint);
  color: var(--danger-ink); border: 1px solid var(--danger-line);
}
.msg.interrupted { opacity: .75; font-style: italic; }
/* §9's response contract, rendered: every assistant turn says
   what kind of claim it is. */
.msg-prov { display: flex; gap: 6px; margin-bottom: 6px; }
.asst-suggest {
  display: flex; gap: 6px; flex-wrap: wrap; padding: 0 12px 8px;
}
.asst-suggest .sg {
  min-height: 30px; padding: 4px 10px;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--surface); color: var(--text-secondary);
  font-size: 12px; cursor: pointer; text-align: left;
}
.asst-suggest .sg:hover { border-color: var(--brand); color: var(--brand-dark); }
.asst-input {
  display: flex; gap: 8px; align-items: flex-end;
  padding: 10px 12px; border-top: 1px solid var(--line);
}
.asst-input textarea {
  flex: 1; min-width: 0; resize: none; max-height: 132px;
  padding: 8px 10px; font-size: 13px; color: var(--text-primary);
  background: var(--surface-subtle);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
}
.asst-input textarea::placeholder { color: var(--text-muted); }
.asst-input textarea:focus-visible { background: var(--surface); }
.asst-input .btn { min-height: 36px; min-width: 36px; padding: 0 10px; }
.asst-input .btn.sending {
  background: var(--surface); color: var(--danger-ink);
  border-color: var(--danger-line);
}

/* ===================== WORKFLOW RUN (vagent channel) ========= */
.wf-bar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-bottom: 12px;
}
.wf-status {
  font-size: 13px; font-weight: 700; color: var(--text-secondary);
}
.wf-status.ok { color: var(--success-ink); }
.wf-status.err { color: var(--danger-ink); }
.wf-summary { font-size: 13px; color: var(--text-secondary); margin: 4px 0 0; }
.wf-detail-btn { margin-left: auto; }

.wf-nodes { display: flex; flex-direction: column; gap: 0;
  border: 1px solid var(--line); border-radius: var(--radius-panel);
  overflow: hidden; }
.wf-node {
  display: grid; grid-template-columns: 210px minmax(0, 1fr);
  gap: 14px; align-items: start;
  padding: 11px 13px; background: var(--surface);
  border-bottom: 1px solid var(--line);
  border-left: 3px solid transparent;
}
.wf-node:last-child { border-bottom: 0; }
.wf-node-head {
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; font-weight: 700;
}
.wf-check {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; flex: none; border-radius: 50%;
  border: 1.5px solid var(--line-strong);
  color: var(--text-secondary); background: var(--surface);
}
.wf-node-name { overflow-wrap: anywhere; }
.wf-node-out {
  color: var(--text-secondary); font-size: 13px; min-width: 0;
  white-space: normal;
}
.wf-node-out > :first-child { margin-top: 0; }
.wf-node-out > :last-child { margin-bottom: 0; }
/* Step state: a border colour, an icon AND the word in
   `.wf-state` — three signals, because one is a colour. */
.wf-node.pending { color: var(--text-secondary); }
.wf-node.running { border-left-color: var(--brand); }
.wf-node.running .wf-check { border-color: var(--brand); color: var(--brand); }
.wf-node.done { border-left-color: var(--success); }
.wf-node.done .wf-check {
  background: var(--success-tint); border-color: var(--success);
  color: var(--success-ink);
}
.wf-node.error { border-left-color: var(--danger); }
.wf-node.error .wf-check {
  background: var(--danger-tint); border-color: var(--danger);
  color: var(--danger-ink);
}
.wf-node.error .wf-node-out { color: var(--danger-ink); }
.wf-state {
  margin-left: auto; font-size: 12px; font-weight: 600;
  color: var(--text-secondary);
}
.wf-modal-wrap { display: flex; flex-direction: column; gap: 12px; }
.wf-modal-wrap .wf-summary { margin: 0; font-weight: 700; }

/* ===================== COMMAND CENTER ===================== */
/* The record's name on the left, its asides right-justified and
   level with it. `align-items: start` so the aside sits with the
   TITLE rather than centring against a two-line name block, and
   `flex-wrap` so a narrow pane drops it under the name instead
   of crushing either. The DOM order is name → sub → aside; only
   this rule lifts the aside beside the title, which is what
   keeps D-21's stylesheet-off read in the record's own order. */
.cc-detail-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 12px;
}
.cc-head-main { min-width: 0; }
/* Centred against the whole name block, so it lands on the
   NAME's line rather than on the status pill above it.
   DELIBERATELY UNCAPPED, and it holds one control.
   A design review at 1280px is why: it used to hold the design's
   status, its version AND the button as a fixed `max-width: 20rem`
   block, and this pane is variable-width — so anything under a
   ~590px pane wrapped the whole thing onto its own line, where it
   floated mid-pane aligned to nothing. Keep this host to a single
   short actionable; record STATE belongs in `facts()` below,
   which already owns alignment and wrapping. */
.cc-head-aside {
  display: flex; align-items: center; justify-content: flex-end;
  gap: 8px;
}
.cc-head-aside:empty { display: none; }
/* 44px of TOUCH TARGET without 44px of button. DESIGN.md §8 asks
   for the target; the button is `.btn.sm` (30px) and matching
   `Show detail` and `Run` beside it matters more than matching
   the number, so the hit area is extended past the box instead
   of the box being grown. Safe here specifically because this
   button has no neighbour within the 7px it reaches — see the
   note in the audit about `.btn.sm` being 30px app-wide, which
   is a system-level decision and not this button's to make. */
.cc-cfg-btn { position: relative; }
.cc-cfg-btn::after {
  content: ""; position: absolute; left: 0; right: 0;
  top: 50%; height: 44px; transform: translateY(-50%);
}
.cc-detail-head h2 {
  margin: 4px 0 3px; font-size: 17px; line-height: 24px; font-weight: 700;
}
.cc-detail-head .sub { color: var(--text-secondary); font-size: 12px; }

/* `.cc-design-inline`, `.cc-design-facts` and `.cc-design-lbl`
   were deleted here. They positioned the design status and
   version in the record head; both are now a `facts()` row, and
   `facts()` owns its own label weight, alignment and wrapping.
   A cluster with its own layout rules beside a grid that already
   had them was the duplication the 1280px review found. */
.cc-actions {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 8px;
}
.cc-note { font-size: 13px; color: var(--success-ink); }
.cc-note.on { font-weight: 700; }
.cc-note:empty { display: none; }

/* Run history (read-only past runs) */
.cc-run {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 11px; align-items: center;
  padding: 9px 12px; background: var(--surface-subtle);
  border: 1px solid var(--line); border-radius: var(--radius);
}
.cc-run-badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%;
  border: 1px solid var(--line-strong);
  color: var(--text-secondary); background: var(--surface);
}
.cc-run-badge.done { color: var(--success-ink); border-color: var(--success);
  background: var(--success-tint); }
.cc-run-badge.error, .cc-run-badge.killed, .cc-run-badge.cancelled {
  color: var(--danger-ink); border-color: var(--danger);
  background: var(--danger-tint); }
.cc-run-badge.paused { color: var(--warn-ink); border-color: var(--warn);
  background: var(--warn-tint); }
.cc-run-badge.running { color: var(--brand-dark); border-color: var(--brand);
  background: var(--brand-tint); }
.cc-run-main { min-width: 0; }
.cc-run-when { font-size: 13px; font-weight: 700; }
.cc-run-meta { color: var(--text-secondary); font-size: 12px; }
.cc-run-acts { display: flex; gap: 6px; flex: none; }
.cc-run-detail:empty { display: none; }
.cc-run-detail { margin-top: 10px; }

/* Run history lives INSIDE the processing panel (2026-08-07),
   under a rule rather than in a panel of its own: this run and
   the runs before it are one subject, and two stacked boxes said
   so twice while costing the record pane a whole panel's chrome.
   An `h4` rather than an `h3` because it is subordinate to
   "Control processing" — the heading level carries that, so a
   text-only read (D-21) gets the nesting the border draws. */
/* The run's decision, directly under the run bar it decides.
   Tighter than a free-standing `.cc-actions` because it is part
   of the panel above it, not a new subject. */
.wf-decide { margin-top: 12px; }
.wf-decide .cc-design-hint { margin-top: 6px; width: 100%; }
.wf-decide .cc-design-hint[hidden] { display: none; }

.cc-hist {
  margin-top: 14px; padding-top: 12px;
  border-top: 1px solid var(--line);
}
.cc-hist h4 {
  margin: 0 0 8px; font-size: 12px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em;
  color: var(--text-secondary);
}

/* Prepared package (the run's stage artifacts) */
.cc-package:empty { display: none; }
.cc-package {
  margin-top: 12px; padding-top: 12px;
  border-top: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 8px;
}
.cc-pkg-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; font-size: 13px; color: var(--text-secondary);
}
.cc-pkg-head b { color: var(--text-primary); }
.cc-pkg-item {
  border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; background: var(--surface);
}
.cc-pkg-name {
  display: flex; align-items: center; gap: 9px; width: 100%;
  min-height: 36px; padding: 8px 12px;
  border: 0; background: var(--surface-subtle);
  color: var(--text-primary); font-size: 13px; font-weight: 700;
  text-align: left; cursor: pointer;
}
.cc-pkg-name:hover { background: var(--brand-tint); }
.cc-pkg-name .cc-pkg-lbl { flex: 1; min-width: 0; }
.cc-pkg-chev { color: var(--text-secondary); transition: transform var(--t-mid); }
.cc-pkg-item.open .cc-pkg-chev { transform: rotate(180deg); }
.cc-pkg-body {
  padding: 12px 14px; font-size: 13px; color: var(--text-secondary);
  border-top: 1px solid var(--line);
}
.cc-pkg-body > :first-child { margin-top: 0; }
.cc-pkg-body > :last-child { margin-bottom: 0; }

/* Control-design review lifecycle (the config screen's Design
   tab). `.cc-design-head` and `.cc-cfg-btn { margin-left: auto }`
   were deleted with the panel they positioned: the summary moved
   into the record head, where `.cc-head-aside` does the
   right-justifying, and an `auto` margin left behind would push
   the Configure button away from the version it belongs with. */
.cc-design-ver { font-size: 12px; color: var(--text-secondary); }
.cc-design-meta {
  font-size: 13px; color: var(--text-secondary); margin: 0 0 10px;
}
/* --text-secondary, not --text-muted: this is CONTENT (it states
   that sign-off is human), and muted is reserved for placeholder
   and disabled, where it sits at 2.56 on white. The live-DOM
   contrast sweep caught it. */
.cc-design-hint {
  font-size: 12px; color: var(--text-secondary);
  margin-top: 10px; line-height: 1.5;
}
.cc-rev { max-width: 220px; }
.cc-cfg { display: flex; flex-direction: column; gap: 12px; }
.cc-cfg .panel-box { margin: 0; }
.cc-cfg-p { margin: 0; color: var(--text-secondary); font-size: 13px;
  line-height: 1.55; }
.cc-cfg-list { margin: 0; padding-left: 20px; color: var(--text-secondary);
  font-size: 13px; }
.cc-cfg-list li { margin: 3px 0; }
.cc-cfg .toolbar { margin: 0; }

/* ---- The Control config screen (command_center/config.js) ----
   Three tabs over one modal body: the design record and its
   review, the configuration as authored, and the editor. The tab
   bar reuses `.facets` — the same "same subject, different lens"
   control the Command Center's own lanes use — so a reader meets
   one selection idiom, not two. */
.cc-cfg-screen { display: flex; flex-direction: column; gap: 12px; }
.cc-cfg-tabs { margin: 0; }
.cc-cfg-body { display: flex; flex-direction: column; gap: 12px; }
.cc-cfg-body > .panel-box, .cc-cfg-body > .cat { margin: 0; }
.cc-cfg-body .tbl-wrap { margin: 8px 0 0; }
/* The source-and-provenance panel leads the reading tab, and it
   is deliberately tinted: ADR-049 makes vagent's copy
   authoritative, so "this is the authored text" is a caveat on
   everything below it rather than one more fact among them. */
.cc-cfg-prov { background: var(--surface-subtle); }
.cc-cfg-lbl {
  margin: 10px 0 4px; font-size: 12px; font-weight: 700;
  color: var(--text-secondary);
}
.cc-cfg-tech { margin-top: 4px; }
.cc-cfg-tech .cat-body > .panel-box:first-child { margin-top: 0; }

/* The editor. Monospace because it is a file, and `resize:
   vertical` because a config runs to 300 lines and a fixed box
   turns reading it into scrolling a porthole. */
.cc-cfg-edit {
  width: 100%; box-sizing: border-box; resize: vertical;
  min-height: 220px; padding: 10px 12px;
  font-family: var(--font-mono); font-size: 12px; line-height: 1.6;
  color: var(--text-primary); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius);
}
.cc-cfg-raw {
  margin: 0; max-height: 320px; overflow: auto; padding: 10px 12px;
  font-family: var(--font-mono); font-size: 12px; line-height: 1.6;
  color: var(--text-secondary); background: var(--surface-subtle);
  border: 1px solid var(--line); border-radius: var(--radius);
}
.cc-cfg-check-out { display: flex; flex-direction: column; gap: 8px; }
/* A verdict is TEXT with a tone, never a colour alone (§8's pill
   contract, applied to a status line). Both words survive the
   stylesheet being removed. */
.cc-cfg-ok {
  margin: 0; font-size: 13px; color: var(--success-ink);
}
.cc-cfg-bad {
  margin: 0; font-size: 13px; font-weight: 700;
  color: var(--danger-ink);
}

/* ===================== RUN VIEWER (M-03 f9) ===================== */
.run-head h3 { margin: 0 0 10px; font-size: 14px; line-height: 20px; }
.run-view h4 {
  margin: 16px 0 8px; font-size: 12px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-secondary);
}
.run-ok { color: var(--success-ink); font-weight: 700; }
.run-bad { color: var(--danger-ink); font-weight: 700; }
.run-timeline { list-style: none; margin: 0; padding: 0 0 0 6px; }
.run-ev {
  position: relative; display: flex; gap: 12px;
  padding: 0 0 14px 14px; border-left: 2px solid var(--line);
}
.run-ev:last-child { border-left-color: transparent; }
.run-dot {
  position: absolute; left: -6px; top: 4px;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--brand); box-shadow: 0 0 0 3px var(--surface);
}
.run-ev-bad .run-dot { background: var(--danger); }
.run-ev-body { flex: 1; min-width: 0; }
.run-ev-head {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.run-ev-title { font-size: 13px; font-weight: 700; }
.run-when {
  margin-left: auto; font-size: 12px; color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
.run-issue {
  font-size: 12px; color: var(--brand-dark);
  border: 1px solid var(--line-strong); border-radius: 999px;
  padding: 1px 8px;
}
.run-issue:hover { background: var(--brand-tint); text-decoration: none; }
/* The step-detail CONTAINER. Its block elements are styled by
   `.md` below — the class setMarkdown() adds — so only the
   container's own type scale lives here. */
.run-detail { font-size: 13px; line-height: 1.5; margin-top: 4px; }
.run-evidence { list-style: none; margin: 0; padding: 0; }
.run-evidence li {
  display: flex; gap: 8px; align-items: baseline; padding: 4px 0;
  font-size: 13px;
}
.run-evidence code { font-size: 12px; color: var(--text-secondary); }
.ev-body {
  background: var(--surface-subtle); border: 1px solid var(--line);
  padding: 10px; border-radius: var(--radius);
  font-size: 12px; overflow-x: auto; white-space: pre-wrap;
}

/* ---- D-39 segregation-of-duties banner ----------------------
   Shared by the run viewer and the deficiency drawer (C-35: one
   detector, one banner). Amber, NOT red: the record is valid and
   complete, and this is a finding to evaluate rather than an
   error to clear (ADR-015). Red would say the write failed.

   No dismiss control on purpose — a dismissible banner is one
   that gets dismissed, and this one is permanent. */
.sod-banner {
  background: var(--warn-tint);
  border: 1px solid var(--warn-line); border-left: 4px solid var(--warn);
  border-radius: var(--radius-panel);
  padding: 11px 13px; margin: 12px 0; font-size: 13px;
}
.sod-banner b { color: var(--warn-ink); }
.sod-banner ul { margin: 5px 0 6px; padding-left: 20px; }
.sod-banner li { margin: 2px 0; }
.sod-banner p { margin: 0; font-size: 12px; }

/* ===================== DASHBOARD ROLLUPS =====================
   T2 over T3 — every number here is computed on read, so there
   is no table behind these and nothing to drift. */

/* The MW spotlight. Two levels, two tones DELIBERATELY: a
   significant deficiency is worth knowing about; a material
   weakness will actually stop a certification (ADR-032). One
   tone for both would either under-warn or cry wolf. */
.spotlight {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 10px;
  background: var(--warn-tint);
  border: 1px solid var(--warn-line); border-left: 4px solid var(--warn);
  border-radius: var(--radius-panel);
  padding: 11px 13px; margin: 0 0 14px; font-size: 13px;
}
.spotlight.blocking {
  background: var(--danger-tint); border-color: var(--danger-line);
  border-left-color: var(--danger);
}
.spotlight b { color: var(--warn-ink); }
.spotlight.blocking b { color: var(--danger-ink); }
.spot-n {
  background: var(--surface); border-radius: 999px; padding: 1px 9px;
  font-weight: 700; font-variant-numeric: tabular-nums;
}
.spot-ids { color: var(--text-secondary); font-size: 12px; }
.spotlight p { width: 100%; margin: 0; color: var(--text-secondary);
  font-size: 12px; }

/* Aging. The COUNT comes before the bar in source order, so with
   the stylesheet off this still reads as a list of numbers
   rather than a row of empty boxes (D-21). */
.aging { list-style: none; margin: 0; padding: 0; }
.aging li {
  display: grid; grid-template-columns: 96px 44px minmax(0, 1fr);
  align-items: center; gap: 10px; padding: 3px 0; font-size: 13px;
}
.age-lbl { color: var(--text-secondary); }
.age-n { font-weight: 700; font-variant-numeric: tabular-nums;
  text-align: right; }
.age-bar {
  position: relative; height: 8px; border-radius: 999px;
  background: var(--line);
}
.age-bar::before {
  content: ""; position: absolute; inset: 0 auto 0 0;
  width: var(--w, 0); border-radius: 999px; background: var(--brand);
}

/* A progress meter. Never the only statement of progress — the
   percentage is written next to it. */
.meter { height: 8px; background: var(--line); border-radius: 999px;
  overflow: hidden; }
/* The fill's width is a PARAMETER the view sets as `--w`, the
   same pattern as the aging bars — not a style rule in JS. */
.meter > span { display: block; height: 100%; width: var(--w, 0);
  background: var(--success); }
.meter.warn > span { background: var(--warn); }
.meter.bad > span { background: var(--danger); }
.meter-lbl { margin-top: 6px; font-size: 12px; color: var(--text-secondary); }

/* ===================== RCM AND DEFICIENCIES ===================== */
/* RCM, grouped by business process. `h2` per process because the
   matrix is a list of processes, and a screen-reader user should
   be able to jump between them. */
.rcm-proc { font-size: 16px; line-height: 24px; margin: 0 0 2px; }
/* An uncovered risk is a FINDING, so it is marked in the row
   rather than left as an empty cell the reader must notice. Not
   colour alone — the cell says so in words (WCAG 1.4.1).
   `--danger-ink`, not `--danger`: the latter is the fill used for
   badges and borders and does not carry AA as body text on
   `--surface`. Measured, not assumed. */
tr.rcm-uncovered > th[scope="row"] { color: var(--danger-ink); }
.rcm-gap { margin: 0; color: var(--danger-ink); font-weight: 700; }

/* The ten-stage table in the drawer. `stage-here` marks where the
   record actually is — with CSS off, the view still prints
   "← here" as text next to it. */
.stages th[scope="row"] {
  font-weight: 500; white-space: nowrap;
  color: var(--text-secondary); width: 38%;
}
.stages tr.stage-here th, .stages tr.stage-here td {
  background: var(--brand-tint); font-weight: 700;
  color: var(--text-primary);
}

/* Name/value pairs about one subject (views/profile.js). A <dl>
   rather than a table: these are not rows of comparable
   entities, and table() gives every row a drawer that opens
   nothing. */
.kv-list {
  display: grid; grid-template-columns: max-content minmax(0, 1fr);
  gap: 8px 24px; margin: 0 0 16px; font-size: 13px;
}
.kv-list dt { color: var(--text-secondary); }
.kv-list dd { margin: 0; font-weight: 600; overflow-wrap: anywhere; }
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 14px; }
.chips .chip { cursor: default; }
.chips .chip:hover { border-color: var(--line-strong); color: var(--text-secondary); }

/* ===================== `.md` — ONE rendered-markdown style ====
   `06` b6: one table pattern, one drawer, one empty state — and
   one way rendered markdown looks.

   **This class is not applied by hand.** `setMarkdown()` and
   `htmlMarkdown()` in [agent/chat.js](agent/chat.js) set it, and
   they are the only way to render markdown — a lint in
   core/escape.test.js fails the build if a call site reaches past
   them to the raw renderer.

   That indirection is the point. Rendering and STYLING used to be
   separate decisions, and the styling half was forgotten on every
   new surface: the run log, the prepared package, and the Copilot
   bubbles each had rules for `p` and `code` and none for `table`,
   so a findings table parsed correctly and drew as borderless
   rows that read as plain text. Three surfaces, one bug report
   each, same four words every time. Binding the class to the
   render makes the fourth surface impossible. */
.md { overflow-x: auto; }
.md > :first-child { margin-top: 0; }
.md > :last-child { margin-bottom: 0; }
.md p { margin: 0 0 8px; }
.md ul, .md ol { margin: 4px 0 8px; padding-left: 20px; }
.md li { margin: 2px 0; }
.md table {
  border-collapse: collapse; margin: 6px 0; font-size: 12px;
  background: var(--surface);
}
.md th, .md td {
  border: 1px solid var(--line); padding: 4px 8px;
  text-align: left; vertical-align: top;
}
.md th { background: var(--surface-subtle); font-weight: 700; white-space: nowrap; }
/* Count and amount columns line up only with tabular figures. */
.md td:not(:first-child) { font-variant-numeric: tabular-nums; }
/* Headings inside rendered output are content, not page chrome —
   they must not inherit the view's heading scale. */
.md h1, .md h2, .md h3 {
  font-size: 13px; font-weight: 700; margin: 10px 0 4px;
  text-transform: none; letter-spacing: 0; color: var(--text-primary);
}
.md code {
  background: var(--surface-subtle); border: 1px solid var(--line);
  padding: 0 4px; border-radius: 4px; font-size: 12px;
  font-family: var(--font-mono);
}
.md pre {
  background: var(--surface-subtle); border: 1px solid var(--line);
  padding: 9px 11px; border-radius: var(--radius);
  overflow-x: auto; margin: 6px 0;
}
.md pre code { background: none; border: 0; padding: 0; }
.md blockquote {
  margin: 6px 0; padding: 4px 10px;
  border-left: 3px solid var(--line); color: var(--text-secondary);
}
.md hr { border: 0; border-top: 1px solid var(--line); margin: 10px 0; }
.md a { color: var(--brand-dark); }
/* A wide findings table inside a drawer scrolls rather than
   clipping — the drawer is the narrowest surface `.md` lands on,
   and a clipped table silently hides columns. */
.drawer .md table { min-width: 100%; width: max-content; }

/* Inline error text, replacing a style="" attribute that the
   no-styles-in-JS rule forbids. */
.bad { color: var(--danger-ink); }

/* ===================== SIGN-IN =====================
   `09` §7. Shown only when the server says authentication is
   required and there is no session. Hiding it is not a control —
   the server returns 401 regardless. */
.signin {
  position: fixed; inset: 0; z-index: 1000;
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  /* Flat --background, not the former three-stop gradient: §4
     admits no gradients anywhere, and "the sign-in page is the
     one screen with nothing else on it" was an argument for
     decorating the exception rather than for keeping the rule. */
  background: var(--background);
}
.signin[hidden] { display: none; }
.signin-card {
  display: flex; flex-direction: column;
  width: 360px; max-width: 100%; padding: 26px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-panel);
}
.signin-brand { text-align: center; margin-bottom: 18px; }
.signin-mark { width: 44px; height: 44px; border-radius: var(--radius);
  display: inline-block; }
.signin-name {
  margin: 10px 0 2px; font-size: 20px; line-height: 28px;
  font-weight: 700; letter-spacing: -.02em;
}
.signin-tagline { margin: 0; font-size: 13px; color: var(--text-secondary); }
.signin-panel { display: flex; flex-direction: column; gap: 6px; }
.signin-panel[hidden] { display: none; }
.signin-h {
  margin: 0 0 10px; font-size: 16px; line-height: 24px; font-weight: 700;
}
.signin-help {
  margin: 0 0 6px; font-size: 13px; line-height: 1.45;
  color: var(--text-secondary);
}
.signin-card label {
  font-size: 12px; font-weight: 600; color: var(--text-secondary);
  margin-top: 8px;
}
/* Label and "Forgot Password?" share a row. baseline so the
   small link sits on the label's text line rather than
   floating. */
.signin-labelrow {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px;
}
.signin-labelrow label { margin-top: 8px; }
.signin-card input {
  min-height: 36px; padding: 8px 11px; font-size: 14px;
  color: var(--text-primary); background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
}
.signin-card input::placeholder { color: var(--text-muted); }
/* 8px, not 16: .signin-error above already reserves its height so
   an error does not shove the button down, and 16 on top of that
   reserved row read as a gap rather than as spacing. */
.signin-card button[type="submit"] {
  margin-top: 8px; min-height: 40px; padding: 9px 12px;
  font-size: 14px; font-weight: 700;
  color: var(--surface); background: var(--brand);
  border: 1px solid transparent; border-radius: var(--radius);
  cursor: pointer;
}
.signin-card button[type="submit"]:hover { background: var(--brand-dark); }
.signin-card button[type="submit"]:disabled { opacity: .6; cursor: default; }
/* A text button that reads as a link but switches panel. Kept a
   real <button> for the keyboard and the screen reader; styled
   down so it does not compete with the submit. */
.signin-link {
  padding: 0; border: 0; background: none; cursor: pointer;
  font-size: 12px; font-family: inherit; color: var(--brand-dark);
}
.signin-link:hover { text-decoration: underline; }
.signin-back { margin-top: 12px; align-self: center; }
.signin-error {
  margin: 8px 0 0; min-height: 16px; font-size: 12px;
  color: var(--danger-ink);
}
/* The success case reuses .signin-error's slot — same
   role="alert", so the message is announced either way. The
   colour is not the only difference: the copy states the
   outcome. */
.signin-error.ok { color: var(--success-ink); }
.signin-foot {
  margin: 18px 0 0; padding-top: 16px;
  border-top: 1px solid var(--line);
  font-size: 12px; line-height: 1.45; text-align: center;
  color: var(--text-secondary);
}

/* ===================== FILE DROP =====================
   core/filedrop.js. The zone is a <label> wrapping a real
   `<input type="file" multiple>`, so everything below is styling
   a native control rather than replacing one: the keyboard path,
   the picker and the AT announcement are the platform's.

   The input is moved off-screen rather than `display: none`,
   because a hidden input is not focusable and the label would
   then be unreachable by keyboard — the exact failure §14 is
   about. `.sr-only`'s technique, applied for the same reason. */
.drop-input {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.drop { margin: 0 0 16px; }
.drop-target {
  display: flex; flex-direction: column; align-items: center;
  gap: 4px; padding: 28px 20px; cursor: pointer; text-align: center;
  background: var(--surface-subtle);
  border: 2px dashed var(--line-strong);
  border-radius: var(--radius-panel);
  transition: background var(--t-fast) ease-out,
    border-color var(--t-fast) ease-out;
}
.drop-target:hover {
  border-color: var(--brand); background: var(--brand-tint);
}
/* Focus lands on the INPUT, which is off-screen, so the ring has
   to be drawn on the label it belongs to — otherwise a keyboard
   user tabs onto an invisible target with no visible focus at
   all. */
.drop-input:focus-visible + .drop-icon,
.drop-target:focus-within {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}
/* Dragging. Not colour alone: the border also goes solid, so the
   state survives a monochrome or high-contrast rendering (§14). */
.drop-target.is-over {
  border-style: solid; border-color: var(--brand);
  background: var(--brand-tint);
}
.drop-icon { color: var(--brand-dark); line-height: 0; }
.drop-icon svg { width: 24px; height: 24px; }
.drop-lead { font-size: 14px; font-weight: 700; }
.drop-hint {
  font-size: 12px; line-height: 1.45;
  color: var(--text-secondary); max-width: 46ch;
}
/* The queue. One row per file, and the note is where the outcome
   is stated in WORDS — a row that only changed colour would say
   nothing to a screen reader, which is why the list is
   aria-live. */
.drop-list { list-style: none; margin: 12px 0 0; padding: 0; }
.drop-item {
  display: flex; align-items: baseline; gap: 8px;
  padding: 7px 10px; font-size: 13px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface);
}
.drop-item + .drop-item { margin-top: 6px; }
.drop-item-name {
  font-weight: 600; overflow-wrap: anywhere; flex: 1 1 auto;
}
.drop-item-size {
  color: var(--text-secondary); font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.drop-item-note { color: var(--text-secondary); white-space: nowrap; }
.drop-item.is-done {
  border-color: var(--success-line); background: var(--success-tint);
}
.drop-item.is-done .drop-item-note { color: var(--success-ink); }
.drop-item.is-refused {
  border-color: var(--danger-line); background: var(--danger-tint);
}
.drop-item.is-refused .drop-item-note {
  color: var(--danger-ink); font-weight: 600; white-space: normal;
}
.drop-item.is-sending { border-color: var(--brand-line); }

/* Company Setup's document corpus. It uses the shared table
   contract, with only the column geometry and caption hierarchy
   specialized for this workspace. */
.documents-workspace .state { margin-bottom: 16px; }
.documents-workspace .drop-target { padding-block: 20px; }
.doc-table { table-layout: fixed; margin: 24px 0; }
.doc-table caption {
  color: var(--text-primary); font-size: 16px; font-weight: 700;
}
.doc-count {
  float: right;
  color: var(--text-secondary); font-size: 12px; font-weight: 600;
  white-space: nowrap;
}
.doc-table th:first-child { width: 48%; }
.doc-table th:nth-child(2) { width: 16%; }
.doc-table th:nth-child(3) { width: 26%; }
.doc-table th:last-child { width: 10%; }
.doc-table tbody th, .doc-table tbody td { vertical-align: middle; }
.doc-name { display: block; overflow-wrap: anywhere; }
.doc-primary {
  display: flex; align-items: flex-start; flex-wrap: wrap; gap: 6px 8px;
}
.doc-identity { min-width: 0; }
.doc-primary .doc-name { min-width: 0; }
.source-kind { display: block; font-weight: 600; }
.doc-remove { min-width: 36px; padding-inline: 8px; }
.doc-remove svg { width: 16px; height: 16px; }
button.btn.quiet.doc-remove { color: var(--text-secondary); }
button.btn.quiet.doc-remove:hover {
  color: var(--danger-ink); background: var(--danger-tint);
}

/* EDGAR is one retrieval lane: status, action and repeated records
   stay together instead of reading as three unrelated blocks. */
.filing-lane { margin-top: 24px; }
.filing-lane .doc-head { margin: 0 0 4px; }
.filing-copy { max-width: 76ch; margin: 0 0 12px; }
.filing-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; min-height: 44px; padding: 7px 10px;
  background: var(--surface-subtle); border: 1px solid var(--line);
  border-radius: var(--radius-panel);
}
.filing-counts { display: flex; align-items: center; gap: 16px; }
.filing-count {
  color: var(--text-secondary); font-size: 12px; white-space: nowrap;
}
.filing-count b {
  color: var(--text-primary); font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.filing-count.success b { color: var(--success-ink); }
.filing-count.danger b { color: var(--danger-ink); }
.filing-source {
  display: inline-flex; align-items: center;
  gap: 6px; min-height: 32px;
}
.filing-source svg { width: 15px; height: 15px; }
.docs-next {
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px; margin-top: 24px; padding-top: 16px;
  border-top: 1px solid var(--line);
}
.docs-next h3 { margin: 0 0 2px; font-size: 14px; }
.docs-next p { margin: 0; color: var(--text-secondary); font-size: 13px; }
.docs-next .btn { flex: none; }

/* A list that is a list semantically and reads as prose. Used for
   the permitted storage roots on Settings, where the bullets add
   nothing and the paths are the content. */
ul.plain { list-style: none; margin: 6px 0 0; padding: 0; }
ul.plain li { font-size: 12px; overflow-wrap: anywhere; }

/* ===================== RESPONSIVE =====================
   §11: the interaction MODEL changes; the desktop layout is not
   merely shrunk.

   Shell preferences degrade rather than being overwritten. The
   `nav-rail` / `copilot-open` classes and their stored
   preferences are left alone; these rules override the WIDTH
   they resolve to, so a desktop preference is still there when
   the window widens again.

   **Nothing here hides text inside `#view` with `display:none`.**
   view_check.py reads every view twice — with the stylesheet and
   without it — and fails if the words or their order change. A
   pane that must disappear at narrow widths is therefore given
   the `hidden` ATTRIBUTE by JS ([core/responsive.js](core/responsive.js)),
   which survives the stylesheet being removed. */

/* 960–1279: the icon rail is preferred and supporting panels
   become overlays. */
@media (max-width: 1279px) {
  /* The rail's WIDTH is no longer set here — `nav-rail-auto`
     carries it, so the width and the presentation cannot
     disagree. See the note beside that class. */
  .layout.copilot-open { --copilot-rail: min(var(--copilot-w), 45vw); }
}

/* Below 960 the Copilot stops being a column: side by side would
   push the work area under its usable minimum (§9). */
@media (max-width: 959px) {
  :root { --gutter: 16px; }
  .layout { grid-template-columns: var(--nav-w) minmax(0, 1fr); }
  .assistant { display: none; }
  .layout.copilot-open .assistant {
    position: fixed; top: var(--topbar-h); right: 0; bottom: 0;
    z-index: 800; display: flex;
    width: min(420px, 100vw); border-left: 1px solid var(--line-strong);
    box-shadow: var(--shadow-overlay);
  }
  .layout.copilot-open .asst-resizer { display: none; }
  .workspace, .workspace.mirrored { grid-template-columns: minmax(0, 1fr); }
  .pane.queue-pane { border-right: 0; border-bottom: 1px solid var(--line); }
  .wf-node { grid-template-columns: minmax(0, 1fr); gap: 6px; }

  /* The document ledger has four evidence-heavy columns. Switch
     it to compact records with the workspace at the documented
     960px mode change instead of squeezing the desktop geometry. */
  table.doc-table[data-records] thead { display: none; }
  table.doc-table[data-records],
  table.doc-table[data-records] tbody,
  table.doc-table[data-records] th,
  table.doc-table[data-records] td { display: block; width: auto; }
  table.doc-table[data-records] { border: 0; }
  table.doc-table[data-records] tr {
    position: relative; display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    margin-bottom: 8px; padding: 4px 0 6px;
    background: var(--surface);
    border: 1px solid var(--line); border-radius: var(--radius-panel);
  }
  table.doc-table[data-records] th,
  table.doc-table[data-records] td {
    border-bottom: 0; padding: 6px 12px;
  }
  table.doc-table[data-records] tbody th[scope="row"] {
    grid-column: 1 / -1; padding-right: 56px;
    font-size: 14px; white-space: normal;
    border-bottom: 1px solid var(--line);
  }
  table.doc-table[data-records] td[data-label]::before {
    content: attr(data-label);
    display: block; color: var(--text-secondary);
    font-size: 12px; font-weight: 600;
  }
  table.doc-table[data-records] td:nth-of-type(1) { grid-column: 1; }
  table.doc-table[data-records] td:nth-of-type(2) { grid-column: 1 / -1; }
  table.doc-table[data-records] td.row-act {
    position: absolute; top: 4px; right: 4px;
    width: 44px; padding: 0;
  }
  table.doc-table[data-records] td.row-act::before { content: none; }
  .doc-table caption { font-size: 16px; }
  .doc-count { float: none; display: block; margin-top: 2px; }
  .doc-name { display: flex; align-items: center; min-height: 44px; }
  .doc-remove { min-width: 44px; }
}

/* Below 720: mobile task mode. One task at a time, full-width
   controls, 44px touch targets (§11, §14). */
@media (max-width: 719px) {
  /* The utility bar has to hold a nav toggle, the brand, a
     search field and two actions in 390px. Something gives, and
     what gives is the DECORATION: the framework tag and the
     account's role text (both restated inside the app — the tag
     on the sign-in screen, the roles on My Profile), and the
     Copilot button's visible word. The button keeps its
     `aria-label`, so it is still a named target for a screen
     reader AND for the agent, which reads `aria-label` before
     text. Nothing that is only available here is dropped. */
  .topbar { gap: 8px; padding: 0 10px; }
  .acct { display: none; }
  .brand-logo { height: 20px; }
  .search { height: 40px; }
  .search #search-btn { display: none; }
  #asst-toggle > span:not(.ico) { display: none; }
  #asst-toggle { min-width: 44px; padding: 0; }
  /* The nav becomes an off-canvas drawer. It is removed from the
     grid entirely rather than squeezed. */
  .layout { grid-template-columns: minmax(0, 1fr); }
  .sidebar {
    position: fixed; top: var(--topbar-h); bottom: 0; left: 0;
    z-index: 810; width: 268px; max-width: 84vw;
    box-shadow: var(--shadow-overlay);
    transform: translateX(-100%);
    transition: transform var(--t-slow) ease-out;
  }
  .layout.nav-open .sidebar { transform: translateX(0); }
  /* The rail is a desktop affordance only (§8) — at mobile the
     drawer shows full labels. */
  .layout.nav-rail .sidebar,
.layout.nav-rail-auto .sidebar { padding-inline: 8px; }
  .layout.nav-rail .nav-item,
.layout.nav-rail-auto .nav-item {
    justify-content: flex-start; gap: 10px;
    padding: 9px 10px 9px 18px; font-size: 13px;
  }
  /* The flyout label. On the LEAVES and on the GROUP HEADINGS
   alike — §8 requires that a collapsed destination expose its
   full label on hover and on keyboard focus, and a heading
   reduced to a hairline needs it for the same reason. Printed
   from `data-label` so the visible label span can stay in the
   DOM at `font-size: 0`, which is what keeps `cb_adapter`'s
   label for an agent target unchanged by the collapse. */
.layout.nav-rail .nav-item::after,
.layout.nav-rail-auto .nav-item::after,
.layout.nav-rail .nav-head::after,
.layout.nav-rail-auto .nav-head::after { content: none; }
  .layout.nav-rail .nav-head,
.layout.nav-rail-auto .nav-head {
    height: auto; margin: 0; padding: 9px 8px;
    font-size: 12px; background: none;
  }
  .layout.nav-rail .nav-item .badge,
.layout.nav-rail-auto .nav-item .badge {
    position: static; font-size: 12px; padding: 0 7px;
  }
  .nav-scrim {
    position: fixed; inset: var(--topbar-h) 0 0; z-index: 805;
    background: rgba(15, 23, 42, .45); border: 0;
  }
  .nav-scrim[hidden] { display: none; }
  .layout.copilot-open .assistant {
    width: 100vw; border-left: 0;
  }
  /* Touch targets. 44px is the floor, not the look.
     **`.sm` has to be named explicitly.** `button.btn.sm` is
     (0,2,1) and the bare `button.btn` below is (0,1,1), so the
     base rule's `min-height: 30px` won and every small button —
     Back to the queue, Show config, Show detail, and all three
     Deficiencies actions — measured 30px at 375px wide while
     this block looked like it had covered them. A media query
     adds no specificity; only the selector does. */
  .nav-item, .queue-row, .facet, .chip, .icon-btn,
  button.btn, .cc-pkg-name, .tbl-row-open { min-height: 44px; }
  button.btn.sm, a.btn.sm, button.btn.lg,
  select.filter, input.filter,
  .asst-suggest .sg, .asst-model, .signin-link {
    min-height: 44px;
  }
  /* The two the named list missed, found by the target-size
     assertion in keyboard_check.py rather than by reading this
     block again: a group heading measured 251x33 and the global
     search field 52x38. The search is a composite — the wrapper
     is what has the border, so both it and the input need the
     floor or the box grows around a 38px hit area. */
  .nav-group > .nav-head, .search, .search input {
    min-height: 44px;
  }
  /* The wizard's and the form primitive's controls, which the
     named list above missed for the same reason it missed the
     other two: a block that looks like it covers "controls" only
     covers the selectors it writes down. Measured at 375px before
     this existed — step pills 29px, inputs 35px, selects 33px.

     16px text on every input, not 13px: below 16px iOS Safari
     zooms the viewport on focus, which then leaves the page
     horizontally scrolled and is indistinguishable from a layout
     bug. This is the one place the type scale is overridden
     upwards on purpose. */
  .wiz-step, .cat > summary { min-height: 44px; }
  .field input, .field select, .field textarea,
  .check {
    min-height: 44px; font-size: 16px;
  }
  .field textarea { min-height: 88px; }
  .check { align-items: center; }
  .sw { min-height: 44px; min-width: 44px; }

  /* Below 720px the six step pills cost ~169px of the first
     screen before the task begins. The count and the bar carry
     orientation; the full list becomes a disclosure. */
  .wiz-rail { display: none; }
  .wiz-rail.shown { display: block; }
  .wiz-rail ol { flex-direction: column; }
  .wiz-step { width: 100%; text-align: left; border-radius: var(--radius); }
  /* BLOCK, not flex. The desktop default is `display: none` and the
     first version of this rule turned it into a flex ROW, which put
     the phase number, the phase name, the screen name and the
     button side by side in 375px and cramped all four. The header
     is four stacked lines. */
  .wiz-here { display: block; }

  /* An inline hit area as well as a block one, where the control
     is not already full width. */
  .icon-btn, #asst-toggle, #nav-toggle { min-width: 44px; }
  button.btn { width: 100%; }
  .page-head-actions .btn, .cc-actions .btn, .decision-acts .btn,
  .tbl-actions .btn, .state .btn { width: 100%; }
  .page-head { flex-direction: column; align-items: stretch; gap: 12px; }
  .page-head-actions { flex-direction: column; }
  /* A pane head holding a title and a filter strip has no room
     for both on one line at 390px. */
  .pane-head { flex-direction: column; align-items: stretch; gap: 8px; }
  .decision { grid-template-columns: minmax(0, 1fr); }
  .metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .metric { border-bottom: 1px solid var(--line); }
  .facts { grid-template-columns: minmax(0, 1fr); }
  .fact { border-right: 0; border-bottom: 1px solid var(--line); }
  .fact:last-child { border-bottom: 0; }
  .basis > div { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .drawer { width: 100vw; max-width: 100vw; }
  .cc-modal { padding: 0; }
  .cc-modal-card { max-height: 100vh; border-radius: 0; border: 0; }
  .kv { grid-template-columns: minmax(0, 1fr); gap: 2px 0; }
  .kv .k { margin-top: 8px; }

  /* §8: a mobile RECORD representation, not a clipped table.
     Done with generated content from `data-label`, which is the
     one technique that adds no text to the DOM — so D-21's
     stylesheet-off read still gets a plain, correct table with
     its real <thead>, and the styled read gets records.

     **Gated on `[data-records]`, and that is the fix for a real
     defect.** The rule used to apply to every `table.tbl` and
     hide every `<thead>`. `table()` labels its cells, but the
     hand-built tables in `processes.js` and `rcm.js` did not — so
     at 375px a reader saw "1", "Receive customer order",
     "Sales operations" with no column names anywhere, visible or
     programmatic. A global rule that depends on a per-call-site
     contract will outrun that contract the first time someone
     writes a table by hand. Now the table has to *say* it has
     labels before its headers are taken away. */
  table.tbl[data-records] thead { display: none; }
  table.tbl[data-records],
  table.tbl[data-records] tbody, table.tbl[data-records] tr,
  table.tbl[data-records] th,
  table.tbl[data-records] td { display: block; width: auto; }
  table.tbl[data-records] { border: 0; }
  table.tbl[data-records] tr {
    margin-bottom: 8px; padding: 4px 0;
    background: var(--surface);
    border: 1px solid var(--line); border-radius: var(--radius-panel);
  }
  table.tbl[data-records] th,
  table.tbl[data-records] td {
    border-bottom: 0; padding: 6px 12px;
  }
  table.tbl[data-records] tbody th[scope="row"] {
    font-size: 14px; white-space: normal;
    border-bottom: 1px solid var(--line);
  }
  table.tbl[data-records] td[data-label]::before {
    content: attr(data-label);
    display: block; color: var(--text-secondary);
    font-size: 12px; font-weight: 600;
  }
  /* A table without labels keeps its headers and scrolls inside
     its own box rather than losing meaning. Not ideal — §8
     prefers no horizontal scrolling — but a scrollable table a
     reader can interpret beats a stacked one they cannot. */
  table.tbl:not([data-records]) { display: block; overflow-x: auto; }
  table.tbl-fixed { table-layout: auto; }
  .stages th[scope="row"] { width: auto; }
  .documents-workspace .drop-target { padding-block: 20px; }
  table.doc-table[data-records] tr { display: grid; }
  .filing-toolbar { align-items: stretch; flex-direction: column; }
  .filing-counts { justify-content: space-between; gap: 8px; }
  .filing-actions .btn { width: 100%; }
  .filing-source { justify-content: flex-start; min-height: 44px; }
  .docs-next { align-items: stretch; flex-direction: column; }
}

/* ===================== ACCESSIBILITY AND PLATFORM =====================
   Audited against devdocs/migration-steps/ui-guidelines-pinned.md. */

/* Visually hidden but reachable by screen readers and by Tab.
   Not `display:none` — that removes it from the a11y tree. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: 8px; top: -48px; z-index: 1200;
  padding: 8px 14px; border-radius: 0 0 var(--radius) var(--radius);
  background: var(--brand); color: var(--surface);
  text-decoration: none; transition: top var(--t-mid) ease-out;
}
.skip-link:focus { top: 0; }

/* Headings should not leave one word on the last line. */
h1, h2, h3 { text-wrap: balance; }

/* Native select: Windows dark mode renders an unstyled select
   with a dark background and dark text unless both are set. */
select.filter, select {
  background-color: var(--surface); color: var(--text-primary);
}

/* A scroll inside a modal or drawer must not chain to the page
   behind it. */
.drawer, .cc-modal-card, .asst-log { overscroll-behavior: contain; }

/* Removes the 300ms double-tap delay, and makes the tap
   highlight a deliberate choice rather than the OS default. */
button, a, .nav-item, .queue-row, .tbl-row-open, .card {
  touch-action: manipulation;
  -webkit-tap-highlight-color: rgba(37, 99, 235, .15);
}

/* Honour a reduced-motion preference. Not "no animation" —
   near-instant, so state changes are still perceivable. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---- The testing calendar (views/calendar.js, M-04) ---------
   A list by month, not a grid of squares: a testing schedule is
   read as "what is due and what has slipped", and a month with
   two entries in a seven-column grid is mostly empty cells.
   Each row is date / control / sample / state, and it degrades
   to a readable line when the stylesheet is gone (D-21) because
   every part carries its own words. */
.cal-list { list-style: none; margin: 0; padding: 0; }
.cal-entry {
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr) auto auto;
  align-items: center; gap: 10px;
  padding: 7px 0; border-bottom: 1px solid var(--line);
}
.cal-entry:last-child { border-bottom: 0; }
.cal-when { color: var(--text-secondary); font-size: 12px; }
.cal-what { min-width: 0; overflow-wrap: anywhere; }
.cal-size { color: var(--text-secondary); font-size: 12px; }
.cal-month + .cal-month { margin-top: 12px; }

@media (max-width: 720px) {
  .cal-entry { grid-template-columns: minmax(0, 1fr); gap: 2px; }
}

/* ---- Walkthroughs (views/walkthroughs.js, M-01) -------------
   The question, its rubric and the answer box, stacked. No
   player and no timeline: ADR-034 drops the video pipeline and
   keeps the documented walkthrough, so this surface is prose and
   a text box by design. */
.wt-input {
  display: block; width: 100%; margin: 6px 0 8px;
  padding: 8px 10px; font: inherit;
  color: var(--text-primary); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-panel);
  resize: vertical;
}
<<<<<<< Updated upstream
=======
/* --brand, not --focus: there is no --focus token, and
   every other focus-visible rule here uses --brand. An
   undefined custom property makes `outline` invalid, so
   the ring does not render at all and a keyboard user
   loses the focus indicator silently. */
>>>>>>> Stashed changes
.wt-input:focus-visible { outline: 2px solid var(--brand);
  outline-offset: 1px; }
/* A frozen answer reads as prose, not as a disabled input: the
   database refuses the write, and a greyed-out box invites the
   attempt. */
.wt-answer {
  margin: 6px 0 4px; padding: 8px 10px;
  background: var(--surface-sunken, var(--surface));
  border-left: 3px solid var(--line); border-radius: 3px;
}
.wt-q + .wt-q { margin-top: 10px; }
.wt-detail { margin: 14px 0 8px; }

/* ---- The trial-balance preview (views/import.js, M-02 f9c) ---
   Five counts in a row, each a number over its label. The
   numbers are what a reader acts on — "12 changed" is the whole
   decision — so they lead, and the label sits under. Wraps to
   one per line under 640px rather than shrinking. */
.tb-counts {
  display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0;
}
.tb-count {
  flex: 1 1 110px; padding: 8px 11px;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-panel);
}
.tb-count b { display: block; font-size: 18px; }
.tb-count span {
  display: block; margin-top: 2px;
  color: var(--text-secondary); font-size: 12px;
}
.tb-preview-box { margin-top: 14px; }
