*, *::before, *::after {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

/* ══════════════════════════════════════════════════════════════════════════
   #105 / #444 / #399 — THE PARAQUALIS PALETTE, AND A DARK RAMP BUILT FROM IT.

   RULED by Craig 2026-08-20, as one answer to three issues that had been pulling
   in different directions:

     · #105, 2026-08-15: "it is the design of stuff, we use the ParaQualis color
       pallette for the design of the UI".
     · #444, 2026-07-29: "see if there is a standard set of colors published by
       someone who cares about accessibility to google, apple, microsoft, … Lets
       adopt that."

   Those are not the same instruction and dark mode is where they collide: the
   published ParaQualis palette is a LIGHT palette with no dark variants. His
   ruling was ParaQualis in BOTH modes with the dark ramp built from it — the app
   stays recognisably ours at night and we do the accessibility work ourselves
   rather than inheriting someone else's scale. He accepted that consequence
   explicitly, which is why every derived pair below carries its MEASURED
   contrast ratio: "we do it ourselves" only works if the number is checked
   rather than eyeballed. Every ratio here was computed from the sRGB relative
   luminance formula, not estimated.

   THE VALUES ARE QUOTED FROM THE BRAND FILE, which lives at
   `~/.claude/brand_colors.md` — OUTSIDE this repo, so RegCheck cannot see its
   own palette source. That is why they are written out here rather than
   referenced: this block is the repo's copy of record, and the brand file is the
   authority it was taken from (2026-05-19 brand asset, "2605 Paraqualis brand
   colors v1.tif").

   THE BRAND NARRATIVE, because it constrains how these are used: the colours are
   the sky at sunset — deep blue at the top, through a lighter blue band, into
   warm orange and pink at the horizon. Cool-to-warm is the signature. The file's
   own rule: "Combine more than three blues + one orange in a single surface —
   the palette tells a story; don't make it shouty."

   WHAT THIS BLOCK IS NOT. It is not the sweep. #399 is the sweep over the ~1,400
   hardcoded light literals, and Craig's ordering note is explicit: it runs AFTER
   these tokens exist, "or it hardcodes a second time against colours that are
   about to move".
   ══════════════════════════════════════════════════════════════════════════ */
:root {
    /* #1273 — the shared tooltip. Declared as TOKENS in both themes rather than
       written as literals in rc_tip.js: a colour named in JavaScript is a colour
       that cannot follow the theme, and a var() naming a token that does not
       exist is a hardcoded colour wearing one (styles.css records that mistake
       three times). Dark ink on a light ground here, inverted below. */
    --tip-bg: #1f2937;
    --tip-fg: #f9fafb;
    --tip-border: transparent;   /* #1917: a dark pill on a light page needs no edge */

    /* ── The palette, verbatim from the brand file ───────────────────────── */
    --pq-blue-900:  #276BB2;   /* Primary deep blue — headers, primary CTAs. 5.48 on white */
    --pq-blue-800:  #3274C2;   /* Secondary deep blue — alt header / pressed */
    --pq-blue-600:  #4C8ED8;   /* Mid blue — secondary buttons, active tabs, links */
    --pq-blue-500:  #5294DC;   /* Mid blue, warmer — hover, focus rings */
    --pq-blue-300:  #89C0F5;   /* Light blue — input grounds, info banners */
    --pq-blue-200:  #B6DBF6;   /* Very light blue — subtle grounds, pills */
    --pq-mint-050:  #E7F3EB;   /* Pale mint — page and card backgrounds */
    --pq-slate-400: #81A5C1;   /* Muted slate — secondary text, borders */
    --pq-peach-200: #FECF81;   /* Pale peach — soft highlights, badge grounds */
    --pq-gold-400:  #FFBE69;   /* Golden orange — warning, attention */
    --pq-orange-500:#FF8053;   /* Vivid orange — key highlight */
    --pq-coral-400: #F08568;   /* Coral — destructive, softened */
    --pq-cream-050: #EFEAE3;   /* Warm cream — the alternative page ground */
    /* #1903 (code review FE-F30) - values that were declared under three or more
       token names. Each is written ONCE, here; every surface token that used the
       literal is now an alias of its constant, so a change is one edit. */
    --pq-white: #ffffff;
    --pq-mist-100: #e6eaf0;
    --pq-mist-200: #e2e8f0;
    --pq-green-800: #276749;
    --pq-green-300: #6ee7b7;
    --pq-navy-800: #1a3a5c;
    --pq-night-850: #1a212b;
    --pq-night-900: #1a202c;
    --pq-night-950: #0f172a;
    --pq-indigo-500: #4f6ddb;
    --pq-slate-750: #334155;
    --pq-rose-300: #fc9aa0;
    --pq-amber-500: #e8a330;
    --pq-blue-250: #9dbbe6;
    --pq-blue-350: #6fa8dc;
    --pq-grey-500: #6b7280;

    /* ── Derived, because the brand file has no value for these ──────────── */
    /* The file is explicit that three of its own colours must not carry body
       text: #FF8053 is 2.9 on white, #81A5C1 is 3.0. Both fail AA for normal
       text, so anywhere a brand hue has to be INK it is darkened here and the
       measured result is recorded beside it. */
    --pq-blue-050:  #e2f1fb;   /* blue-200 into white 60% — the palest ground */
    --pq-slate-700: #546b7d;   /* slate-400 into black 35% — 5.56 on white, 4.88 on mint. AA */
    --pq-border:    #e1ded8;   /* #1938 — the artboard's neutral line; a separator, not ink */
    --pq-danger:    #a85d49;   /* coral into black 30% — 4.85 on white, 4.26 on mint. AA normal on white */
    --pq-warn:      #946e3d;   /* gold into black 42% — 4.61 on white. AA */

    /* ── SEMANTIC ROLES, LIGHT ───────────────────────────────────────────────
       The page ground is the brand's own mint rather than its cream. Both are
       offered by the file ("Page background: #EFEAE3 (warm cream) OR #E7F3EB
       (pale mint)"); mint is the smaller move from the cool grey the app has
       worn until now, and it keeps the cool-to-warm story running in the right
       direction. Cream is one line away if it reads better on a real screen —
       which is a judgement to make by looking, not by reasoning. */
    /* #1938 — NEUTRAL GROUNDS (Craig 2026-10-06, comment 21760: "the colors are
       better than the existing with a neutral color, light grey (with the
       selected item highlighted ... I am preferring the light blue"). The page
       ground, the soft panel ground, the lines and the ink are the Option 1
       revised artboard's paper greys (documentation/design/ui/design_handoff_qms_page/); the mint
       ground is retired. The light blue is --primary-pale, and it marks the
       SELECTED item only — hover is the neutral --hover-bg. */
    --pq-ground:    #f4f2ee;   /* the page */
    --pq-soft:      #f6f5f2;   /* a panel's own ground: rails, table heads, bands, insets */
    --pq-surface:   #ffffff;
    --pq-elevated:  #ffffff;
    --pq-ink:       #1c1d1f;   /* 15.6 on the ground, 17.1 on white */
    --pq-ink-muted: #565a61;   /* 6.4 on the ground, 7.0 on white — AA */
    --pq-on-danger: #ffffff;   /* ink ON a --pq-danger fill (the notification badge) — 4.85 */
    --pq-accent:    var(--pq-blue-900);
    --pq-accent-2:  var(--pq-blue-600);
    --pq-line:      var(--pq-border);
}

/* ══════════════════════════════════════════════════════════════════════════
   #444 / #399 — THE TOKENS THAT WERE REFERENCED AND NEVER DEFINED.

   Measured 2026-08-21: 47 custom properties are read by `var()` somewhere in the
   frontend and declared NOWHERE — 282 references, and 140 of them carry NO
   FALLBACK. A `var()` with no fallback and no definition makes the whole
   declaration INVALID AT COMPUTED-VALUE TIME, so the property falls back to
   inherited or initial. These are not wrong-theme colours. They are rendering
   failures, and they are failing in LIGHT MODE TOO.

   The worst of them by a distance: `--danger`, read 50 times with 43 of those
   unguarded. Every one of those is an error message or a destructive control
   that has been inheriting the ambient body colour instead of rendering red —
   including `frontend/app.js` writing `status.style.color = 'var(--danger)'`
   directly onto a status line.

   #444's own comment named this shape on 2026-08-16, from two instances found
   while touching this file for #405: "A token name that is not defined is a
   hardcoded colour wearing one, and it is invisible to a grep for hex literals
   in the obvious places. Worth a grep for the same pattern when this is picked
   up: `var(--something, #literal)` where the token does not exist." This is that
   grep, run.

   THIS IS A NET DELETION, NOT 47 NEW COLOURS. Most of these are SYNONYMS of
   tokens that already exist — five names for one surface, three for one border,
   two for one danger — so they are aliased onto the existing token rather than
   given values of their own. That is charter 6 applied to the token layer: the
   app had six vocabularies for one palette because each new surface invented the
   name it expected to find. Aliasing means they follow the theme automatically
   and there is still ONE place to change each colour.

   Two are deliberately NOT colours and are aliased to the stacks the app already
   uses (`--sans`, `--serif`), and the six `--{cat}-badge-ink` tokens get the
   light-mode literal their call sites already carry as a fallback, so a default
   domain (where `applyFindingColors` returns early) is no longer relying on a
   fallback that only half its call sites supply.
   ══════════════════════════════════════════════════════════════════════════ */
:root {
    /* — surfaces —
       #1655 item 1: THREE OF THESE NAMES ARE GONE. `--surface2`, `--surface3`
       and `--surface-alt` each aliased `var(--pq-elevated)`, exactly as
       `--surface-2` does — four names for one colour, which the audit called
       "naming drift papered over rather than resolved", and it was right: the
       comment here used to read "five names for two things" and simply
       described the problem.

       All 40 call sites across styles.css, app.js and the two sandboxes that
       link this file were rewritten to `--surface-2` first, so the rewrite is
       a provable no-op — every site resolved to `--pq-elevated` before and
       resolves to it now. The names were then deleted rather than left as
       aliases, because an alias kept "in case" is how a cluster of four
       becomes a cluster of five.

       WHY THIS HAD TO COME FIRST, and it is the audit's own argument: deciding
       which name wins is cheap while there are 40 sites and expensive once a
       tokenisation sweep has let hundreds of new sites pick one at random. */
    --surface-2:           var(--pq-elevated);
    --card-bg:             var(--pq-elevated);
    --rc-panel:            var(--pq-surface);
    --input-bg:            var(--pq-surface);
    --sidebar-bg:          var(--pq-ground);
    --background:          var(--pq-ground);
    --bg-alt:              var(--pq-ground);
    --bg-light:            var(--pq-ground);
    --bg-primary:          var(--pq-ground);
    /* #1938 — the subtle grounds are NEUTRAL; the light blue is selection only. */
    --bg-subtle:           var(--pq-soft);
    --rc-subtle-bg:        var(--pq-soft);
    /* — hover: three names — NEUTRAL (#1938): a wash of the ink, so it reads on
         white, on a soft panel and in dark mode alike, and is never the selection. */
    --hover-bg:            color-mix(in srgb, var(--pq-ink) 6%, transparent);
    --bg-hover:            var(--hover-bg);
    /* — ink: four names — */
    --fg:                  var(--pq-ink);
    --rc-fg:               var(--pq-ink);
    --text-color:          var(--pq-ink);
    --text-secondary:      var(--pq-ink-muted);
    --muted:               var(--pq-ink-muted);
    --rc-muted:            var(--pq-ink-muted);
    /* — lines: three names — */
    --border-color:        var(--pq-line);
    --rc-subtle-border:    var(--pq-line);
    --rc-anchor-ring:      var(--pq-accent-2);
    /* (BURNED — #1575) `--rc-find-selected` lived here, resolving to
       `--pq-blue-050` — "the palest ground", a BACKGROUND tint. Its one consumer
       drew it as a 2px OUTLINE on the selected document anchor, so the mark for
       "this is the finding you are looking at" was a near-white stroke on a white
       page: applied, computed, and invisible. Selection strokes use
       `--rc-anchor-ring` (the token the drawer's selected card already uses), so
       the page and the drawer cannot disagree about what selected looks like. */
    /* — brand: four names — */
    --brand:               var(--pq-blue-900);
    --accent:              var(--pq-accent);
    --rc-accent:           var(--pq-accent);
    --accent-soft:         var(--primary-pale);
    --primary-dark:        var(--pq-blue-900);
    --primary-ultra-light: var(--primary-pale);
    /* `--primary-rgb` is read inside rgba(), so it must be a CHANNEL TRIPLE, not
       a colour. #276BB2 = 39 107 178. */
    --primary-rgb:         39, 107, 178;
    --mist:                var(--pq-soft);
    /* — status: five names for three states — */
    --danger:              var(--pq-danger);
    --error:               var(--pq-danger);
    --ok: var(--pq-green-800);
    --success: var(--pq-green-800);
    --success-text: var(--pq-green-800);
    /* — type stacks, not colours — */
    --sans:  -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --serif: Georgia, "Times New Roman", serif;
    /* #1654 — THE MONOSPACE STACK, which had no token while --sans and --serif
       did. Measured: 97 font-family declarations carry SIX different monospace
       stacks — `monospace` alone (15), `ui-monospace, SFMono-Regular, Menlo,
       Consolas, monospace`, `'SFMono-Regular', Consolas, monospace`,
       `ui-monospace, "SF Mono", Menlo, Consolas, monospace`, `'Courier New',
       Courier, monospace` and `'Courier New', monospace`. Those render as
       different typefaces on the same machine, so a tabular column and a code
       span disagree for no reason. This is the most complete of the six and the
       one the newest call sites already use. — */
    --mono:  ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    /* — the SIX finding badge inks. `applyFindingColors` mints these at runtime
         ONLY when a domain supplies custom colours, so on a default domain every
         call site was relying on its own literal fallback — and not all of them
         carried one. These are those literals, stated once.
         `--missing-badge-ink` joined them 2026-08-31: the missing badge shipped
         in v4.2.906 reading a token nobody had declared. It carried a `#fff`
         fallback so it rendered correctly and nothing looked wrong — the palette
         contract caught it, which is what that contract is for. #fff because
         --missing-bar is deep magenta (#ce1b7d), the darkest of the six bars. — */
    --conflict-badge-ink: var(--pq-white);
    --gap-badge-ink: var(--pq-night-900);
    --vague-badge-ink: var(--pq-night-900);
    --unnecessary-badge-ink: var(--pq-white);
    --ai-badge-ink: var(--pq-white);
    --missing-badge-ink: var(--pq-white);

    /* ══ #1654 · THE SIZING SCALE ═══════════════════════════════════════════
       Craig ruled 2026-09-20 after the UI consistency audit (#1652): "do 1 and
       2 in that order as suggested". This is job 1.

       WHY IT EXISTS. Every custom property in this file was a COLOUR. There was
       no --space, no --radius, no --font-size, no --z. So every geometric value
       in 17,800 lines was a literal, and the drift is measurable: 866 font-size
       declarations across 61 distinct sizes once normalised to rem, 511
       border-radius declarations across 37 values, 90 z-index declarations
       across 43 values, and eight different sizes for one .ic icon class. None
       of that arrived at once — each component was hand-tuned against its
       neighbours, so the set grows every time a surface is added. The scale is
       what makes the later consolidation finite: once it exists, "done" is a
       number rather than an opinion.

       NOTHING CONSUMES THESE YET, DELIBERATELY. Adoption is a separate item and
       is where the risk lives — source does not predict paint in this file (87
       !important declarations, 32 duplicated selectors, 2,092 inline style
       sites). A custom property nothing references cannot change a pixel, which
       is precisely what makes this safe to land mid-delivery.

       IT IS DESCRIPTIVE, NOT IDEALISED, and that was a decision. Every value
       below is one the stylesheet already uses in quantity. A scale invented
       from first principles matches nothing and gets ignored; one derived from
       the actual distribution can be adopted a rule at a time. Where a step was
       dropped it is named as noise below, so the tightening is visible rather
       than silent.

       MEASURED IN THIS PASS, not taken from the audit. #1652's figures had
       already moved by the time this was built — an ordinary UI change (#1653)
       shifted the radius distribution enough to displace 5px from the top four
       and added two new one-off font sizes. That is the drift thesis
       demonstrated, and the reason these steps come from a fresh count.
       ═══════════════════════════════════════════════════════════════════════ */

    /* — TYPE. rem, not px, and that is not a preference: rem scales with the
         reader's own browser text size and px does not, which the standing rule
         that interfaces respect user settings settles. 769 of 866 declarations
         are already rem; the 109 px and 10 em values are the deviation.
         The band 0.66–1.10 carries 94% of all type in the app; the four display
         steps cover the headings above it. Dropped as noise: the 0.01rem
         neighbours (0.62/0.63/0.64, 0.73/0.74, 0.79, 0.81/0.83/0.84, 0.86/0.87)
         and the px-derived thirds (0.6562, 0.7188, 0.8438). — */
    --font-size-3xs: 0.66rem;   /* 10.6px */
    --font-size-2xs: 0.70rem;   /* 11.2px */
    --font-size-xs:  0.75rem;   /* 12.0px */
    --font-size-sm:  0.80rem;   /* 12.8px */
    --font-size-md:  0.85rem;   /* 13.6px — the most-used band */
    --font-size-lg:  0.90rem;   /* 14.4px */
    --font-size-xl:  1.00rem;   /* 16.0px */
    --font-size-2xl: 1.10rem;   /* 17.6px */
    --font-size-3xl: 1.25rem;   /* 20.0px */
    --font-size-4xl: 1.50rem;   /* 24.0px */
    --font-size-5xl: 2.00rem;   /* 32.0px */
    --font-size-6xl: 3.00rem;   /* 48.0px */

    /* — SPACE. One ramp for padding, margin AND gap, because they already share
         a vocabulary: the same 2/4/6/8/10/12 values dominate all three. An even
         2px ramp to 16 then 4px to 32. The odd neighbours (5, 7, 9, 11) are
         real and common and are deliberately NOT steps — absorbing them into
         their even neighbour is the tightening this scale exists to enable. — */
    --space-0:   0;
    --space-3xs: 2px;
    --space-2xs: 4px;
    --space-xs:  6px;
    --space-sm:  8px;
    --space-md:  10px;
    --space-lg:  12px;
    --space-xl:  14px;
    --space-2xl: 16px;
    --space-3xl: 20px;
    --space-4xl: 24px;
    --space-5xl: 32px;

    /* — RADIUS. The top four measured values cover 55% of all uses and the
         first six cover 73%. 5px and 7px are frequent and are deliberately not
         steps, for the same reason as the odd spaces. `pill` and `circle` are
         shapes rather than sizes and keep their own names so nobody has to
         remember that 999px means "fully round". — */
    --radius-none:   0;
    --radius-xs:     3px;
    --radius-sm:     4px;
    --radius-md:     6px;
    --radius-lg:     8px;
    --radius-xl:     10px;
    --radius-2xl:    12px;
    --radius-pill:   999px;
    --radius-circle: 50%;

    /* — ICON. One .ic class renders at eight different sizes between 11 and
         18px, which is the drift this step names. Four even steps; 11, 13, 15
         and 17 are the neighbours to absorb. — */
    --icon-sm: 12px;
    --icon-md: 14px;
    --icon-lg: 16px;
    --icon-xl: 18px;

    /* — Z-INDEX. 43 distinct values from 0 to 12000 is not a scale, it is 43
         separate decisions, and the reason they climbed is that each new
         floating thing had to beat whatever it landed on top of. These are
         LAYERS, named for what sits in them, and every value below is one the
         file already uses — so a rule adopting a layer keeps its current
         stacking rather than moving. The header really is above the modal
         layer (10200, measured): it is sticky and its z-index caps the menu
         inside it, which is why it outranks the floats. — */
    --z-base:     1;
    --z-raised:   10;
    --z-sticky:   30;
    --z-chrome:   70;
    --z-dropdown: 300;
    --z-popover:  700;
    --z-overlay:  5000;
    --z-toast:    9000;
    --z-notify:   9999;
    --z-header:   10200;
    /* #1912 — A BLOCKING DIALOG OF A SURFACE (the reason box, the BPMN delete and
       lane-change confirm, the edit-suggestion box, the QMS template modal, the
       registry popover, the review drawer, the welcome panel): ABOVE every piece
       of page chrome - the process map header (10100), its menu (10120), the
       chat window (10150), the popover band (rc_anchor.js 10150-10199) and the
       top header (10200) - so nothing behind a dialog can be clicked. Seven of
       them sat at 1100-9999, under the map header, so its Save and Revert stayed
       live behind a delete's reason box. Below the progress family (11000-11060,
       the session warning must show over a dialog) and the branded modal.
       Within the band a dialog asked FROM another sits above it:
       calc(var(--z-dialog) + n), in steps of 10. */
    --z-dialog:   10300;
    /* The app's branded modal (.modal-overlay: confirm, alert, prompt) - asked
       over anything, a surface dialog included. Was named at 1200, which is
       where the BPMN confirm that adopted it landed: under the map header. */
    --z-modal:    11500;
    --z-topmost:  12000;
    /* #1761 — the dim behind a window that blocks the page (Start Analysis). */
    --rc-scrim:   rgba(15, 23, 42, 0.28);

    /* ── #755 — THE DATA BLOCK'S OWN TOKENS ────────────────────────────────
       The block is ONE shared component with three call sites, and it was
       reading `--rc-db-*` — a family declared on `.pg-bpmn-host`, not
       on :root. On BPMN it is created with that host and the tokens resolve; on
       Level 1 and Level 2 it is created with `.pg-canvas-host`, the PARENT, so
       all twenty-one references fell through to their light literal fallbacks.
       The same block was dark on BPMN and light on the other two, in dark mode,
       side by side. The fallbacks were doing their job perfectly — they are
       simply light values, and they fired in both themes.

       Craig ruled 2026-08-21, against adopting each host's palette: "Option 1
       is the obvious answer" — the block gets its own tokens and looks the same
       everywhere. It is a floating panel OVER the paper, not a graph element
       (established on #461), and the facts it reports are identical on all
       three surfaces because they are three views over one object model (P2).
       A component showing identical content that deliberately looks different
       in each place asks the user to learn it three times for no information.

       ALIASED TO `--pq-*`, NOT HAND-PICKED. 117 ParaQualis tokens exist as of
       v4.2.666; minting a parallel set is how a fifth un-spined family starts.
       Aliasing means the block inherits the dark ramp for free, which is why
       only the two values below need a dark twin at all.

       The `--rc-db-*` family is deliberately LEFT ALONE. It is scoped
       to `.pg-bpmn-host` because it IS BPMN's palette, and the BPMN dump panel
       still reads it. The bug was a shared component reaching into a
       surface-specific family, not the family being scoped. */
    --rc-db-bg:     var(--pq-surface);
    --rc-db-head:   var(--pq-elevated);
    --rc-db-line:   var(--pq-line);
    --rc-db-rule:   var(--pq-border);
    --rc-db-fg:     var(--pq-ink);
    --rc-db-mute:   var(--pq-ink-muted);
    --rc-db-dim:    var(--pq-slate-400);
    --rc-db-btn-bg: var(--pq-elevated);
    --rc-db-btn-fg: var(--pq-ink);

    /* #1292 — THE MINIMAP CHROME, as tokens, because it needs all THREE theme
       states and a media block only gives one.

       Both minimaps (BPMN and flow-graph V2) had their dark palette in a
       `@media (prefers-color-scheme: dark)` block and nowhere else. So when the
       viewer STATED dark through the old in-app override, the canvas themed and
       the minimap did not, and it sat bottom-right as the brightest thing on the
       screen. #1852 deleted that override (Craig 2026-09-30, "yes (a)": the theme
       follows the computer, #1809), so dark now has one signal: the
       prefers-color-scheme block.

       The comment at the `--pq-*` block says why this is the fix rather than a
       second media block: three states, and "everything above inherits the ramp
       for free, which is the point of aliasing rather than duplicating". These
       were also byte-identical between the two minimaps (charter 6).

       #1846 — one reader now: the Level 2 minimap (.pg-gav2-navigator). The
       BPMN surface's minimap is the adopted editor's own and is themed by the
       editor's --jj-* tokens in bpmn_editor/rc_theme.css. */
    --rc-minimap-bg:     rgba(248,250,252,0.92);
    --rc-minimap-border: #c8d1e0;
    --rc-minimap-shadow: 0 2px 10px rgba(0,0,0,0.12);
}
@media (prefers-color-scheme: dark) {
    :root {
        /* #1917 — a RAISED DARK SURFACE, not an inverted light pill. #1273 lightened
           the ground so a tooltip on a dark page kept an edge; the result was a white
           flash on every hover in dark mode (Craig's dark-contrast sensitivity), over
           the dark menus too. The edge now comes from a step up the slate ramp
           (slate-750, above the menu's #1e2c3f) and a slate-700 border, with light
           ink. Checked against WCAG AA by the contrast tests. */
        --tip-bg: var(--pq-slate-750);
        --tip-fg: #f9fafb;
        --tip-border: var(--pq-slate-700);

        /* Only the four that do not follow a `--pq-*` alias need a dark twin;
           everything above inherits the ramp for free, which is the point of
           aliasing rather than duplicating. */
        --ok: var(--pq-green-300);   /* 10.14 on the dark surface — AA */
        --success: var(--pq-green-300);
        --success-text: var(--pq-green-300);
        --gap-badge-ink: var(--pq-night-900);   /* the gap and vague bars stay pale in dark, so
                                         their badge ink stays dark — measured, not assumed */
        --vague-badge-ink: var(--pq-night-900);
        /* #755 — the data block. Only `dim` needs a twin: the rest alias
           `--pq-*` and inherit the dark ramp, which is the point of aliasing
           rather than duplicating. `--pq-slate-400` is a fixed step on the
           light ramp and reads as a mid-grey on a dark ground, so the dim
           (empty-value) text is lifted to stay legible. */
        --rc-db-dim: #788594;
        /* #1292 — see the light block. */
        --rc-minimap-bg:     color-mix(in srgb, var(--pq-ground) 92%, transparent);   /* #1655: on the ramp */
        --rc-minimap-border: var(--pq-border);
        --rc-minimap-shadow: 0 2px 10px rgba(0,0,0,0.50);
    }
}

/* ── THE DARK RAMP ──────────────────────────────────────────────────────────
   Built from the same hues, lightness rebuilt so contrast holds — not a
   separate hand-picked set. The grounds are the brand deep blue mixed into a
   near-black neutral, so the dark app is a night version of the sunset rather
   than a grey app with blue buttons.

   MEASURED, every one, against the surface #192536:
       white     15.45      mint-050  13.55      cream-050 12.91
       blue-200  10.63      blue-300   8.04      blue-600   4.54
       blue-500   4.88      slate-400  5.95      gold-400   9.43
       orange-500 6.23      coral-400  6.08      peach-200 10.64
   Every one of those clears AA for normal text. That is the whole reason the
   ground was chosen where it was: a lighter ground would have pushed blue-600
   below 4.5 and taken the mid-blues out of use as ink.

   NOTE ON THE TWO DEEPEST BLUES: #276BB2 and #3274C2 measure 3.1-3.8 against
   this ground and therefore CANNOT be ink here. In dark mode they are chrome —
   a filled button's ground, with light ink on top — and the mid and light blues
   take over as text. That inversion is what "the dark ramp is derived from it"
   actually means in practice.

   Two states: a bare `:root` above (light) and this media block (dark, when the
   computer says so). The theme follows the computer (#1809); the explicit
   `[data-theme]` override that once sat below was unreachable and was deleted
   (#1852). */
@media (prefers-color-scheme: dark) {
    :root {
        --pq-ground:    #141517;   /* #1938 — the neutral dark greys of the Option 1 revised artboard */
        --pq-soft:      #25282c;
        --pq-blue-sel:  #1e2a3d;   /* #1938 — the artboard's dark light-blue: the selection */
        --pq-surface:   #1d1f22;
        --pq-elevated:  #25282c;
        --pq-ink:       #e9e7e2;
        --pq-ink-muted: var(--pq-slate-400);   /* 5.95 on surface — AA */
        --pq-accent:    var(--pq-blue-300);    /* 8.04 — the deep blues cannot be ink here */
        --pq-accent-2:  var(--pq-blue-600);    /* 4.54 — AA, the floor of the ramp */
        --pq-line:      #34373c;
        --pq-border:    #34373c;
        --pq-danger:    var(--pq-coral-400);   /* 6.08 — the brand coral IS legible here */
        --pq-on-danger: var(--pq-ground);      /* 7.07 on the coral fill — white measured 2.54 (#1880) */
        --pq-warn:      var(--pq-gold-400);    /* 9.43 */
        /* #1938 — THE PALEST GROUND HAD NO DARK VALUE. These seven names resolve
           to --pq-blue-050 (#e2f1fb) in light, and nothing gave them a dark
           counterpart, so in dark mode they stayed pale under white ink: the QMS
           Structure list's band rows measured 1.15:1 (Test, 2026-10-06), and every
           hover and soft-accent ground on them did the same. In dark they follow
           --primary-pale, the one pale-ground name that already had a dark value
           (the elevated surface), so the dark pale ground is stated once. */
        /* #1938 — and, as in light, neutral grounds and hover; the light blue
           (here its dark twin) is the selection only. */
        --bg-subtle:           var(--pq-soft);
        --rc-subtle-bg:        var(--pq-soft);
        --hover-bg:            color-mix(in srgb, var(--pq-ink) 8%, transparent);
        --bg-hover:            var(--hover-bg);
        --accent-soft:         var(--primary-pale);
        --primary-ultra-light: var(--primary-pale);
        --mist:                var(--pq-soft);
    }
}

/* #1655 — WHICH THEME `light-dark()` READS. Every light literal in an ordinary
   rule carries its dark value as `light-dark(<literal>, <ramp token>)`
   (etc/bin/colour_dark_twins_1655.py), and the function answers from the
   element's used colour-scheme. So the scheme is stated once, here, with the
   same three states as the ramp: follow the OS by default, and let the app's
   own choice win in both directions. It also darkens native controls and
   scrollbars in dark, which the OS setting asks for. */
:root                     { color-scheme: light dark; }
/* #1903 (code review FE-F38) - REDUCED MOTION IS ONE RULE, NOT A LIST. Someone who
   asks their computer for less motion gets none, on every animation and
   transition this stylesheet will ever declare - the same one line the public
   pages carry (public.css, content_mgt.css). It replaced 21 per-component
   blocks that covered 9 of 16 animations and 18 of 97 transitions. A component
   that needs a STILL state in place of its motion (a static progress bar, a
   dimmed loading line) declares only that, beside the component. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; scroll-behavior: auto !important; }
}

/* #1957 — ONE CURSOR PER CONNECTOR DRAG. While a connector handle is held
   (rc_link_tools.js sets the class and the handle's own cursor), that cursor is
   shown everywhere on the page, so it no longer flips with whatever is under the
   pointer. The release (or a cancelled pointer) removes the class. */
html.rc-link-held, html.rc-link-held * { cursor: var(--rc-held-cursor, move) !important; }   /* #2180: no hands */

:root {
    /* #105 — RE-POINTED AT THE PARAQUALIS PALETTE, 2026-08-21.
       These four carried `#1a3a5c` and its family: a navy that is not a
       ParaQualis colour and never was — the issue's own body records it as
       "legacy". They are the brand blues now, so a brand change is one edit at
       the `--pq-*` block rather than a sweep (charter 6). The measured numbers
       are on those definitions. */
    --primary:        var(--pq-blue-900);
    --primary-light:  var(--pq-blue-600);   /* NB a MID-TONE brand blue, NOT a pale tint. Never use
                                    as a background under body text (B-230 lesson). */
    --primary-pale:   var(--pq-blue-050);
    /* #1764 — THE CHROME PAIR, DEFINED ONCE. The domain's chrome background and
       font colour (#1641, Domain config) paint every piece of chrome through
       these two names: the app header and the QMS side panel read them, so one
       setting repaints both (Craig 2026-09-27, "One setting should control
       both"). The fallbacks are the pre-#1641 values, so a domain that states
       nothing renders as it always has. Resolved here, on :root, where the
       backend and app.js set both inputs. */
    --chrome-bg:      var(--domain-chrome-bg, var(--primary));
    --chrome-ink:     var(--domain-chrome-ink, #fff);
    /* #1880 — THE ONE TINT A CHROME GROUND MAY TAKE UNDER TEXT (hover, the
       selected nav item, the org select). Text on chrome is the full chrome ink
       — never a faded one — because white on the default blue is only 5.48:
       80% white measured 4.14, 55% 2.8, and white on an 18% tint 3.84. At 6% it
       holds AA (tests/test_1880_*). Icon-only controls may tint harder. */
    --chrome-tint:    color-mix(in srgb, var(--chrome-ink) 6%, transparent);
    /* #1655 — `--primary` AS TEXT. `--primary` is the domain's Theme Colour
       (injected at page load, unbranched, in both themes), so as INK it is a
       navy on the dark surface: Craig 2026-09-26, "dark blue on a darker blue
       backround". In light this IS --primary, so every text site moved onto it
       renders exactly as before; the dark remap lightens it. */
    --primary-ink:    var(--primary);
    --primary-fg:     var(--pq-accent);   /* primary AS text/border/accent — split from the
                                    bg role so dark mode can lighten it (2026-06-26). It now
                                    follows `--pq-accent`, which IS that lightening: the two
                                    deepest brand blues cannot carry text on a dark ground. */
    --bg:             var(--pq-ground);
    --bg-white:       var(--pq-surface);
    --text:           var(--pq-ink);
    --text-muted:     var(--pq-ink-muted);
    --border:         var(--pq-line);
    --border-subtle:  #ebe8e3;   /* N3 — faint row divider; #1938 neutral */
    /* 2026-06-26 dark-mode: define these explicitly (were referenced but only
       riding #fff fallbacks) so the global dark :root below has a light twin. */
    --surface:        var(--pq-surface);
    --text-primary:   var(--pq-ink);
    --card:           var(--pq-elevated);
    /* #1859 — THE QUOTED PASSAGE ON A FINDING CARD, a pair defined in BOTH
       themes. `.card-quote` had themed text on a fixed light wash, so in dark
       mode white text sat on light grey (2.74:1). Light keeps its look: a 55%
       surface wash over the card. Dark: see the dark :root below. */
    --quote-surface:  color-mix(in srgb, var(--pq-surface) 55%, transparent);
    --quote-text:     var(--pq-ink);

    /* ── Finding-category BASES ────────────────────────────────────────────
       `--{stem}-bar` is the ONE authored colour per category. Every other
       finding colour in the app is derived from it — see the derivation block
       below, and applyFindingColors() in app.js for the arithmetic CSS cannot
       do. The Settings → Branding picker writes these bases onto
       documentElement at runtime, so an inline value wins over the default
       here and the whole family moves with it.
       -text/-badge/-bg/-hi are the card/pill family and they are DERIVED here,
       not authored: each is a color-mix() of the base, so picking a colour
       moves the pill, the badge, the summary row and the highlight with it.
       Nothing in JS writes these four — that matters, because an inline style
       would beat the dark-mode block below and repaint dark pills with
       light-mode maths (the defect this replaced).
       The mixes reproduce the arithmetic they came from exactly: 60% base into
       black is the old mix-toward-black 0.40, 20% into white the old 0.80, 12%
       the old 0.88, 22% the old 0.78. Check with conflict — 60% of #ed1d1d into
       black is #8e1111, which is what this line used to say by hand.
       -hi is TRANSLUCENT rather than a white mix so one value works on any
       background and needs no dark twin. */
    /* #512 — TWO DEFAULTS MOVED, because no ink could rescue them.
   WCAG 2.1 AA needs 4.5:1 for this 10-12px bold badge text. Measured, the old
   values reached AA with NEITHER white nor dark ink — they sat in the contrast
   valley, too light for dark and too dark for white:
       conflict    #ed1d1d   white 4.38   dark 4.05   best 4.38
       unnecessary #3182ce   white 4.03   dark 4.41   best 4.41
   Darkened by 5% and 7% lightness respectively — visually near-identical, same
   hue — so WHITE now passes on both, which is the preferred ink.
       conflict    #ea1313   white 4.56
       unnecessary #2e79c0   white 4.55
   #1310 adds the other half of the same contract: each base is also the visible
   BOUNDARY around a finding, so it must reach 3:1 against both editor grounds.
   Four defaults kept their hue and saturation and moved only far enough in
   lightness to clear that floor; the measured pairs live in the #1310 guard.
   rc_contrast.js still picks badge ink per fill, while the server refuses a
   configured colour that would break either contract (#1248/#1310). */
    --conflict-bar:   #ea1313;

    --gap-bar:        #f1660a;

    --vague-bar:      #93960a;

    --unnecessary-bar:   #2e79c0;

    --ai-bar:   #805ad5;

    /* Inserted (accepted-replacement) palette. --inserted-hi is the only -hi
       with a live consumer (.warn-banner-info, the benign-notice banner); Craig
       flagged 0.32 as "much too strong", so it stays at 16% where the rest of
       the family sits at 22%. That is a tuned value, not drift — leave it. */
    --inserted-bar:   #38a169;

    /* #174 — MISSING: the requirement is entirely ABSENT from the document.
       Distinct from --gap-bar (orange, "it is here but deficient") because the
       WORK is different — add rather than fix — and that is the whole reason it
       is its own category.

       CHOSEN BY MEASUREMENT, after my first choice failed. I picked #a3341f as a
       deep red-brown, reasoning that it sat "adjacent to conflict's red" and read
       as serious. #444's contrast contract then measured it at ONE DEGREE of hue
       from conflict (#ea1313) — the same colour, slightly darker, which as
       translucent fills on a dark page would have been genuinely hard to tell
       apart. #444's scope note says the categories must stay distinguishable FROM
       EACH OTHER as well as legible, and that is exactly the half a person
       eyeballing a swatch does not check.

       Deep magenta at hue 327 is 33 degrees clear of the nearest neighbour, the
       widest gap available in a palette that already occupies red, orange,
       yellow, green, blue and two violets. It reads as grave, which suits an
       absent regulatory requirement, and it is nobody else's colour. */
    --missing-bar:    #ce1b7d;

    /* Two more bases, so EVERY finding a canvas can paint has one — and so the
       Settings picker covers every one of them. Neither is an analyser
       category: `structural` is a finding SOURCE (source='parser'), violet to
       stay distinct from the pinker AI purple; `unknown` is the catch-all for
       a category the taxonomy does not name.

       #1653 — THESE NOW HAVE THE DERIVED SIBLINGS. This comment used to end
       "Neither has -text/-badge/-bg/-hi siblings because nothing renders a card
       or pill for them", and that premise was false by the time it was read:
       the filter row has carried a Structural pill since B-215, and the live
       report showed 53 structural findings on the document Craig was reading.
       So the chip had a hue and no ink for it. Derived here on the same
       formulas as every sibling rather than given a fallback, because a
       fallback would be a second rule for one of the eight.

       #1846 — LIGHTNESS ONLY, +1.6 points (was #854bdf; hue 263.5 and
       saturation unchanged, the #1310 rule for moving a base). The BPMN
       editor's structural "S" badge is a ring of this colour on a --pq-surface
       disc, and #854bdf measured 2.99:1 on the dark surface #192536 — under the
       3:1 WCAG 1.4.11 floor for a graphical object. #8a52e0 measures 3.21:1
       there and 3.39:1 on the dark editor ground, and still carries white text
       at 4.81:1 (the white-ink record in test_finding_badge_contrast.py). */
    --structural-bar:   #8a52e0;

    --unknown-bar:    #718096;

    /* RESOLVED is a STATE, not one of the eight finding hues, and it is on the
       filter row beside them — so it needs the same token shape or it is the
       one chip built by hand. #2f855a is the green the #282 note already names
       for Resolved (green.700), one step DARKER than Inserted's green.600,
       which is the separation that note exists to protect. Nothing is
       re-picked here; the existing colour is given the family's formulas. */
    --resolved-bar:    #2f855a;

    /* BEGIN GENERATED #2058 finding tokens: light - backend/finding_color_tokens.py; do not edit */
    --conflict-bg: color-mix(in srgb, var(--conflict-bar) 12%, white);
    --conflict-hi: color-mix(in srgb, var(--conflict-bar) 22%, transparent);
    --conflict-text: color-mix(in srgb, var(--conflict-bar) 60%, black);
    --conflict-badge: color-mix(in srgb, var(--conflict-bar) 20%, white);
    --gap-bg: color-mix(in srgb, var(--gap-bar) 12%, white);
    --gap-hi: color-mix(in srgb, var(--gap-bar) 22%, transparent);
    --gap-text: color-mix(in srgb, var(--gap-bar) 60%, black);
    --gap-badge: color-mix(in srgb, var(--gap-bar) 20%, white);
    --vague-bg: color-mix(in srgb, var(--vague-bar) 12%, white);
    --vague-hi: color-mix(in srgb, var(--vague-bar) 22%, transparent);
    --vague-text: color-mix(in srgb, var(--vague-bar) 60%, black);
    --vague-badge: color-mix(in srgb, var(--vague-bar) 20%, white);
    --unnecessary-bg: color-mix(in srgb, var(--unnecessary-bar) 12%, white);
    --unnecessary-hi: color-mix(in srgb, var(--unnecessary-bar) 22%, transparent);
    --unnecessary-text: color-mix(in srgb, var(--unnecessary-bar) 60%, black);
    --unnecessary-badge: color-mix(in srgb, var(--unnecessary-bar) 20%, white);
    --ai-bg: color-mix(in srgb, var(--ai-bar) 12%, white);
    --ai-hi: color-mix(in srgb, var(--ai-bar) 22%, transparent);
    --ai-text: color-mix(in srgb, var(--ai-bar) 60%, black);
    --ai-badge: color-mix(in srgb, var(--ai-bar) 20%, white);
    --inserted-bg: color-mix(in srgb, var(--inserted-bar) 12%, white);
    --inserted-hi: color-mix(in srgb, var(--inserted-bar) 16%, transparent);
    --inserted-text: color-mix(in srgb, var(--inserted-bar) 60%, black);
    --inserted-badge: color-mix(in srgb, var(--inserted-bar) 22%, transparent);
    --missing-bg: color-mix(in srgb, var(--missing-bar) 12%, white);
    --missing-hi: color-mix(in srgb, var(--missing-bar) 22%, transparent);
    --missing-text: color-mix(in srgb, var(--missing-bar) 60%, black);
    --missing-badge: color-mix(in srgb, var(--missing-bar) 20%, white);
    --structural-bg: color-mix(in srgb, var(--structural-bar) 12%, white);
    --structural-hi: color-mix(in srgb, var(--structural-bar) 22%, transparent);
    --structural-text: color-mix(in srgb, var(--structural-bar) 60%, black);
    --structural-badge: color-mix(in srgb, var(--structural-bar) 20%, white);
    --unknown-bg: color-mix(in srgb, var(--unknown-bar) 12%, white);
    --unknown-hi: color-mix(in srgb, var(--unknown-bar) 22%, transparent);
    --unknown-text: color-mix(in srgb, var(--unknown-bar) 60%, black);
    --unknown-badge: color-mix(in srgb, var(--unknown-bar) 20%, white);
    --resolved-bg: color-mix(in srgb, var(--resolved-bar) 12%, white);
    --resolved-text: color-mix(in srgb, var(--resolved-bar) 60%, black);
    --resolved-badge: color-mix(in srgb, var(--resolved-bar) 20%, white);
    /* END GENERATED #2058 finding tokens: light */

    /* ── T-118 C8.3 — finding-decoration box palette (DERIVED) ─────────────
       ONE unified bordered-box model for every finding on a canvas: a border
       (`-edge`), a resting fill (`-fill`) and a stronger shade for the ACTIVE
       finding (`-sel`).

       Nothing here is authored. Each triple is a pure reference to its
       category's `--{stem}-bar` base, so moving the base moves the border and
       both fills with it — no JS, and no second value to keep in step. That is
       what lets the Settings → Branding picker, which writes only the bases,
       reach these surfaces at all, and what lets the graph canvas read these
       same vars at render time instead of holding its own hexes.

           -edge = the base
           -fill = base at 18%      -sel = base at 32%

       Two mappings a reader needs: the CSS class carries the raw finding
       category, so `incomplete`→gap, `unnecessary_detail`→unnecessary and
       `ai_suggestion`→ai (see .rc-finding-* further down); and `structural` is
       keyed off a finding's SOURCE (source='parser'), not its category. */








    /* BEGIN GENERATED #2058 finding tokens: box - backend/finding_color_tokens.py; do not edit */
    --rc-find-conflict-edge: var(--conflict-bar);
    --rc-find-conflict-fill: color-mix(in srgb, var(--conflict-bar) 18%, transparent);
    --rc-find-conflict-sel: color-mix(in srgb, var(--conflict-bar) 32%, transparent);
    --rc-find-gap-edge: var(--gap-bar);
    --rc-find-gap-fill: color-mix(in srgb, var(--gap-bar) 18%, transparent);
    --rc-find-gap-sel: color-mix(in srgb, var(--gap-bar) 32%, transparent);
    --rc-find-missing-edge: var(--missing-bar);
    --rc-find-missing-fill: color-mix(in srgb, var(--missing-bar) 18%, transparent);
    --rc-find-missing-sel: color-mix(in srgb, var(--missing-bar) 32%, transparent);
    --rc-find-vague-edge: var(--vague-bar);
    --rc-find-vague-fill: color-mix(in srgb, var(--vague-bar) 18%, transparent);
    --rc-find-vague-sel: color-mix(in srgb, var(--vague-bar) 32%, transparent);
    --rc-find-unnecessary-edge: var(--unnecessary-bar);
    --rc-find-unnecessary-fill: color-mix(in srgb, var(--unnecessary-bar) 18%, transparent);
    --rc-find-unnecessary-sel: color-mix(in srgb, var(--unnecessary-bar) 32%, transparent);
    --rc-find-ai-edge: var(--ai-bar);
    --rc-find-ai-fill: color-mix(in srgb, var(--ai-bar) 18%, transparent);
    --rc-find-ai-sel: color-mix(in srgb, var(--ai-bar) 32%, transparent);
    --rc-find-inserted-edge: var(--inserted-bar);
    --rc-find-inserted-fill: color-mix(in srgb, var(--inserted-bar) 18%, transparent);
    --rc-find-inserted-sel: color-mix(in srgb, var(--inserted-bar) 32%, transparent);
    --rc-find-structural-edge: var(--structural-bar);
    --rc-find-structural-fill: color-mix(in srgb, var(--structural-bar) 18%, transparent);
    --rc-find-structural-sel: color-mix(in srgb, var(--structural-bar) 32%, transparent);
    --rc-find-unknown-edge: var(--unknown-bar);
    --rc-find-unknown-fill: color-mix(in srgb, var(--unknown-bar) 18%, transparent);
    --rc-find-unknown-sel: color-mix(in srgb, var(--unknown-bar) 32%, transparent);
    /* END GENERATED #2058 finding tokens: box */

    /* #325 / #708 — THE THREE DISPOSITION STATES, so a finding's box in the
       document can say what has happened to it. Until now the anchor knew only
       `inserted`, so a rejected or agreed finding's card changed colour and the
       box round its text did not.

       NO NEW BRAND COLOURS, and that is deliberate. #492 makes the Settings
       colour picker the ONLY place a FINDING CATEGORY colour is chosen, and
       these are not categories — they are dispositions, and every one of them
       ALREADY HAS a colour Craig picked, living in two or three other homes:

         resolved — the green from `.stat-pill.resolved` and the card badge,
                    chosen at #611 when he ruled black-on-dark-green out.
         rejected — the muted grey the rejected CARD already wears.
         acted    — the soft green the acted CARD already wears.

       B-215's grammar rule is ONE colour in every home — chip, pill, card, and
       now the anchor. So this adds a fourth home for three existing colours
       rather than three new swatches to the picker. */
    --rc-find-resolved-edge: var(--pq-green-800);
    --rc-find-resolved-fill: color-mix(in srgb, #276749 12%, transparent);
    --rc-find-resolved-sel:  color-mix(in srgb, #276749 26%, transparent);

    --rc-find-acted-edge: #38a169;
    --rc-find-acted-fill: color-mix(in srgb, #38a169 10%, transparent);
    --rc-find-acted-sel:  color-mix(in srgb, #38a169 24%, transparent);

    --rc-find-rejected-edge: var(--text-muted);
    --rc-find-rejected-fill: color-mix(in srgb, var(--text-muted) 8%, transparent);
    --rc-find-rejected-sel:  color-mix(in srgb, var(--text-muted) 20%, transparent);


    /* ── M4 P2 — Process Graph design tokens (v4.0.38) ─────────────────────
       Semantic palette per node type. Originally read via getComputedStyle()
       by the Cytoscape renderer (graph_view.js — retired v4.1.128); today the
       remaining consumers are styles.css's own document-surface rules (the
       --rc-graph-section-* / --rc-sop-card-* uses) — names still stable.
       Dark-mode equivalents live in the "Process Graph dark-mode token
       overrides" @media block much further down this file.
       Memo §4.4 "visual design system" + §4.5 vector zoom. */

    /* :Action — process step (the leaf of the procedure flow) */
    --rc-graph-action-bg: var(--pq-indigo-500);
    --rc-graph-action-border:    #2b3f8c;
    --rc-graph-action-text: var(--pq-white);

    /* :Decision — branching step */
    --rc-graph-decision-bg: var(--pq-amber-500);
    --rc-graph-decision-border:  #8a5e0c;
    --rc-graph-decision-text:    #1a1a1a;

    /* :Section — explanatory chapter card (Scope/Purpose/Definitions/etc.) */
    --rc-graph-section-bg:       #eef4ff;
    --rc-graph-section-border: var(--pq-indigo-500);
    --rc-graph-section-text:     #1a3389;

    /* :Procedure — operational flow container */
    --rc-graph-procedure-bg:     #eaf6ec;
    --rc-graph-procedure-border: #2f8a45;
    --rc-graph-procedure-text:   #1f5a2c;

    /* :Procedures — top-level operational layer (wraps every :Procedure) */
    --rc-graph-procedures-bg:    #dff0e3;
    --rc-graph-procedures-border:#2f8a45;
    --rc-graph-procedures-text:  #1f5a2c;

    /* :Context — page-1 title card (SOP identity at a glance) */
    --rc-graph-context-bg:       #fff8e8;
    --rc-graph-context-border:   #d4a23c;
    --rc-graph-context-text:     #5c3f0d;

    /* Edges — flow + branch + meta */
    --rc-graph-edge-next: var(--pq-grey-500);
    --rc-graph-edge-branch: var(--pq-amber-500);
    --rc-graph-edge-branch-text: #8a5e0c;
    --rc-graph-edge-meta:        #9aa3af;

    /* Selected node outline */
    --rc-graph-selected-border: var(--pq-night-950);

    /* Role chip (in-card; v4.0.38 n8n pattern — no separate :Role nodes) */
    --rc-graph-chip-resp-fg:     #c7b8f0;   /* lavender, on dark Action bg */
    --rc-graph-chip-inv-fg:      #a68bd9;

    /* v4.0.47 — SOP-as-Agent-card palette (Craig 2026-06-01).
       v4.0.48 LIGHTENED — dark navy header read as too "heavy" against
       a light canvas. New recipe: light tinted header, dark text, gold
       badge stays. Card surfaces feel like document-on-paper. */
    --rc-sop-card-bg: var(--pq-white);
    --rc-sop-card-border:        #cbd5e0;
    --rc-sop-card-header-bg:     #eef3f9;
    --rc-sop-card-header-fg: var(--pq-navy-800);
    --rc-sop-card-header-meta:   #718096;
    --rc-sop-card-badge-bg:      #d4a23c;
    --rc-sop-card-badge-fg:      #2d1f00;
    --rc-sop-card-slot-bg: var(--pq-white);
    --rc-sop-card-slot-hover:    #f0f4fa;
    --rc-sop-card-slot-fg:       #2d3748;
    --rc-sop-card-slot-dot: var(--pq-indigo-500);
}

/* #1903 — PLACED AFTER the plain :root that defines --chrome-bg/--chrome-ink: the
   same selector, so the later one wins. Before #1852 the dark rule carried an
   extra attribute test that outranked the plain one; #1852 simplified it to bare
   `:root`, and the banner and the QMS side panel stayed full domain blue in dark
   (regcheck, v4.3.1505). */
/* #1655 — THE CHROME IN DARK (Craig 2026-09-27: "The banner at the top of the
   screen is not following the dark mode motif"). The one chrome setting (#1764)
   still decides it: in dark, the domain's chrome colour keeps its HUE and is
   taken down into the ParaQualis dark ramp, with light ink, so the banner and the
   QMS side panel sit in the same palette as the page instead of a bright slab of
   the domain colour (hue is the constant, readability wins — 2026-07-22). Light
   mode is untouched: these only answer the two dark states. */
@media (prefers-color-scheme: dark) {
    :root {
        --chrome-bg:  color-mix(in oklab, var(--domain-chrome-bg, var(--primary)) 38%, var(--pq-ground));
        --chrome-ink: var(--pq-ink);
    }
}

/* ── 2026-06-26 GLOBAL DARK MODE — the missing :root remap (Phase 0 + 2).
   The app already honours prefers-color-scheme via component blocks (Process
   Graph, TipTap editor); this flips the whole app CHROME by remapping the core
   vars + the finding-category palette. Brand --primary stays (#1a3a5c reads on
   dark). Palette matches the editor/graph dark themes for consistency. ── */
@media (prefers-color-scheme: dark) {
  :root {
    /* core chrome
       #105 — RE-POINTED at the derived dark ramp, 2026-08-21. These were six
       hand-picked greys and one "lifted blue" (#6fa8dc) that is not a
       ParaQualis colour. The ramp above is built from the brand hues with every
       pair's contrast measured, so this block now says WHICH ROLE each token
       plays and the palette says what colour that is — one place to change,
       and the numbers are recorded where the values are.

       The core chrome no longer restates its own greys; what stays here is the
       finding-category derivation below, which is genuinely different in dark
       (the mix TARGETS invert) and has nothing to do with the brand ramp. */
    --bg:            var(--pq-ground);
    --bg-white:      var(--pq-surface);
    --surface:       var(--pq-surface);
    --card:          var(--pq-elevated);
    --primary-pale:  var(--pq-blue-sel);   /* #1938 — the selection in dark */
    --primary-fg:    var(--pq-accent);   /* the brand light blue: 8.04 on the dark surface */
    /* #1655 — the domain's primary as INK, lightened for a dark ground: the
       hue is kept and the lightness adapts (Craig 2026-07-22: hue is the
       constant, readability wins). 30% of the domain colour into the dark
       theme's ink stays light whatever colour an administrator picks. */
    /* #1655, Craig 2026-09-27: "3 different color schemes - we need to get
       this coordinated". Blue TEXT in dark is ONE blue, the accent; the domain's
       own colour shows in its chrome (#1641/#1764), not as a second text blue. */
    --primary-ink:   var(--pq-accent);
    --text:          var(--pq-ink);
    --text-primary:  var(--pq-ink);
    --text-muted:    var(--pq-ink-muted);   /* brand slate: 5.95 on the dark surface — AA */
    /* #1859 — the quote sits in an OPAQUE inset of the ground colour, so its
       contrast does not depend on the category tint bleeding in from the card's
       left edge: white on #111721, 18.4:1. */
    --quote-surface: var(--pq-ground);
    --quote-text:    var(--pq-ink);
    --border:        var(--pq-line);
    --border-subtle: rgba(255,255,255,0.055);   /* N3 — faint row divider on dark */
    /* Finding categories — darken bg/badge, lift text, keep the -bar accent.
       DERIVED from the same base as the light values above, so a colour picked
       in Settings moves the dark pills too. Only the mix TARGETS change: bg and
       badge mix into near-black and the raised surface instead of white, and
       text mixes toward white instead of black — the light-mode direction would
       give pale badges and near-black ink on a dark ground.
       The percentages are calibrated against the hand-tuned values these
       replaced, not invented: conflict lands within a channel or two of the old
       #2a1618 / #5a2a2e / #fc9aa0. A few channels drift up to ~25/255 on the
       blue of gap and the green of vague; that is the cost of one rule
       replacing six hand-picks, and it is the trade that lets the picker reach
       dark mode at all.
       -hi needs no dark twin: it is translucent, so it works on any ground. */
    /* BEGIN GENERATED #2058 finding tokens: dark - backend/finding_color_tokens.py; do not edit */
    --conflict-bg: color-mix(in srgb, var(--conflict-bar) 12%, var(--pq-ground));
    --conflict-badge: color-mix(in srgb, var(--conflict-bar) 27%, var(--pq-elevated));
    --conflict-text: color-mix(in srgb, var(--conflict-bar) 45%, white);
    --gap-bg: color-mix(in srgb, var(--gap-bar) 12%, var(--pq-ground));
    --gap-badge: color-mix(in srgb, var(--gap-bar) 27%, var(--pq-elevated));
    --gap-text: color-mix(in srgb, var(--gap-bar) 45%, white);
    --vague-bg: color-mix(in srgb, var(--vague-bar) 12%, var(--pq-ground));
    --vague-badge: color-mix(in srgb, var(--vague-bar) 27%, var(--pq-elevated));
    --vague-text: color-mix(in srgb, var(--vague-bar) 45%, white);
    --unnecessary-bg: color-mix(in srgb, var(--unnecessary-bar) 12%, var(--pq-ground));
    --unnecessary-badge: color-mix(in srgb, var(--unnecessary-bar) 27%, var(--pq-elevated));
    --unnecessary-text: color-mix(in srgb, var(--unnecessary-bar) 45%, white);
    --ai-bg: color-mix(in srgb, var(--ai-bar) 12%, var(--pq-ground));
    --ai-badge: color-mix(in srgb, var(--ai-bar) 27%, var(--pq-elevated));
    --ai-text: color-mix(in srgb, var(--ai-bar) 45%, white);
    --inserted-bg: color-mix(in srgb, var(--inserted-bar) 12%, var(--pq-ground));
    --inserted-badge: color-mix(in srgb, var(--inserted-bar) 27%, var(--pq-elevated));
    --inserted-text: color-mix(in srgb, var(--inserted-bar) 45%, white);
    --missing-bg: color-mix(in srgb, var(--missing-bar) 12%, var(--pq-ground));
    --missing-badge: color-mix(in srgb, var(--missing-bar) 27%, var(--pq-elevated));
    --missing-text: color-mix(in srgb, var(--missing-bar) 45%, white);
    --structural-bg: color-mix(in srgb, var(--structural-bar) 12%, var(--pq-ground));
    --structural-badge: color-mix(in srgb, var(--structural-bar) 27%, var(--pq-elevated));
    --structural-text: color-mix(in srgb, var(--structural-bar) 45%, white);
    --unknown-bg: color-mix(in srgb, var(--unknown-bar) 12%, var(--pq-ground));
    --unknown-badge: color-mix(in srgb, var(--unknown-bar) 27%, var(--pq-elevated));
    --unknown-text: color-mix(in srgb, var(--unknown-bar) 45%, white);
    --resolved-bg: color-mix(in srgb, var(--resolved-bar) 12%, var(--pq-ground));
    --resolved-badge: color-mix(in srgb, var(--resolved-bar) 27%, var(--pq-elevated));
    --resolved-text: color-mix(in srgb, var(--resolved-bar) 45%, white);
    /* END GENERATED #2058 finding tokens: dark */
    /* #1653 — FOUR CATEGORIES WERE MISSING FROM THIS BLOCK, and the gap was
       silent because the light values still resolve: they are simply the wrong
       derivation on a dark ground.

       `missing` is the live one. It was added by #174 AFTER this block was
       written, so on a dark page it kept `color-mix(missing 60%, black)` as its
       INK — a near-black magenta on a dark surface — and a 20%-into-WHITE badge,
       a pale wash of a colour that has no business being pale here. Every other
       category inverted; this one did not, and nothing pointed at it.

       `structural`, `unknown` and `resolved` are new siblings from this same
       change and are derived here for the same reason, so the eight categories
       and the one state all invert together rather than six of nine. */
  }
}

html, body {
    height: 100%;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
    background: var(--bg);
    color: var(--text);
    overflow: hidden;
}

/* #1641 — a swatch in the ParaQualis range is a BUTTON because pressing it
   does something; the ring is what says so, on focus as well as hover because
   the row is reachable by keyboard. */
.pq-swatch:hover,.pq-swatch:focus-visible{outline:2px solid var(--primary-fg);outline-offset:2px}

/* ── Header ── */
header {
    /* #1641 — THE DOMAIN'S CHROME PAIR IS CONSUMED HERE. Craig asked for a
       background and a font colour per domain and ruled them on 2026-09-20 as
       an ACCENT PAIR on chrome rather than a page ground; the app header is the
       canonical piece of chrome, so this is where the pair lands.

       THE FALLBACKS ARE TODAY'S VALUES, so a domain that states nothing renders
       exactly as it does now: the backend emits no declaration at all when a
       field is empty, and these two var() calls then resolve to --primary and
       white. Nothing changes until an administrator chooses one.
       #1764: the pair and its fallbacks are defined ONCE, as --chrome-bg /
       --chrome-ink on :root, and every piece of chrome reads those. */
    background: var(--chrome-bg);
    color: var(--chrome-ink);
    padding: 0 24px;
    height: 52px;
    display: flex;
    align-items: center;
    gap: 16px;
    flex-shrink: 0;
    border-bottom: 1px solid var(--border);   /* #1938 — the calm bar as drawn */
    /* L-048 (Craig 2026-06-18) — the header is `position:sticky` so it forms a
       stacking context; its z-index CAPS the ☰ menu dropdown inside it. The
       graph palette (.pg-palette, z-index 200) lives outside this context, so at
       the old z-index:100 the palette buttons poked THROUGH the open menu.
       Raise the whole header above the palette + the canvas floats (≤10050) so
       its dropdown wins — the top nav should sit on top of everything. */
    z-index: 10200;
    position: sticky;
    top: 0;
}

.logo {
    display: flex;
    align-items: center;
    gap: 9px;
}

/* #1764 — EVERY CONTROL ON THE BANNER TAKES THE CHROME FONT COLOUR. The
   divider, the version stamp, the organisation picker, Menu, Help and Options
   were fixed white, so on a light chrome (cream, measured on v4.3.1273) they all
   but vanished while the tagline beside them followed the setting. Each is now
   --chrome-ink at the strength the white had, so the default chrome (white ink)
   renders exactly as before. */
.logo-sep {
    width: 1px;
    height: 20px;
    background: color-mix(in srgb, var(--chrome-ink) 25%, transparent);
}

.tagline {
    font-size: 0.82rem;   /* #1880 — full chrome ink; opacity 0.7 measured 3.56 */
}

/* ── Analysis progress ────────────────────────────────────────────────────────
 * #300 (Craig 2026-08-05) — NOTHING IS LEFT TO STYLE. The floating window went at
 * v4.2.64 and became the Progress drawer; the drawer is now burned too. Gone with
 * it here: .status-log, .status-line, .status-latest and .status-error.
 *
 * A refusal now surfaces as .rc-toast.rc-toast-err (see the toast rules further
 * down) and everything else goes to the audit trail only. */

/* ── Layout ── */
.app-wrap {
    display: flex;
    height: calc(100vh - 52px);
    overflow: hidden;
    position: relative;
}

/* ── Left sidebar ── */
.sidebar {
    width: 320px;
    flex-shrink: 0;
    background: var(--bg-white);
    border-right: 1px solid var(--border);
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 0;
    position: relative;
    min-width: 260px;
    max-width: 560px;
    transition: width 0.2s, min-width 0.2s, border 0.2s;
}
.sidebar.sidebar-hidden {
    width: 0 !important;
    min-width: 0 !important;
    overflow: hidden;
    border-right: none;
}

/* ── T-154 P4 — document-window palette + launch popover ───────────────────
   The sidebar is RETIRED: hidden but kept in the DOM so #rc-fw-host remains the
   float panel's home (the /api/analyze checkbox contract reads it wherever it
   lives). Picker → float (📚), Start-Analysis → launch popover (🔍). */
.app-wrap > .sidebar { display: none !important; }

.doc-palette {
    width: 46px;
    flex-shrink: 0;
    background: var(--bg-white);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 12px 0;
}
.doc-palette-btn {
    width: 32px; height: 32px;
    border: 1px solid var(--border);
    background: var(--bg-white);
    border-radius: 6px;
    cursor: pointer;
    font-size: 16px; line-height: 1;
    display: flex; align-items: center; justify-content: center;
    transition: background 0.12s, border-color 0.12s;
}
.doc-palette-btn:hover { background: light-dark(#eef2ff, color-mix(in oklab, #eef2ff 14%, var(--pq-surface))); border-color: #4f6ddb; }
.doc-palette-btn.active { background: light-dark(#e0e7ff, color-mix(in oklab, #e0e7ff 14%, var(--pq-surface))); border-color: #4f6ddb; }

.doc-launch-pop {
    position: fixed;
    left: 56px; top: 96px;
    z-index: 9998;
    background: var(--bg-white);
    /* Option 1c (2026-08-10) — FIXED 820x560: the card cannot grow from the
       inside (that growth is what pushed it off screen, #527). No border — the
       navy .sa-head supplies the top edge; the old 1px hardcoded-blue border
       had no dark twin. */
    border-radius: 10px;
    box-shadow: 0 8px 32px rgba(0,0,0,.28);
    width: 820px;
    max-width: calc(100vw - 24px);
    height: 560px;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.doc-launch-pop.hidden { display: none; }
/* #1761 — Start Analysis is MODAL on both surfaces (Craig 2026-09-27: "the start
   analysis dialog needs to prevent click behinds"). A sibling of the card, one
   z-index below it, that swallows every click meant for the page behind. */
.sa-modal-backdrop {
    position: fixed;
    inset: 0;
    background: var(--rc-scrim);
}
/* Option 1c — the .doc-launch-bar/.doc-launch-title/.doc-launch-x strip is
   deleted; the card's navy .sa-head is the drag handle and close home. */
.doc-launch-body { padding: 0; overflow: hidden; flex: 1; min-height: 0; display: flex; }
.doc-launch-body > .sa-card { flex: 1; min-width: 0; }
/* the relocated trigger section sheds its sidebar chrome inside the popover */
.doc-launch-body > .sidebar-section { padding: 0; border: none; }

/* Drag handle on the right edge of the sidebar */
.sidebar-resizer {
    position: absolute;
    top: 0;
    right: -3px;
    width: 6px;
    height: 100%;
    cursor: col-resize;
    z-index: 20;
    background: transparent;
    transition: background 0.15s;
}

.sidebar-resizer:hover,
.sidebar-resizer.dragging {
    background: var(--primary-light);
    opacity: 0.5;
}

.sidebar-section {
    padding: 16px;
    border-bottom: 1px solid var(--border);
}

.sidebar-section:last-child {
    border-bottom: none;
    margin-top: auto;
}

.section-title {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: var(--text-muted);
    margin-bottom: 10px;
    display: flex;
    align-items: center;
    gap: 6px;
}

.section-title .optional {
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
}


.ich-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-top: 6px;
}


.remove-btn {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text-muted);
    font-size: 0.75rem;
    padding: 1px 4px;
    border-radius: 3px;
    flex-shrink: 0;
    line-height: 1;
}

.remove-btn:hover {
    background: var(--border);
    color: var(--conflict-text);
}

/* eCFR part checkboxes */
.parts-list {
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.parts-list label {
    display: flex;
    align-items: flex-start;
    gap: 7px;
    padding: 5px 7px;
    border-radius: 5px;
    cursor: pointer;
    font-size: 0.8rem;
    line-height: 1.4;
    transition: background 0.15s;
}

.parts-list label:hover {
    background: var(--bg);
}

.parts-list input[type="checkbox"] {
    margin-top: 2px;
    cursor: pointer;
    accent-color: var(--primary-fg);
    flex-shrink: 0;
}

.part-num {
    font-weight: 600;
}

.part-desc {
    color: var(--text-muted);
    font-size: 0.73rem;
}

/* Analysis trigger panel — replaced the 2×3 grid with four explicit controls */
.trigger-panel {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 10px;
    padding: 10px 12px;
    border: 1.5px solid var(--border);
    border-radius: 8px;
    background: var(--bg-subtle, #f8fafc);
    font-size: 0.78rem;
}
/* v3.12.36 (rework of v3.12.32) — trigger-row keeps the 2-col label/select
   layout, but the label column is narrowed from 90px → 56px so the select
   gets enough room for full option text + chevron. Pre-condition: the
   longest label ("Review") fits in 56px. The v3.12.32 ellipsis-truncation
   made the row WORSE (Craig: "you have made space by truncating the labels
   and this is worse") — this iteration removes truncation entirely and
   reclaims the space the labels never needed. */


.trigger-panel input[type="range"] {
    width: 100%;
    font-size: 0.78rem;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--bg);
    color: var(--text-color, inherit);
    font-family: inherit;
}


/* Batch-history delete button — visibly red so it can't be missed.
   Lives on the Tool B "Recent batches" rows; the icon-only siblings
   (Open / Export) didn't read as actions to Craig at first review. */
.batch-history-delete-btn {
    background: light-dark(#fee2e2, color-mix(in oklab, #fee2e2 14%, var(--pq-surface)));
    color: light-dark(#991b1b, color-mix(in oklab, #991b1b 45%, white));
    border: 1px solid light-dark(#fca5a5, color-mix(in oklab, #fca5a5 30%, var(--pq-surface)));
    border-radius: 4px;
    font-size: 0.74rem;
    font-weight: 600;
    padding: 3px 10px;
    cursor: pointer;
    font-family: inherit;
    white-space: nowrap;
}
.batch-history-delete-btn:hover {
    background: light-dark(#fecaca, color-mix(in oklab, #fecaca 14%, var(--pq-surface)));
    border-color: #f87171;
}

/* "Add another analysis" affordance — appears after a successful first run.
   Hidden by the shared `.hidden` utility (#2279 removed a duplicate of it here). */
.add-pass-btn {
    background: var(--bg);
    color: var(--primary-ink, #1a3a5c);
    border: 1.5px solid var(--primary, var(--primary-fg));
    border-radius: 6px;
    font-weight: 600;
    font-size: 0.78rem;
    padding: 6px 10px;
    cursor: pointer;
    font-family: inherit;
    transition: background 0.12s;
}
.add-pass-btn:hover    { background: var(--bg-hover, #f1f5f9); }
.add-pass-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.add-pass-reset {
    background: var(--bg);
    color: var(--text-muted);
    border: 1px solid var(--border);
    border-radius: 6px;
    font-size: 0.74rem;
    padding: 6px 10px;
    cursor: pointer;
    font-family: inherit;
}
.add-pass-reset:hover { background: var(--bg-hover, #f1f5f9); color: var(--text-color); }

/* Coverage panel — one row per analysis run on this SOP */
.coverage-panel {
    margin-top: 10px;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg);
    font-size: 0.72rem;
}
.coverage-panel.hidden { display: none; }
.coverage-panel-header {
    font-weight: 600;
    color: var(--text-muted);
    margin-bottom: 6px;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.coverage-row {
    display: grid;
    grid-template-columns: 28px 1fr auto;
    gap: 6px;
    padding: 4px 0;
    border-top: 1px dashed var(--border);
    align-items: center;
}
.coverage-row:first-of-type { border-top: none; }
.coverage-rindex {
    font-family: monospace;
    font-weight: 700;
    color: var(--primary-fg);
    text-align: center;
    background: var(--bg-subtle);
    border-radius: 3px;
    padding: 1px 4px;
    font-size: 0.66rem;
}
.coverage-meta {
    display: flex;
    flex-direction: column;
    gap: 1px;
    overflow: hidden;
}
.coverage-meta-line { color: var(--text-color); }
.coverage-meta-sub  { color: var(--text-muted); font-size: 0.66rem; }
/* T-065 — per-Challenger-pass model attribution lines under the
   meta-line. Slightly muted vs the main label, monospace so the
   model ids align cleanly when several rows stack. */
.coverage-pass-line {
    color: var(--text-muted);
    font-size: 0.7rem;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    line-height: 1.35;
    padding-left: 8px;
}
.coverage-count {
    font-family: monospace;
    color: var(--text-muted);
    font-size: 0.7rem;
    text-align: right;
}


/* Analyze button */
.analyze-btn {
    width: 100%;
    padding: 11px 16px;
    background: var(--primary);
    color: #fff;
    border: none;
    border-radius: 8px;
    font-size: 0.88rem;
    font-weight: 600;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    transition: background 0.2s, opacity 0.2s;
    letter-spacing: 0.2px;
}

.analyze-btn:hover:not(:disabled) {
    background: var(--primary-light);
}

.analyze-btn:disabled {
    opacity: 0.38;
    cursor: not-allowed;
}

.analyze-btn-stop {
    background: #c53030 !important;
}

.analyze-btn-stop:hover {
    background: #9b2c2c !important;
}

/* Spinner inside the Stop Analysis button */
@keyframes _spin {
    to { transform: rotate(360deg); }
}
.btn-spinner {
    display: inline-block;
    width: 13px;
    height: 13px;
    border: 2px solid rgba(255,255,255,0.4);
    border-top-color: light-dark(#fff, var(--pq-border));
    border-radius: 50%;
    animation: _spin 0.7s linear infinite;
    flex-shrink: 0;
}

/* Analysis-in-progress banner */
/* T-180 — the run countdown + progress line now live IN the top-centre pill
   (moved off the small Stop button, which couldn't fit it). */
/* P4.6b — the flashing ellipsis (.analysis-dots / @keyframes _dots) was RETIRED. The
   banner now shows a steady, advancing "Analysing — Pass N of M" via #analysis-progress-text
   (see _handleProgress). Removed per the house standard against flashing long-op status (L-062). */

/* Framework reset bar */
.fw-reset-btn {
    background: none;
    border: 1px solid var(--border, #dde1e7);
    border-radius: 4px;
    color: var(--text-muted, #64748b);
    cursor: pointer;
    font-size: 0.72rem;
    padding: 3px 8px;
    width: 100%;
    text-align: left;
    transition: background 0.15s, color 0.15s;
}
.fw-reset-btn:hover {
    background: var(--bg-hover, #f1f5f9);
    color: var(--text, #1e293b);
}

/* Chat framework-select button (injected next to list items) */
.chat-fw-select-btn {
    background: none;
    border: 1px solid var(--primary, var(--primary-fg));
    border-radius: 3px;
    color: var(--primary-ink, #1a3a5c);
    cursor: pointer;
    font-size: 0.68rem;
    margin-left: 6px;
    padding: 1px 5px;
    vertical-align: middle;
    white-space: nowrap;
    transition: background 0.15s, color 0.15s;
}
.chat-fw-select-btn:hover {
    background: var(--primary, #1a3a5c);
    color: #fff;
}
.chat-fw-select-btn.fw-unselected {
    background: none;
    border-color: #2b6cb0;
    color: light-dark(#2b6cb0, var(--pq-accent));
}
.chat-fw-select-btn.fw-unselected:hover {
    background: #2b6cb0;
    color: #fff;
}
.chat-fw-select-btn.fw-selected {
    background: #276749;
    border-color: #276749;
    color: #fff;
}
.fw-ref-unselected { border-left: 2px solid #2b6cb0; padding-left: 4px; }
.fw-ref-selected   { border-left: 2px solid #276749; padding-left: 4px; }
td.fw-ref-unselected, td.fw-ref-selected { border-left: none; padding-left: inherit; background: rgba(39,103,73,0.06); }
.chat-fw-btn-wrap  { display: inline-flex; gap: 3px; flex-wrap: wrap; margin-left: 4px; vertical-align: middle; }
.ich-code-editable { cursor: text; border-bottom: 1px dashed transparent; }
.ich-code-editable:hover { border-bottom-color: var(--primary, var(--primary-fg)); }

/* Legend */
.legend {
    display: flex;
    flex-direction: column;
    gap: 9px;
}


/* ── Main document area ── */
.main {
    flex: 1;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    min-width: 0;
    order: 0;
}

/* Placeholder */
.placeholder {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 40px;
    color: var(--text-muted);
}

/* P4.50 — empty Document screen renders an entry toolbar at the TOP (like the QMS /
   Graph surfaces) instead of the centred upload prompt. */
.placeholder.placeholder--toolbar {
    justify-content: flex-start;
    align-items: stretch;
    padding: 0;
    text-align: left;
}


/* #279 (2026-07-20) — the P4.47/B-230 "current row" highlight is BURNED (Craig's
   ruling: the tint lingered and "doesn't really help anything"). Hover tint stays. */

/* P4.51 — login welcome panel (System / Organisation / Personal) */
.welcome-overlay { position: fixed; inset: 0; z-index: calc(var(--z-dialog) + 30); display: flex; align-items: center;
    justify-content: center; background: rgba(15,23,42,0.45); padding: 20px; }
.welcome-overlay .wp-card { border: 1px solid var(--border); border-radius: 12px;
    background: var(--surface, #fff); box-shadow: 0 12px 40px rgba(15,23,42,.22);
    max-width: 580px; width: 100%; overflow: hidden; }
.wp-head { display: flex; align-items: center; gap: 12px; padding: 16px 18px; background: var(--primary); color: #fff; }
.wp-head .wp-hi { font-size: 16px; font-weight: 600; }

.wp-sections { display: grid; grid-template-columns: 1fr 1fr 1fr; }
.wp-sec { padding: 16px; border-right: 1px solid var(--border); }
.wp-sec:last-child { border-right: none; }
.wp-sec-hd { display: flex; align-items: center; gap: 7px; font-size: 11px; font-weight: 700;
    letter-spacing: .05em; text-transform: uppercase; color: var(--primary-ink); margin-bottom: 10px; }
.wp-row { font-size: 13px; line-height: 1.5; margin-bottom: 4px; }
.wp-row:empty { display: none; }
.wp-row b { color: var(--text-primary); }
.wp-row span { color: var(--text-secondary); }
.wp-row .wp-badge { display: inline-block; font-size: 11px; font-weight: 600; padding: 3px 8px;
    border-radius: 999px; background: var(--primary); color: #fff; }   /* B-093: .wp-row .wp-badge beats `.wp-row span` (else dark-on-navy, unreadable) */
.wp-foot { padding: 12px 18px; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; }
/* #91 — the messages section is a FULL-WIDTH ROW BENEATH the three columns,
   not a fourth column beside them. Craig 2026-08-10: "under all three of the
   existing messages. It runs the length of the dialog as a second level".

   The `:has()` rules that widened .wp-sections to four columns are DELETED
   rather than adjusted — the grid is three columns again, unconditionally, and
   has no opinion about whether a message exists. One fewer thing coupled to
   another. Prose needs width; a message in a quarter of a modal is one nobody
   reads. */
.wp-messages-sec {
    border-top: 1px solid var(--border);
    padding: 14px 18px;
}
.wp-messages-sec.hidden { display: none; }
/* The messages themselves: each one labelled with WHOSE it is. An announcement
   from ParaQualis and one from the reader's own admin carry different weight, and
   a stack of unattributed paragraphs hides that. */
.wp-msg { margin-bottom: 10px; }
.wp-msg:last-child { margin-bottom: 0; }
.wp-msg-from {
    font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
    color: var(--text-muted); margin-bottom: 2px;
}
.wp-msg-body { font-size: 13px; line-height: 1.5; white-space: pre-wrap; }

@media (max-width: 560px) {
    /* The `:has()` twin is gone with the fourth column (#91, 2026-08-16) — the
       grid no longer varies with whether a message exists. */
    .wp-sections { grid-template-columns: 1fr; }
    .wp-sec { border-right: none; border-bottom: 1px solid var(--border); }
}

/* P4.6a — Analysis History panel (prior runs for a document) */
.analysis-history-popup { position: fixed; z-index: 1200; width: 420px; max-width: calc(100vw - 24px);
    border: 1px solid var(--border); border-radius: 12px; background: var(--surface, #fff);
    box-shadow: 0 12px 36px rgba(15,23,42,.20); overflow: hidden; }
.analysis-history-header { display:flex; align-items:center; gap:7px; padding:11px 14px;
    background: var(--surface-2, #f6f7fb); border-bottom:1px solid var(--border); font:600 13px/1 system-ui; cursor:move; }
.analysis-history-body { max-height: 60vh; overflow-y: auto; padding: 4px 0; }
.ahist-row { padding:9px 14px; border-bottom:1px solid var(--border, #eceef3); }
.ahist-row:last-child { border-bottom:none; }
.ahist-when { font:600 12.5px/1.3 system-ui; color: var(--text-primary); }
.ahist-meta { font:12px/1.4 system-ui; color: var(--text-secondary); }
.ahist-fw { font:11.5px/1.4 system-ui; color: var(--text-secondary); margin-top:2px; }
.ahist-empty, .ahist-loading { padding: 16px; text-align:center; color: var(--text-secondary); font:13px/1.4 system-ui; }

/* P4.6a — Run Mode toggle (Design 1): explicit Add-to-existing vs Fresh */

.runmode-opt { flex:1; border:none; background: var(--surface,#fff); padding:6px 8px; font:600 11.5px/1.2 system-ui; cursor:pointer; color: var(--text-secondary,#667); }
.runmode-opt + .runmode-opt { border-left:1px solid var(--border,#cfd2da); }
.runmode-opt.active { background: var(--primary,#4f6ddb); color:#fff; }


/* ── Start-Analysis card — option 1c "Section rail" (design handoff 2026-08-10,
   documentation/design/ui/design_handoff_analysis_window). One fixed-geometry card rendered by
   RCAnalysis.buildLaunchBody() into BOTH #doc-launch-body and the graph popover.
   Fallback literals point at the real token values so dark mode (the
   prefers-color-scheme blocks) inherits without a second block. ── */
.sa-card { display: flex; flex-direction: column; height: 100%; overflow: hidden;
    font: 13px system-ui, -apple-system, sans-serif; color: var(--text-primary, #2d3748); }

/* header — the drag handle and the window's top edge */
.sa-head { flex: none; display: flex; align-items: center; gap: 11px;
    padding: 13px 12px 12px 18px; background: var(--primary, #1a3a5c); color: #fff;
    cursor: move; user-select: none; border-radius: 10px 10px 0 0; }
.sa-head-ic { width: 17px; height: 17px; }
.sa-title { flex: 1; margin: 0; font: 600 1rem/1.2 system-ui; color: #fff; }
.sa-doc-ctx { font: 11.5px/1 system-ui; color: rgba(255,255,255,.6); }


/* body — rail | panes */
.sa-body { flex: 1; min-height: 0; display: grid; grid-template-columns: 196px 1fr; }
.sa-rail { background: var(--primary-pale, #eef3f9); border-right: 1px solid light-dark(#dbe4ee, var(--pq-border));
    padding: 14px 10px; display: flex; flex-direction: column; gap: 3px; min-height: 0; }
/* #1761 — QE Expert's families list: its heading and its tickable rows. */
.sa-qe-fams-hd { font: 600 11px/1.3 system-ui; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: .04em; padding: 0 4px 6px; }
.sa-qe-fam { display: flex; align-items: flex-start; gap: 6px; padding: 4px 8px;
    font: 12px/1.3 system-ui; color: var(--text); cursor: pointer; }
.sa-qe-fam:hover { background: var(--hover-bg); }
.sa-qe-fam input { margin: 2px 0 0; flex: none; }
.sa-qe-fam:focus-within { outline: 2px solid var(--primary-fg); outline-offset: -2px; }
/* #2397/#2412 — RegPacks (frontend/rc_regsets.js), Settings > RegPacks. Rebuilt on
   Craig's test (2026-10-25): one top bar (pack dropdown, actions, Add, the Packs menu)
   and ONE scrolling list of the pack's documents with frozen headers; a framework item
   of several documents is a group in that list. Theme tokens only, so light and dark
   both follow the user's setting; the global prefers-reduced-motion rule stills the spinner. */
.rs-page { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.rs-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; padding: 6px 8px;
    border: 1px solid var(--border); border-radius: 8px; background: var(--card); }
.rs-pick { display: inline-flex; align-items: center; gap: 6px; font-size: 0.82rem; color: var(--text-secondary); }
.rs-pick select { min-width: 220px; max-width: 360px; font-weight: 600; color: var(--text); }
.rs-bar-group { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.rs-barmenu { position: relative; margin-left: auto; }
.rs-barmenu-panel { position: absolute; right: 0; top: calc(100% + 4px); z-index: var(--z-dialog, 50); width: min(380px, 90vw);
    display: flex; flex-direction: column; gap: 8px; padding: 10px; border: 1px solid var(--border); border-radius: 8px;
    background: var(--surface); box-shadow: 0 6px 20px color-mix(in srgb, var(--text) 18%, transparent); }
.rs-jobs { border-top: 1px solid var(--border); padding-top: 6px; }
.rs-jobs h5 { margin: 0 0 4px; font-size: 0.8rem; color: var(--text-secondary); }
.rs-job-list { list-style: none; margin: 0; padding: 0; max-height: 40vh; overflow-y: auto; }
.rs-job { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; padding: 5px 0; font-size: 0.82rem; border-bottom: 1px solid var(--border-subtle); }
.rs-job-name { font-weight: 600; }
.rs-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.rs-form { display: flex; flex-direction: column; gap: 6px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px;
    background: var(--bg-subtle); font-size: 0.85rem; max-width: 560px; }
.rs-form label { display: flex; flex-direction: column; gap: 2px; }
.rs-form label.rs-check, .rs-check { flex-direction: row; display: inline-flex; align-items: center; gap: 6px; font-size: 0.82rem; }
.rs-form h6 { margin: 0; font-size: 0.85rem; }
.rs-list-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; }
.rs-filter { width: 280px; max-width: 100%; }
/* The scroll container owns the scroll, so the header row sticks to ITS top: a structural
   freeze, not a z-index layer over the page. separate borders keep sticky cells' lines. */
.rs-scroll { max-height: 68vh; overflow: auto; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); }
.rs-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 0.82rem; }
.rs-table thead th { position: sticky; top: 0; z-index: 1; background: var(--card); text-align: left; font-weight: 600;
    color: var(--text-secondary); padding: 5px 6px; border-bottom: 1px solid var(--border); white-space: nowrap; }
.rs-table td { padding: 3px 6px; border-bottom: 1px solid var(--border-subtle); vertical-align: middle; }
.rs-table tr[tabindex]:focus-visible { outline: 2px solid var(--primary-fg); outline-offset: -2px; }
.rs-table tr.rs-off td { opacity: 0.6; }
.rs-doc[draggable="true"] { cursor: grab; }
.rs-doc:hover td { background: var(--hover-bg); }
.rs-grp td { background: var(--bg-subtle); border-top: 1px solid var(--border); font-weight: 400; }
.rs-grp-name { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.rs-grp-name > .ic { width: 15px; height: 15px; color: var(--primary-fg); }
.rs-name-edit { min-width: 240px; font-weight: 600; }
.rs-mem td:first-child { padding-left: 28px; box-shadow: inset 3px 0 0 var(--primary-fg); }
.rs-carried td { background: var(--hover-bg); font-style: italic; }
/* Where a drop lands: a line before or after the row, or the whole header for "into". */
.rs-drop-before td { box-shadow: inset 0 2px 0 var(--primary-fg); }
.rs-drop-after td { box-shadow: inset 0 -2px 0 var(--primary-fg); }
.rs-drop-into td { background: var(--hover-bg); box-shadow: inset 0 0 0 1px var(--primary-fg); }
.rs-cite-row td { background: var(--bg-subtle); }
.rs-cite { display: flex; flex-direction: column; gap: 4px; max-width: 560px; padding: 4px 0 4px 28px; }
.rs-try { display: flex; flex-direction: column; gap: 3px; font-size: 0.8rem; color: var(--text-secondary); }
.rs-addr { max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rs-addr a { color: var(--primary-fg); }
.rs-ident { overflow-wrap: anywhere; }
.rs-ord { display: inline-block; min-width: 1.6em; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.rs-force { font-size: 0.8rem; }
.rs-actions { white-space: nowrap; }
.rs-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.rs-link { background: none; border: none; padding: 0; color: var(--primary-fg); text-decoration: underline; cursor: pointer; font: inherit; }
.rs-icon { background: none; border: 1px solid transparent; border-radius: 4px; padding: 2px 4px; cursor: pointer; color: var(--text-secondary); }
.rs-icon:hover:not(:disabled) { border-color: var(--border); color: var(--text); }
.rs-icon:disabled { opacity: 0.35; cursor: default; }
.rs-icon .ic { width: 14px; height: 14px; }
.rs-ctx { position: fixed; z-index: var(--z-dialog, 50); min-width: 220px; display: flex; flex-direction: column; padding: 4px;
    border: 1px solid var(--border); border-radius: 8px; background: var(--surface);
    box-shadow: 0 6px 20px color-mix(in srgb, var(--text) 18%, transparent); }
.rs-ctx button { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border: none; border-radius: 5px; background: none;
    color: var(--text); font-size: 0.85rem; text-align: left; cursor: pointer; }
.rs-ctx button:hover:not(:disabled), .rs-ctx button:focus-visible { background: var(--hover-bg); outline: none; }
.rs-ctx button:disabled { opacity: 0.4; cursor: default; }
.rs-ctx .rs-ctx-danger { color: var(--danger); }
.rs-ctx .ic { width: 15px; height: 15px; }
.rs-chip { font-size: 0.72rem; padding: 1px 6px; border: 1px solid var(--border); border-radius: 10px; color: var(--text-secondary); white-space: nowrap; }
.rs-n { font-size: 0.78rem; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.rs-muted { color: var(--text-secondary); font-size: 0.82rem; }
.rs-ok { color: var(--ok); font-size: 0.82rem; }
.rs-warn { color: var(--pq-warn); font-size: 0.82rem; }
.rs-err { color: var(--danger); }
.rs-file { cursor: pointer; }
.rs-status { display: flex; align-items: flex-start; gap: 8px; margin: 0; padding: 7px 10px; border-radius: 6px; border: 1px solid var(--border);
    border-left-width: 4px; background: var(--surface); font-size: 0.88rem; }
.rs-status > span { flex: 1; }
.rs-status-ok { border-left-color: var(--ok); }
.rs-status-warn { border-left-color: var(--pq-warn); }
.rs-status-err { border-left-color: var(--danger); }
.rs-busy { display: inline-flex; align-items: center; gap: 8px; margin: 2px 0; color: var(--text-secondary); font-size: 0.82rem; }
.rs-spin { width: 14px; height: 14px; flex: none; border: 2px solid var(--border); border-top-color: var(--primary-fg);
    border-radius: 50%; animation: spin 0.9s linear infinite; }
.rs-report { border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; font-size: 0.85rem; }
.rs-report ul { margin: 4px 0 6px 18px; padding: 0; }
/* #2195 — a feature the organisation's plan does not include is not offered (RCPlan,
   app.js). !important because the controls marked carry display rules of their own. */
[data-plan-excluded] { display: none !important; }

/* #1761 — the families QE Expert searches: a scrolling list under "Review & run"
   (Craig 2026-09-27: "a separate list underneath Review and Run which is a multi
   select scrolling list"). It takes the rail's spare height and scrolls inside it,
   so the window never resizes. Theme tokens only. */
.sa-qe-fams { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
    margin-top: 12px; border-top: 1px solid var(--border); padding-top: 10px; }
.sa-qe-fams-list { flex: 1 1 auto; min-height: 4.5em; overflow-y: auto;
    border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--card); padding: 3px 0; }
/* .sa-qe-fams-hd and the .sa-qe-fam row share the background-documents rules (#2085, above). */
/* #1761 — one of QE Expert's suggestion rows. Its layout lives HERE rather than
   inline because an inline display beats the [hidden] attribute: on v4.3.1273 a
   row narrowed out of QE's search kept display:flex and stayed on screen. The
   [hidden] rule restates what the attribute means for this flex row. */
.rc-qe-row { display: flex; gap: 6px; align-items: baseline; padding: 3px 2px;
    font-size: 12px; cursor: pointer; }
.rc-qe-row[hidden] { display: none; }
.sa-rail-item { display: flex; align-items: center; gap: 9px; text-align: left;
    border: none; border-radius: 7px; padding: 9px 11px; font: 600 12.5px/1.2 system-ui;
    cursor: pointer; background: transparent; color: light-dark(#41556e, var(--pq-accent));
    transition: background .12s, color .12s; }
.sa-rail-item:hover { background: rgba(26,58,92,.07); }
.sa-rail-item.active { background: var(--primary, #1a3a5c); color: #fff; }
.sa-rail-item.active:hover { background: var(--primary, #1a3a5c); }
.sa-rail-item .ic { width: 15px; height: 15px; }
.sa-rail-label { flex: 1; }
.sa-rail-count { font: 600 11px/1 system-ui; border-radius: 10px; padding: 3px 7px;
    background: rgba(26,58,92,.11); color: inherit; }
.sa-rail-foot { margin-top: auto; font: 11px/1.5 system-ui; color: light-dark(#7d8ea3, var(--pq-ink-muted)); }

/* panes — all in the DOM, one visible; only the framework list scrolls */
.sa-panes { min-width: 0; min-height: 0; display: flex; }
.sa-pane { flex: 1; min-width: 0; min-height: 0; }
.sa-pane-fw { padding: 16px 18px; display: flex; flex-direction: column; gap: 10px; }
.sa-fw-row { display: flex; gap: 9px; align-items: center; }
.sa-fw-searchwrap { position: relative; flex: 1; display: block; }
.sa-fw-searchic { position: absolute; left: 9px; top: 8px; width: 15px; height: 15px;
    stroke: #9aa5b1; pointer-events: none; }
.sa-fw-search { width: 100%; box-sizing: border-box; padding: 7px 9px 7px 30px;
    border: 1px solid light-dark(#cfd6de, var(--pq-border)); border-radius: 6px; background: var(--surface, #fff);
    color: var(--text-primary, #2d3748); font: 13px system-ui; }
.sa-fw-qe-btn { display: inline-flex; align-items: center; gap: 6px;
    font: 600 12px/1.2 system-ui; padding: 6px 10px; border: 1px solid var(--primary, #1a3a5c);
    border-radius: 7px; background: transparent; color: var(--primary-ink, #1a3a5c);
    cursor: pointer; transition: background .12s, color .12s; }
.sa-fw-qe-btn:hover { background: var(--primary, #1a3a5c); color: #fff; }
.sa-fw-qe-btn .ic { width: 14px; height: 14px; }
.sa-fw-qe-out:not(:empty) { max-height: 160px; overflow: auto; }
.sa-fw-mount { flex: 1; min-height: 0; overflow-y: auto;
    border: 1px solid var(--border, #e2e8f0); border-radius: 8px; padding: 8px 6px; }

.sa-pane-set { padding: 16px 18px; display: flex; flex-direction: column; gap: 15px;
    overflow-y: auto; }
.sa-field > label { display: block; font: 600 12px/1 system-ui; color: light-dark(#5a6472, var(--pq-ink-muted)); margin-bottom: 6px; }
.sa-select { width: 100%; max-width: 100%; box-sizing: border-box; padding: 7px 9px;
    border: 1px solid light-dark(#cfd6de, var(--pq-border)); border-radius: 6px; background: var(--surface, #fff);
    color: var(--text-primary, #2d3748); font: 13px system-ui; }
.sa-scope, .sa-helper { max-width: 440px; }
.sa-depth, .sa-mode { max-width: 440px; }
.sa-helper { font: 11.5px/1.5 system-ui; color: light-dark(#8a93a3, var(--pq-ink-muted)); margin-top: 6px; }
.sa-runmode-row .sa-seg { max-width: 360px; }
.sa-seg { display: flex; border: 1px solid light-dark(#cfd6de, var(--pq-border)); border-radius: 7px; overflow: hidden; }
.sa-seg .sa-runmode-opt { flex: 1; border: none; background: var(--surface, #fff);
    padding: 7px 8px; font: 600 12px/1.2 system-ui; cursor: pointer; color: light-dark(#66717f, var(--pq-ink-muted));
    transition: background .12s, color .12s; }
.sa-seg .sa-runmode-opt + .sa-runmode-opt { border-left: 1px solid light-dark(#cfd6de, var(--pq-border)); }
.sa-seg .sa-runmode-opt.active { background: var(--primary, #1a3a5c); color: #fff; }
.sa-est { font: 12px/1.4 system-ui; color: var(--text-muted, #718096); }

.sa-pane-run { padding: 18px; overflow-y: auto; }
.sa-eyebrow { font: 700 10.5px/1 system-ui; letter-spacing: .06em; text-transform: uppercase;
    color: light-dark(#8a93a3, var(--pq-ink-muted)); margin-bottom: 10px; }
.sa-review { display: grid; grid-template-columns: 120px 1fr;
    border: 1px solid var(--border, #e2e8f0); border-radius: 8px; overflow: hidden;
    font: 12.5px/1.5 system-ui; }
.sa-review-k { padding: 10px 12px; background: light-dark(#f7f9fc, var(--pq-elevated)); color: light-dark(#5a6472, var(--pq-ink-muted)); font-weight: 600;
    border-bottom: 1px solid var(--border-subtle, #eef1f4); }
.sa-review-v { padding: 10px 12px; border-bottom: 1px solid var(--border-subtle, #eef1f4); }
.sa-review-k:nth-last-child(2), .sa-review-v:last-child { border-bottom: none; }
.sa-run-note { margin-top: 14px; padding: 11px 13px; background: var(--primary-pale, #eef3f9);
    border-radius: 7px; font: 12px/1.55 system-ui; color: light-dark(#40506a, var(--pq-accent)); }

/* footer — labelled actions (T-164; the icon-only pair was the "weak" defect) */
.sa-foot { flex: none; display: flex; align-items: center; gap: 10px;
    padding: 12px 16px; border-top: 1px solid var(--border, #e2e8f0); background: light-dark(#fbfcfd, var(--pq-surface)); }
.sa-fwnote { flex: 1; font: 12px/1.4 system-ui; color: var(--text-muted, #718096); margin: 0; }
.sa-fwnote.warn { color: light-dark(#b45309, color-mix(in oklab, #b45309 45%, white)); }
.sa-fwnote.ok   { color: light-dark(#1c7a3f, color-mix(in oklab, #1c7a3f 45%, white)); }
/* #2099 — the analysis's price, beside the Start button (light and dark). */
.sa-credit-quote { font: 12px/1.4 system-ui; white-space: nowrap; margin-right: 8px; color: var(--text-muted, #718096); }
.sa-credit-quote[data-state="cost"] { font-weight: 600; color: var(--text, #1f2937); }
/* #2247 — a refused run (an unavailable model): the refusal colours .rc-tell-warn
   uses, wrapping, because the sentence carries its next step. */
.sa-credit-quote[data-state="refused"] { white-space: normal; font-weight: 600; padding: 4px 8px;
    border-radius: 6px; background: var(--gap-bg); color: var(--gap-text); }
.sa-foot .sa-cancel { padding: 7px 14px; border-radius: 8px;
    border: 1px solid var(--primary-fg, #1a3a5c); background: light-dark(#fff, var(--pq-surface));
    color: var(--primary-fg, #1a3a5c); font: 500 0.82rem system-ui; cursor: pointer;
    transition: background .12s; }
.sa-foot .sa-cancel:hover { background: light-dark(#e8edf5, var(--pq-elevated)); }
.sa-foot .sa-start { display: inline-flex; align-items: center; gap: 7px;
    padding: 7px 14px; border-radius: 8px; border: none;
    background: var(--primary, #1a3a5c); color: #fff; font: 600 0.82rem system-ui;
    cursor: pointer; transition: background .12s; }
.sa-foot .sa-start:hover { background: var(--primary-light, #2a5a8c); }
.sa-foot .sa-start .ic { width: 15px; height: 15px; }
.sa-foot .sa-start:disabled { opacity: .38; cursor: not-allowed; }

/* ── WT-2 (Craig 2026-07-19) — centre-screen analysis countdown ──────────────
   A non-blocking "Analysing… m:ss / ~m:ss" (elapsed / estimate) pinned to the
   middle of the screen while a run is live, so it visibly progresses even when
   the top-centre pill is out of the user's eyeline. pointer-events:none keeps
   it from intercepting clicks. Colours are the shared design tokens, which are
   already theme-aware (the prefers-color-scheme remap), so this reads correctly
   in light AND dark automatically.
   The overrun red is a single mid-red legible on both surfaces. */
/* #575/#431 — the bespoke document analysis pill (spinner + "Analysing…" +
   Cancel) is BURNED, not restyled: the document now mounts the shared
   .pg-run-chip (one look, one arithmetic — RCAnalysisTimer). The clock span
   keeps its id-name class inside the chip but takes the chip's own typography,
   so nothing here survives. Charter 7 — git remembers the old pill. */


.placeholder h2 {
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--text);
    margin-bottom: 8px;
}

.placeholder p {
    font-size: 0.87rem;
    max-width: 380px;
    line-height: 1.6;
}

/* Loading */
.loading {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    color: var(--text-muted);
}

.spinner {
    width: 38px;
    height: 38px;
    border: 3px solid var(--border);
    border-top-color: var(--primary-fg);
    border-radius: 50%;
    animation: spin 0.75s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

.loading-msg {
    font-size: 0.95rem;
    color: var(--text);
}

.loading-sub {
    font-size: 0.82rem;
}

/* Results */
.results {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.results-bar {
    background: var(--bg-white);
    border-bottom: 1px solid var(--border);
    padding: 12px 20px;
    display: flex;
    align-items: center;
    gap: 14px;
    flex-shrink: 0;
    flex-wrap: wrap;
}


.stats {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

/* #442 — the host slot the ONE pill row is moved into on Level 1 / Level 2 /
   BPMN. The chips themselves carry all their own styling (`.rc-chip`, #1653 —
   this said `.stat-pill` until the chip replaced it), which is
   the point of moving the node rather than rebuilding it: the canvas cannot drift
   from the document in appearance any more than it can in counts.
   Only the surrounding strip is described here — a quiet band under the graph
   toolbar, matching `.results-bar`'s padding and separator so the two surfaces
   read as the same piece of furniture in two places. Theme tokens throughout, so
   it follows light/dark without a second rule. */
.rc-pillrow-host {
    padding: 8px 20px;
    border-top: 1px solid var(--border);
    background: var(--bg-white);
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    flex-shrink: 0;
}
.rc-pillrow-host.hidden { display: none; }

/* ── #1653 · THE FINDING CHIP ────────────────────────────────────────────────
   Craig, 2026-09-20, on the pills this replaces: they "look a little 1980s", and
   he wants "something a more 2026, something lighter in terms of look. More
   professional, more constructed and engineered". Ruled from specimens: "I agree
   with your assessment C and E as stated."

   WHAT MADE THE OLD ONE HEAVY WAS EVERYTHING AT ONCE — a 20px fully-round
   capsule, weight 700, UPPERCASE with letter-spacing, and a saturated fill. Each
   is defensible alone; together they shout. The chip turns all four down and
   spends the saturation in ONE place instead: a solid bar of the category's own
   hue at the leading edge, which is also the only part a reader needs to tell
   eight categories apart at a glance.

   TWO WEIGHTS OF ONE TREATMENT, not two components.
     .rc-chip--cap   an 8px full-height block of the hue — the chip standing alone
                     (the filter row), where nothing else carries the colour.
     .rc-chip--rule  a 3px leading border instead — the chip ON a finding card,
                     where the card is already tinted with the same hue. A full
                     cap there would be a third coloured field in one object.

   THE CAP IS CLIPPED BY THE FRAME, not rounded itself: `overflow:hidden` on the
   frame plus `align-items:stretch` is what makes it meet the border exactly at
   the radius, at any font size, with no second radius to keep in step.

   THE COUNT CELL IS MONOSPACE WITH TABULAR FIGURES AND A MIN-WIDTH because the
   filter row is read as a COLUMN of numbers as often as a row of labels — "0"
   and "147" must occupy the same box or the eye cannot compare them. That is
   what the hairline and the slightly distinct ground behind it are for too: the
   count is a different KIND of fact from the name, so it gets its own cell
   rather than being run into the label as "147 Conflicts". */
.rc-chip {
    display: inline-flex;
    align-items: stretch;
    border: 1px solid var(--border);
    border-radius: 3px;
    overflow: hidden;
    background: var(--bg-white);
    font-size: 12px;
    font-weight: 500;
    line-height: 1;
    cursor: pointer;
    user-select: none;
    vertical-align: middle;
    transition: opacity 0.15s, border-color 0.15s;
}
/* A chip that only labels (a finding card's category) is not clickable. */
.rc-chip.rc-chip--static { cursor: default; }

.rc-chip-cap {
    width: 8px;
    flex: 0 0 8px;
    background: var(--chip-bar, var(--text-muted));
}
.rc-chip--rule { border-left: 3px solid var(--chip-bar, var(--text-muted)); }

.rc-chip-name {
    padding: 6px 9px 5px;
    /* #1665 — 600 -> 400. Craig, 2026-09-20: "they are showing bold and I
       think the sandbox did not do that - i would prefer it look lighter so
       not bold".

       WORTH BEING CLEAR ABOUT WHOSE MISTAKE THIS WAS: the weight came from my
       own #1653 specification, which called for 600 on the name cell, and the
       sandbox did the same. So the build matched the spec exactly and the SPEC
       was wrong. Nothing was implemented incorrectly; the thing that needed
       changing was the decision. The count cell keeps its own weight — it is a
       number and reads as one — so this is the name alone. */
    font-weight: 400;
    color: var(--chip-ink, var(--text));
    white-space: nowrap;
}
.rc-chip-count {
    padding: 6px 8px 5px;
    /* #1665 — `--mono` (:root, ~line 216) is this exact stack. Restating it here
       is the same defect this item exists to remove, one concept with two
       definitions, just in a value rather than a selector. */
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
    font-size: 11.5px;
    color: var(--text-muted);
    border-left: 1px solid var(--border);
    background: var(--bg);
    min-width: 30px;
    text-align: right;
    box-sizing: border-box;
}

.rc-chip.pill-dimmed { opacity: 0.45; }
.rc-chip:not(.rc-chip--static):hover { border-color: var(--chip-bar, var(--border)); }

/* ONE MAP, BOTH VOCABULARIES, and the aliasing is deliberate rather than hidden.
   The filter row calls the category `gap`, `unnecessary` and `ai`; `normCat()`
   and the finding cards call the same three `incomplete`, `unnecessary_detail`
   and `ai_suggestion`. Two names for one fact is a defect, but it is not THIS
   change's defect to rename — the class names reach the analyser payload and the
   stored category. So both spellings are listed here, against one set of tokens,
   where a reader can see they are the same thing. Reconciling the vocabularies
   is tracked separately.

   THE THREE HAND-PICKED CHIPS ARE GONE, folded into the derived tokens they were
   always approximating: resolved's #c6f6d5/#276749, inserted's #f0fff4/#2f855a,
   and structural's #ede5fa/#5b21b6 with its separate dark twin. Each was a pair
   of literals maintained by hand, and structural's existed only because an alpha
   background had composited dark-on-dark in dark mode (2026-07-22). The derived
   tokens invert at the source now, so the twin is not needed and cannot drift.
   Craig's ruling from that day still governs and is satisfied by construction:
   the HUE carries the meaning and never changes; LIGHTNESS adapts per theme.
   Full rule set: documentation/design/ui/theme_colour_rules.md §1 and §7. */
.rc-chip.conflict            { --chip-bar: var(--conflict-bar);    --chip-ink: var(--conflict-text); }
.rc-chip.gap,
.rc-chip.incomplete          { --chip-bar: var(--gap-bar);         --chip-ink: var(--gap-text); }
.rc-chip.missing             { --chip-bar: var(--missing-bar);     --chip-ink: var(--missing-text); }
.rc-chip.vague               { --chip-bar: var(--vague-bar);       --chip-ink: var(--vague-text); }
.rc-chip.unnecessary,
.rc-chip.unnecessary_detail  { --chip-bar: var(--unnecessary-bar); --chip-ink: var(--unnecessary-text); }
.rc-chip.ai,
.rc-chip.ai_suggestion       { --chip-bar: var(--ai-bar);          --chip-ink: var(--ai-text); }
.rc-chip.inserted            { --chip-bar: var(--inserted-bar);    --chip-ink: var(--inserted-text); }
.rc-chip.resolved            { --chip-bar: var(--resolved-bar);    --chip-ink: var(--resolved-text); }
.rc-chip.structural          { --chip-bar: var(--structural-bar);  --chip-ink: var(--structural-text); }
.rc-chip.unknown             { --chip-bar: var(--unknown-bar);     --chip-ink: var(--unknown-text); }
/* #2053 — a finding not yet handled, on the Level 1 / Level 2 / BPMN rows. The
   app's danger token, not a category's colour (a picked Conflict colour must not
   repaint it), and it inverts with the scheme: 4.85:1 on white, 6.50:1 on the dark
   surface. */
.rc-chip.unresolved          { --chip-bar: var(--danger);          --chip-ink: var(--danger); }
/* #2053 — the same chip at the density of a canvas row. */
.rc-chip--compact { flex-shrink: 0; font-size: 10px; }
.rc-chip--compact .rc-chip-name { padding: 2px 6px; }

@media (prefers-color-scheme: dark) {
    .finding-id-badge.finding-id-structural { background: #271f3f; color: #c4b5fd; border-color: #4c3a7a; }
}

/* The card's identifying badge, structural variant — it carries a human TITLE now
   ("Headings jump from 1.0 to 4.0"), not the raw SF-…-135 id (which is the rcUid and meant
   nothing to a reader). The id lives in the tooltip; it is still the key reports and
   dispositions use. Craig 2026-07-12: "the message is a bit cryptic". */
.finding-id-badge.finding-id-structural {
    /* 2026-07-22 — same alpha-composites-into-the-page defect as .stat-pill.structural
       above, and the same fix: the two rgba values computed against white and made opaque
       (0.12 → #ede5fa, 0.35 → #ccb4f2). Light mode unchanged; dark twin below. */
    background: #ede5fa;
    color: #5b21b6;
    border: 1px solid #ccb4f2;
    font-family: inherit;          /* it is prose now, not an identifier — don't monospace it */
    font-weight: 600;
    letter-spacing: 0;
    text-transform: none;
    max-width: 100%;
    white-space: normal;           /* a real sentence may wrap */
}


/* ── Coverage badge ── */
.coverage-badge {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
    padding: 3px 12px;
    font-size: 0.70rem;
    color: var(--text-muted);
    background: var(--primary-pale);
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}
.coverage-label { font-weight: 600; margin-right: 2px; }
.coverage-item {
    background: var(--bg-white);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 1px 7px;
    white-space: nowrap;
}
.coverage-run-date { opacity: 0.65; font-size: 0.88em; margin-left: 3px; }
/* 2026-04-29: docx-preview is now THE primary .docx renderer (mammoth retired).
   docx-preview controls its own page geometry (sized in real units, body
   content positioned relative to a page wrapper). Neutralise the host
   container's padding, border, max-width, and font so docx-preview's layout
   isn't shrunk into a 960px box and clipped — that causes table-headers and
   body paragraphs to overlap. */
.doc-body.docx-render {
    background: light-dark(#e6e6e6, var(--pq-line));
    padding: 8px 0;
    border: none;
    border-radius: 0;
    max-width: none;
    margin: 0;
    font-family: inherit;
    font-size: inherit;
    line-height: inherit;
    word-break: normal;
}
.doc-body.docx-render .docx-wrapper {
    background: transparent;
    padding: 0;
}
.doc-body.docx-render .docx-wrapper > section.docx {
    /* docx-preview uses CSS Paged Media-style sizing on each section.docx (the "page"). */
    margin: 0 auto 12px;
    box-shadow: 0 2px 6px rgba(0,0,0,0.12);
    background: var(--bg-white);
    /* 2026-04-29: docx-preview's own CSS sets `display: flex; flex-flow:
       column` on section.docx, which interacts badly with the negative
       margin-top docx-preview puts on the page header (header sits in the
       padding-top zone with `margin-top: calc(headerMargin - topMargin)` —
       a negative value). In flex column layout that negative margin doesn't
       reliably push subsequent siblings, so the body article ends up
       overlapping the title-block table. Force display: block — flow
       layout handles the negative-margin pattern correctly: header sits in
       the header zone, article naturally follows at header.bottom regardless
       of header content height. The flex-only `margin-bottom: auto` trick
       on the article (which pushed footer to section bottom) becomes a
       no-op; for a rendered preview that's fine — footer just ends up
       right after the body, which matches user expectation. */
    display: block !important;
}
.doc-body.docx-render section.docx > article {
    /* Drop the flex-only margin-bottom: auto trick when block layout is forced */
    margin-bottom: 0 !important;
    /* v3.7.50: removed `padding-top: 1px` (was margin-collapse defence
       based on a wrong diagnosis — the actual bug was the title-block
       table width, fixed in v3.7.47-49). */
}
/* v3.7.48: docx-preview sets `display: flex` on the header element. In
   flex layout, `width: 100%` on a child doesn't force expansion to the
   full container — flex items size by content unless explicitly
   stretched. So v3.7.47's `width: 100%` on the title-block table wasn't
   actually filling the 672px-wide header (table stayed at 591px,
   content-sized). Force header back to block layout and the standard
   width rules apply. The header naturally has only block-style children
   (paragraphs + the title-block table), so block layout is the correct
   default — flex was just the docx-preview default that didn't fit. */
.doc-body.docx-render section.docx > header {
    display: flow-root !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
    margin-top: 0 !important;
    /* v3.7.55: docx-preview gives the header `background: rgb(26,58,92)`
       (dark blue) — translated from page-header shading that's NOT in
       the original Word visual. Cell row 1 has gray shading; rows 2-3
       are transparent (white in original). The dark-blue header bg
       therefore shows through rows 2-3, making them appear blue with
       white text (also docx-preview-set). Original has white bg +
       black text. Force the header bg transparent and the text black
       to restore the original visual. The row-1 gray cell shading
       stays (it's per-cell, not on the header). */
    background-color: transparent !important;
}
.doc-body.docx-render section.docx > header,
.doc-body.docx-render section.docx > header * {
    color: black !important;
}
/* v3.7.47: title-block table forced to span full content width. Word's
   original render treats the page header as margin-to-margin; docx-preview
   emits `width: auto` which sizes only to the column widths total and
   leaves the right column too narrow, causing the right-side header logo
   to wrap and right-aligned dates to truncate. */
.doc-body.docx-render section.docx > header table {
    width: 100% !important;
}
/* 2026-04-29: hanging-indent for bulleted lists (Craig: "the line should
   then go to the indent level — this would line things up").
   docx-preview emits each bulleted paragraph as
     p.docx-num-X-N {
       display: list-item;
       list-style-position: inside;
       text-indent: -18pt;
       margin-left: 18pt;
     }
     p.docx-num-X-N::before { content: "<bullet>\9 "; }
   That's the classic hanging-indent pattern but it relies on
   marker_width == |text-indent|. Our marker is "<bullet><tab><space>" —
   wider than 18pt because CSS `\9` is rendered with default `tab-size: 8`.
   Result: first-line text starts ~6px to the right of the left margin;
   wrapped lines start exactly at the left margin — wrapped text appears
   LEFT of where first-line text begins. Misalignment.
   Fix: remove the negative text-indent and absolutely position the ::before
   marker just to the LEFT of the content edge. With text-indent: 0 and
   the marker out of flow, all lines (first + wrapped) align at the same
   x-position — clean hanging indent regardless of marker width or
   tab-rendering. */
.doc-body.docx-render p[class*="docx-num"] {
    text-indent: 0 !important;
    position: relative;
}
.doc-body.docx-render p[class*="docx-num"]::before {
    position: absolute !important;
    right: 100% !important;
    margin-right: 4pt !important;
    /* Keep the original content / counter-increment / font-family from
       docx-preview's generated rule — we override only positioning. */
}
/* 2026-04-29: Word stores `<w:trHeight w:val="..." w:hRule="exact"/>` on
   title-block table rows; docx-preview translates this to inline
   `height: <Npx>` on each `<tr>`. When that row gets MORE content than
   the exact height (or when a multi-line cell on one row pushes content
   into the next row's space), body paragraphs overlap the table —
   Craig's GCP SOP screenshot showed "To define the procedure..." body
   text bleeding through the dark blue title-block banner. Override the
   inline height to `auto` so rows size to content. The previous
   `ignoreHeight: true` option only affects the PAGE section's minHeight
   (per docx-preview source), not row heights — so this CSS-side override
   is what actually resolves the overlap. Trade-off: any intentionally
   tall decorative row gets compacted to its content; for SOPs this is
   the right call. */
.doc-body.docx-render tr {
    height: auto !important;
}
.doc-body.docx-render td,
.doc-body.docx-render th {
    overflow: visible;
}
/* (BURNED — B-115 WP3, v4.1.279) docx-preview .hl display rule removed. */

/* Saved Analyses list — compact card rows replacing the old table layout. */
.saved-analyses-list { display: flex; flex-direction: column; gap: 6px; }
.saved-empty {
    padding: 24px 12px;
    text-align: center;
    color: var(--text-muted);
    font-size: 0.88rem;
}
.saved-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg-white);
    transition: background 0.12s, border-color 0.12s;
}
.saved-row:hover { background: var(--hover-bg); border-color: var(--primary-fg); }
.saved-row-main { flex: 1; min-width: 0; }
.saved-row-name {
    font-weight: 600;
    font-size: 0.88rem;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.saved-row-meta {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 0.78rem;
    color: var(--text-muted);
    margin-top: 2px;
}
.saved-row-meta .saved-by { font-style: italic; }
.saved-count {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 10px;
    font-weight: 600;
    font-size: 0.74rem;
}
.saved-count.count-zero { background: light-dark(#e8f5e8, var(--pq-elevated)); color: light-dark(#2a7a2a, color-mix(in oklab, #2a7a2a 45%, white)); }
.saved-count.count-low  { background: light-dark(#fff5d4, color-mix(in oklab, #fff5d4 14%, var(--pq-surface))); color: light-dark(#946000, color-mix(in oklab, #946000 45%, white)); }
.saved-count.count-mid  { background: light-dark(#fde2c4, color-mix(in oklab, #fde2c4 14%, var(--pq-surface))); color: light-dark(#b35400, color-mix(in oklab, #b35400 45%, white)); }
.saved-count.count-high { background: light-dark(#fde0e0, color-mix(in oklab, #fde0e0 14%, var(--pq-surface))); color: light-dark(#b03030, color-mix(in oklab, #b03030 45%, white)); }
.saved-row-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
}
.saved-row-actions .btn-sem {
    padding: 4px 10px;
    font-size: 0.78rem;
}
.saved-row-actions .saved-export,
.saved-row-actions .saved-delete {
    padding: 4px 8px;
    min-width: 30px;
}
.saved-row-actions .saved-delete {
    color: light-dark(#c53030, color-mix(in oklab, #c53030 45%, white));
    border-color: #c53030;
}
.saved-row-actions .saved-delete:hover {
    background: #c53030;
    color: #fff;
}
/* "+N new" badge inside stat pills */
.pill-new-badge {
    display: inline-block;
    font-size: 0.68rem;
    font-weight: 700;
    color: light-dark(#2a7a2a, color-mix(in oklab, #2a7a2a 45%, white));
    background: light-dark(#e8f5e8, var(--pq-elevated));
    border-radius: 8px;
    padding: 0 5px;
    margin-left: 4px;
    vertical-align: middle;
}

/* ── Quality Expert Chat modal ── */
.qe-chat-messages {
    flex: 1;
    overflow-y: auto;
    padding: 16px 20px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-height: 0;
    background: var(--bg);
}
.qe-chat-intro {
    color: var(--text-muted);
    font-size: 0.83rem;
    font-style: italic;
    text-align: center;
    padding: 24px 16px;
    line-height: 1.6;
}
.qe-chat-row          { display: flex; }
.qe-chat-row-user     { justify-content: flex-end; }
.qe-chat-row-asst     { justify-content: flex-start; }
.qe-chat-bubble {
    max-width: 82%;
    padding: 9px 13px;
    border-radius: 10px;
    font-size: 0.84rem;
    line-height: 1.55;
    word-break: break-word;
}
.qe-bubble-user {
    background: var(--primary);
    color: #fff;
    border-radius: 12px 12px 3px 12px;
}
.qe-bubble-asst {
    background: var(--bg-white);
    border: 1px solid var(--border);
    border-radius: 12px 12px 12px 3px;
    color: var(--text);
}
.qe-bubble-asst table { border-collapse: collapse; width: 100%; margin: 6px 0; font-size: 0.82rem; }
.qe-bubble-asst td    { padding: 5px 8px; border: 1px solid var(--border); vertical-align: top; }
.qe-bubble-asst ul    { padding-left: 18px; margin: 4px 0; }
.qe-bubble-asst li    { margin-bottom: 3px; }
.qe-bubble-asst h1, .qe-bubble-asst h2, .qe-bubble-asst h3 { margin: 8px 0 4px; font-size: 0.9rem; }
.qe-chat-footer {
    display: flex;
    gap: 8px;
    padding: 10px 14px;
    border-top: 1px solid var(--border);
    background: var(--bg-white);
    flex-shrink: 0;
}
.qe-chat-input {
    flex: 1;
    resize: none;
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 7px 10px;
    font-size: 0.85rem;
    font-family: inherit;
    background: var(--bg-white);
    color: var(--text);
    line-height: 1.4;
}
.qe-chat-input:focus { outline: none; border-color: var(--primary-light, #4a3899); }
.qe-chat-send-btn {
    background: var(--primary);
    color: #fff;
    border: none;
    border-radius: 6px;
    padding: 0 14px;
    font-size: 1.1rem;
    cursor: pointer;
    align-self: flex-end;
    height: 36px;
    transition: background 0.15s;
}
.qe-chat-send-btn:hover    { filter: brightness(1.15); }
.qe-chat-send-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.qe-thinking { color: var(--text-muted); font-style: italic; }


/* ── Domain panel (the system admin's Domain tab; #2024) ── */


.domain-builtin-badge {
    font-size: 0.68rem;
    padding: 1px 5px;
    border-radius: 3px;
    background: var(--primary);
    color: #fff;
    border: 1px solid var(--primary-fg);
    font-weight: 600;
}


.file-tab {
    padding: 8px 16px;
    font-size: 0.78rem;
    font-weight: 500;
    cursor: pointer;
    border: none;
    border-bottom: 2px solid transparent;
    background: none;
    color: var(--text-muted);
    white-space: nowrap;
    transition: color 0.15s, border-color 0.15s;
    display: flex;
    align-items: center;
    gap: 6px;
}

.file-tab:hover {
    color: var(--text);
}

.file-tab.active {
    color: var(--primary-fg);
    border-bottom-color: var(--primary-fg);
    font-weight: 600;
}


/* Source file badge on finding cards */
.source-badge {
    font-size: 0.68rem;
    background: rgba(0,0,0,0.07);
    border-radius: 4px;
    padding: 2px 6px;
    color: var(--text-muted);
    margin-top: 5px;
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Summary view (all-files tab) */
.summary-file-block {
    background: var(--bg-white);
    border: 1px solid var(--border);
    border-radius: 8px;
    margin-bottom: 20px;
    overflow: hidden;
}

.summary-file-header {
    background: var(--primary);
    color: #fff;
    padding: 10px 16px;
    font-size: 0.85rem;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 10px;
}

.summary-file-stats {
    display: flex;
    gap: 8px;
    margin-left: auto;
}

.summary-findings {
    padding: 12px 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.summary-row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 8px 10px;
    border-radius: 6px;
    font-size: 0.8rem;
    line-height: 1.5;
    cursor: pointer;
    transition: filter 0.15s;
}

.summary-row:hover { filter: brightness(0.95); }
.summary-row.conflict    { background: var(--conflict-bg); }
.summary-row.incomplete { background: var(--gap-bg); }
.summary-row.vague       { background: var(--vague-bg); }
.summary-row.unnecessary_detail { background: var(--unnecessary-bg); }
.summary-row.ai_suggestion      { background: var(--ai-bg); }

.summary-row-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    margin-top: 4px;
    flex-shrink: 0;
}

.summary-row-dot.conflict    { background: var(--conflict-bar); }
.summary-row-dot.incomplete { background: var(--gap-bar); }
.summary-row-dot.vague       { background: var(--vague-bar); }
.summary-row-dot.unnecessary_detail { background: var(--unnecessary-bar); }
.summary-row-dot.ai_suggestion      { background: var(--ai-bar); }

.summary-row-body { flex: 1; min-width: 0; }
.summary-row-ref  { font-size: 0.72rem; color: var(--text-muted); margin-top: 2px; }

/* ICH guideline collapsible groups */
.ich-group-toggle {
    cursor: pointer;
    user-select: none;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.ich-group-toggle:hover {
    color: var(--primary-fg);
    background: var(--hover-bg);
    border-radius: 3px;
}

.group-arrow {
    font-size: 0.7rem;
    opacity: 0.6;
    flex-shrink: 0;
}

.ich-group-items {
    display: none;       /* collapsed by default */
    padding-left: 2px;
}

.ich-group-items.ich-group-open {
    display: block;
}


/* Parts list grouped */
.parts-group-label {
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--primary-fg);
    padding: 8px 7px 3px;
    display: block;
}

.parts-loading {
    font-size: 0.8rem;
    color: var(--text-muted);
    padding: 6px 7px;
}

/* Document text viewer */
.doc-scroll {
    flex: 1;
    overflow-y: auto;
    overflow-x: visible;
    padding: 28px 32px;
    position: relative;
}

.doc-body {
    background: var(--bg-white);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 28px 32px;
    font-family: 'Calibri', 'Segoe UI', Arial, sans-serif;
    font-size: 0.84rem;
    line-height: 1.65;
    word-break: break-word;
    max-width: 960px;
    margin: 0 auto;
}

/* A.2r-2b — #doc-body is a STACK of per-document surfaces (one editor or legacy
   render each); exactly one is visible (inline display toggled in JS). A surface
   is a transparent pass-through so the page chrome on .doc-body (or the grey
   .doc-body.docx-render page-viewer) shows around its content. */
.rc-surface {
    display: block;
    background: transparent;
    border: 0;
    padding: 0;
    margin: 0;
}


/* Styled document blocks */
.doc-h1 {
    font-size: 1.15rem;
    font-weight: 700;
    margin: 1.3em 0 0.35em;
    color: var(--primary-fg);
    border-bottom: 2px solid var(--border);
    padding-bottom: 3px;
    font-family: 'Calibri', 'Segoe UI', Arial, sans-serif;
    line-height: 1.3;
}

.doc-h2 {
    font-size: 1.0rem;
    font-weight: 700;
    margin: 1.1em 0 0.3em;
    color: var(--primary-fg);
    font-family: 'Calibri', 'Segoe UI', Arial, sans-serif;
    line-height: 1.35;
}

.doc-h3 {
    font-size: 0.93rem;
    font-weight: 700;
    margin: 0.95em 0 0.25em;
    color: var(--text);
    font-family: 'Calibri', 'Segoe UI', Arial, sans-serif;
    line-height: 1.4;
}

.doc-h4, .doc-h5, .doc-h6 {
    font-size: 0.88rem;
    font-weight: 700;
    margin: 0.85em 0 0.2em;
    color: var(--text);
    font-family: 'Calibri', 'Segoe UI', Arial, sans-serif;
}

.doc-body-block {
    margin: 0.25em 0;
    line-height: 1.65;
}

.doc-list-item {
    margin: 0.2em 0 0.2em 1.6em;
    line-height: 1.7;
    position: relative;
}

.doc-list-item::before {
    content: "•";
    position: absolute;
    left: -1.1em;
    color: var(--text-muted);
}

.doc-list-num-item {
    margin: 0.2em 0 0.2em 1.6em;
    line-height: 1.7;
}

/* Indentation levels for nested list/body paragraphs */
.doc-indent-1 {
    margin-left: 1.6em;
    padding-left: 1.5em;
}
.doc-indent-2 {
    margin-left: 3.2em;
    padding-left: 3em;
}
.doc-indent-3 {
    margin-left: 4.8em;
    padding-left: 4.5em;
}
.doc-indent-4 {
    margin-left: 6.4em;
    padding-left: 6em;
}
.doc-indent-5 {
    margin-left: 8.0em;
    padding-left: 7.5em;
}

/* Nested list items: bump on top of the base margin already set */
.doc-list-item.doc-indent-1,
.doc-list-num-item.doc-indent-1 { margin-left: 3.2em; }
.doc-list-item.doc-indent-2,
.doc-list-num-item.doc-indent-2 { margin-left: 4.8em; }
.doc-list-item.doc-indent-3,
.doc-list-num-item.doc-indent-3 { margin-left: 6.4em; }
.doc-list-item.doc-indent-4,
.doc-list-num-item.doc-indent-4 { margin-left: 8.0em; }
.doc-list-item.doc-indent-5,
.doc-list-num-item.doc-indent-5 { margin-left: 9.6em; }

/* Legacy pipe-delimited table rows (old archives) */
.doc-table-row {
    margin: 0.15em 0;
    padding: 4px 8px;
    background: light-dark(#f8fafc, var(--pq-elevated));
    border-left: 3px solid var(--border);
    font-size: 0.8rem;
    line-height: 1.5;
}

/* Proper HTML table rendering (new parser) */
.doc-table-wrap {
    overflow-x: auto;
    margin: 0.75em 0;
}

.doc-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.81rem;
    line-height: 1.45;
    table-layout: fixed;
}
.doc-table td, .doc-table th {
    overflow-wrap: break-word;
    word-break: normal;
}

.doc-table th,
.doc-table td {
    border: 1px solid var(--border);
    padding: 5px 9px;
    vertical-align: top;
    text-align: left;
}

.doc-table th {
    background: light-dark(#eef2f7, var(--pq-elevated));
    font-weight: 700;
    font-family: 'Calibri', 'Segoe UI', Arial, sans-serif;
    font-size: 0.79rem;
    color: var(--text);
}

.doc-table td {
    background: var(--bg-white);
}

.doc-table tr:nth-child(even) td {
    background: light-dark(#f8fafc, var(--pq-elevated));
}

.doc-table-cell.doc-table-merged {
    color: var(--text-muted);
    font-style: italic;
}
.doc-indent-6 { padding-left: 9em; }
.doc-indent-7 { padding-left: 10.5em; }
.doc-indent-8 { padding-left: 12em; }

/* Alignment overrides */
.doc-align-center  { text-align: center; }
.doc-align-right   { text-align: right; }
.doc-align-justify { text-align: justify; }

/* Inline images embedded in Word documents */
.doc-inline-img {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 0.5em auto;
    border-radius: 2px;
}

/* ── the resize grip ──────────────────────────────────────────────────────────
   The old handle was a 6px strip with `background: transparent` — invisible until
   you hovered the exact six pixels. Craig, after months of daily use: "I can't drag
   it." That is not a control, it is a secret. A 10px hit area, with a grip you can
   SEE. (The sidebar resizer has the identical defect — L-089.) */
/* #513 — THE GRIP IS THE DRAWER'S EDGE, NOT A GAP.
   Craig 2026-08-01: "the tabs are still clear of the drawer". They were, by ten
   transparent pixels. The rail rides at the host's left edge (#453) and the
   host's leftmost child is this grip, so a `background: transparent` grip put a
   10px strip of canvas between the tab and the drawer it belongs to — the tab
   read as floating rather than as the drawer's handle.
   It is NOT closed by moving the rail inward: the grip runs the full height, so
   overlapping the tabs would leave a resize dead-zone the height of the rail.
   The 10px is functional and stays; what was wrong is that it was painted as
   nothing. Painted as the drawer's own surface, the tab abuts a continuous edge
   exactly as it abuts the window edge when the drawer is shut, and the centre
   pill still says "drag me". */
.rc-rz {
    position: relative;
    width: 10px;
    flex-shrink: 0;
    cursor: col-resize;
    background: var(--bg-white);
    z-index: 20;
    display: flex;
    align-items: center;
    justify-content: center;
    border-left: 3px solid var(--primary);
    box-shadow: -6px 0 16px -10px rgba(15, 23, 42, 0.20);
}
.rc-rz::before {
    content: '';
    width: 3px;
    height: 34px;
    border-radius: 2px;
    background: var(--border);
    transition: background 0.15s, height 0.15s;
}
.rc-rz:hover::before,
.rc-rz.dragging::before { background: var(--primary-fg); height: 56px; }
.rc-rz.rc-hidden { display: none; }

/* ── the drawer ─────────────────────────────────────────────────────────────── */
.rc-drawer {
    --rc-w: 308px;
    width: var(--rc-w);
    flex-shrink: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    background: var(--bg-white);
    /* #513 — the LEFT rule now lives on the grip (.rc-rz), which sits
       immediately to this drawer's left and carries the edge the tab abuts.
       Keeping it here too drew two hairlines 10px apart. */
    transition: width 0.24s ease, opacity 0.18s ease;
}
/* #513 — no `border-left-width: 0` any more: the drawer no longer HAS a left
   border to collapse. Its 3px signature edge lives on the grip, and the grip is
   display:none when the drawer is shut, so the edge goes with it. */
.rc-drawer.rc-shut { width: 0; opacity: 0; }
/* while dragging, the width must track the cursor exactly — no easing */
.app-wrap.rc-dragging .rc-drawer { transition: none; }
/* children hold their width, so a closing drawer WIPES its content rather than
   squashing it through a 240ms reflow */
.rc-drawer-head, .rc-drawer-body { min-width: var(--rc-w); box-sizing: border-box; }

.rc-drawer-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 24px 24px 18px;
    flex-shrink: 0;
    border-bottom: 1px solid var(--border);
    background: linear-gradient(180deg,
            var(--primary-pale, #eef3f9) 0%,
            var(--bg-white, #ffffff) 100%);
    position: relative;
}
.rc-drawer-head h2 {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text);
    margin: 0;
    flex: 1;
    white-space: nowrap;
}
.rc-drawer-body {
    flex: 1;
    overflow-y: auto;
    padding: 20px 22px 28px;
    background: var(--bg, #f0f2f5);
}
.rc-drawer > .rc-drawer-head {
    background: var(--primary);
    border-bottom: none;
    box-shadow: none;
    padding: 8px 14px;
    min-height: 64px;                  /* all heads match the Findings depth — its controls set the height (Craig 2026-07-13) */
    box-sizing: border-box;
    flex-wrap: wrap;
    align-content: center;
    row-gap: 4px;
}
.rc-drawer > .rc-drawer-head::after { display: none; }   /* kill the stray gradient hairline that rule leaks onto the rail */
.rc-drawer > .rc-drawer-head h2 { color: #fff; }

/* the Findings head carries controls built for a LIGHT header — flip them to a
   translucent-white treatment so they stay legible on the navy bar. */
/* #1800 — the sort select ("Document order") is one of those controls. It kept
   its light-header muted text, so on the blue bar it read as near-black on
   blue while "Auto-debate" beside it read clearly. Same rule, same colours. */
.rc-drawer > .rc-drawer-head .run-all-debates-btn,
.rc-drawer > .rc-drawer-head .finding-sort-select {
    color: #fff;
    border-color: rgba(255, 255, 255, 0.35);
    background: rgba(255, 255, 255, 0.06);
}
.rc-drawer > .rc-drawer-head .run-all-debates-btn:hover,
.rc-drawer > .rc-drawer-head .run-all-debates-btn.is-active,
.rc-drawer > .rc-drawer-head .finding-sort-select:hover:not(:disabled) {
    background: rgba(255, 255, 255, 0.20);
    border-color: rgba(255, 255, 255, 0.6);
    color: #fff;
}
/* The OPEN list is drawn by the browser on its own surface, so the options take
   the page's text and surface, never the bar's white. */
.rc-drawer > .rc-drawer-head .finding-sort-select option {
    color: var(--text);
    background: var(--pq-surface);
}
@media (prefers-contrast: more) {
    .rc-drawer > .rc-drawer-head .run-all-debates-btn,
    .rc-drawer > .rc-drawer-head .finding-sort-select { border-color: currentColor; }
}


/* the tab rail lifts with the panels (overrides the flatter base shadow) */
.rc-tab {
    box-shadow: -2px 2px 10px rgba(15, 23, 42, 0.10);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 7px;
    width: 30px;
    padding: 11px 0 13px;
    cursor: pointer;
    border: 1px solid var(--border);
    border-right: none;
    border-radius: 9px 0 0 9px;
    background: var(--surface);
    color: var(--text-muted);
    transition: background 0.15s, color 0.15s, border-color 0.15s;
}

/* the body is a light well the cards sit ON, with even padding + gaps between
   cards (sandbox .dscroll). Scoped so it never hits the graph panel body. */
.rc-drawer > .rc-drawer-body { background: var(--bg-white); }
.rc-drawer > .rc-drawer-body.findings-scroll { padding: 10px; display: flex; flex-direction: column; gap: 8px; }

/* Finding cards = the sandbox Ledger card: a clean white card, hairline border,
   a coloured LEFT RAIL that still carries severity (kept as information, not a
   full background fill), gently embossed off the well. Scoped to the drawer so
   the editor's in-document finding marks are untouched. */
/* Severity reads at a glance: a tint that FADES left→right off the coloured rail
   into the card (Craig 2026-07-13) — the type is obvious, the text stays on a
   light ground. Rail + fade are BOTH derived from the per-category --*-bar var
   (the same palette the app already owns, theme-aware) via color-mix — nothing is
   hardcoded, so the admin-editable colours and dark mode both flow through.
   #1653 — the rail, the radius and the gradient now live HERE rather than being
   restated per category; `--card-bar` is the only thing a category sets. */
.rc-drawer .finding-card {
    background: linear-gradient(90deg,
                color-mix(in srgb, var(--card-bar, transparent) 22%, var(--bg-white)),
                var(--bg-white) 72px);
    border: 1px solid var(--border);
    border-left: 3px solid var(--card-bar, var(--primary-fg));
    border-radius: 4px;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06), 0 2px 6px -4px rgba(15, 23, 42, 0.14);
}
/* #1653 — THE LEAK IS NEAR THE LEFT EDGE ONLY. Craig: "for the finding I would
   do a small amount of leak into the background of the finding, near the left
   edge only." The tint was reaching the plain surface at 60% of the CARD'S
   WIDTH, so a wide drawer washed most of the card and a narrow one washed a
   different amount of it — a proportion where the instruction is a distance.
   72px is a distance: the same on every drawer width, and it leaves the text
   on clean ground wherever the card is read.

   EIGHT CATEGORIES, ONE RULE. This was five hand-written gradients covering
   conflict, incomplete, vague, unnecessary_detail and ai_suggestion — so
   `missing` (added deliberately by #174), `inserted`, `structural` and
   `unknown` had NO card rule at all and drew the default --primary-fg rail with
   no tint. A reader could not tell a Missing finding's card from a category the
   product does not name. The gradient now lives once on the base rule and reads
   `--card-bar`; each category sets that one property. Adding the ninth is a
   line, and forgetting it is visible immediately because the card has no rail
   colour rather than the wrong one. */
.rc-drawer .finding-card.conflict           { --card-bar: var(--conflict-bar); }
.rc-drawer .finding-card.incomplete         { --card-bar: var(--gap-bar); }
.rc-drawer .finding-card.missing            { --card-bar: var(--missing-bar); }
.rc-drawer .finding-card.vague              { --card-bar: var(--vague-bar); }
.rc-drawer .finding-card.unnecessary_detail { --card-bar: var(--unnecessary-bar); }
.rc-drawer .finding-card.ai_suggestion      { --card-bar: var(--ai-bar); }
.rc-drawer .finding-card.inserted           { --card-bar: var(--inserted-bar); }
.rc-drawer .finding-card.structural         { --card-bar: var(--structural-bar); }
.rc-drawer .finding-card.unknown            { --card-bar: var(--unknown-bar); }
.rc-drawer .finding-card:hover {
    transform: translateY(-1px);
    box-shadow: 0 2px 4px rgba(15, 23, 42, 0.08), 0 4px 12px -4px rgba(15, 23, 42, 0.20);
}


@media (prefers-color-scheme: dark) {
    .rc-rz { box-shadow: -6px 0 16px -10px rgba(0, 0, 0, 0.5); }   /* #513 — see light-mode note */
    .rc-drawer .finding-card { box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 2px 6px -4px rgba(0, 0, 0, 0.45); }
    .rc-drawer .finding-card:hover { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4), 0 4px 12px -4px rgba(0, 0, 0, 0.55); }
}

/* ── the tab rail ───────────────────────────────────────────────────────────── */
.rc-tabrail {
    position: absolute;
    right: 0;
    top: 96px;
    z-index: 9;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 8px;
}
/* no document open → nothing to put in a drawer */
.rc-tabrail.rc-hidden { display: none; }
.rc-tab:hover {
    background: var(--hover-bg);
    color: var(--primary-fg);
    border-color: var(--primary-fg);
}
/* OPEN — this drawer is out, so the tab now means "push it back in" */
.rc-tab.rc-open {
    background: var(--primary-pale);
    color: var(--primary-fg);
    border-color: var(--primary-fg);
    box-shadow: none;
}
.rc-tab .ic {
    width: 15px; height: 15px;
    stroke: currentColor; fill: none; stroke-width: 2.2;
    stroke-linecap: round; stroke-linejoin: round;
    flex-shrink: 0;
    transition: transform 0.22s ease;
}
.rc-tab.rc-open .ic { transform: rotate(180deg); }          /* ‹ becomes › */
.rc-tab-lab {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    font: 600 10.5px/1 system-ui, sans-serif;
    letter-spacing: 0.11em;
    text-transform: uppercase;
}
/* white on --primary (#1a3a5c) is ~10.9:1 — and --primary is NOT overridden on
   dark, so this one pairing holds in both schemes without a media query. */
.rc-tab-n {
    background: var(--primary);
    color: #fff;
    border-radius: 999px;
    min-width: 17px;
    padding: 2px 4px;
    font: 700 10px/1.3 system-ui, sans-serif;
    text-align: center;
}
/* #303 — .rc-tab-dot and its @keyframes are BURNED (Craig 2026-08-05: "we dont
   need the dot nomenclature at all — in fact it was a serious error, because
   putting a dot on a tab sometimes is not fail loud"). A 7px mark on a tab the
   user may not be looking at is a signal that has to be noticed to work. What it
   used to say is now said as a warning the user cannot miss. */

/* ── the Metadata drawer body ─────────────────────────────────────────────────
   Read-only. It renders from RCDoc.qmsDocContext — the QMS record already on the
   document object — so there is no second copy of the data and nothing to keep in
   step. Editing stays in the QMS, which owns it; the button routes there. */


.rc-meta-btn {
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    color: var(--text);
    padding: 7px 13px;
    font: 600 12px/1 system-ui, sans-serif;
    cursor: pointer;
}
.rc-meta-btn:hover { border-color: var(--primary-fg); color: var(--primary-fg); }
.rc-meta-btn.is-primary { background: var(--primary); border-color: var(--primary); color: #fff; }
.rc-meta-btn.is-primary:hover { background: var(--primary-light); border-color: var(--primary-light); color: #fff; }

/* edit-mode inputs */


textarea.rc-meta-in { resize: vertical; }

/* #649 / #1922 — THE ONE PICK-LIST (app.js _rcOpenPickPopover): QE lenses,
   confirmed frameworks and groups. A button that reads as an input (it IS one of
   its window's controls), opening a popover of multiply-checkable values. Every
   colour is a token, so it follows the viewer's light/dark choice. */
button.rc-pick-trigger {
    text-align: left;
    cursor: pointer;
}
button.rc-pick-trigger:hover { border-color: var(--primary-fg); }

.rc-pick-pop {
    min-width: 15rem;
    max-width: 26rem;
    max-height: 60vh;
    overflow-y: auto;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
    padding: 8px;
}
.rc-pick-filter {
    display: block;
    width: 100%;
    margin-bottom: 6px;
    padding: 4px 6px;
    border: 1px solid var(--border);
    border-radius: 5px;
    background: var(--surface);
    color: var(--text);
    font: inherit;
}
.rc-pick-group {
    margin: 8px 6px 2px;
    color: var(--text-muted);
    font: 600 0.72rem/1.3 system-ui, sans-serif;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.rc-pick-opt {
    display: flex;
    gap: 8px;
    align-items: baseline;
    padding: 5px 6px;
    border-radius: 5px;
    cursor: pointer;
    font: 400 0.84rem/1.4 system-ui, sans-serif;
}
.rc-pick-opt[hidden] { display: none; }
.rc-pick-opt small { color: var(--text-muted); }
.rc-pick-opt:hover { background: var(--bg-subtle, rgba(127, 127, 127, 0.10)); }
.rc-pick-stray { font-style: italic; }
.rc-pick-empty {
    padding: 8px 6px;
    color: var(--text-muted);
    font: 400 0.82rem/1.4 system-ui, sans-serif;
}
.rc-pick-acts {
    display: flex;
    justify-content: flex-end;
    padding-top: 6px;
    margin-top: 4px;
    border-top: 1px solid var(--border);
}

/* Status and Version stay READ-ONLY even in edit mode, and say WHY (Craig 2026-07-13:
   "except status, version — that should only be in the QMS or by the workflow"). A field
   that silently has no input looks broken; one that explains itself is a control. */

.rc-meta-err {
    margin: 0 12px;
    padding: 8px 10px;
    border-radius: 6px;
    background: var(--conflict-bg);
    color: var(--conflict-text);
    font-size: 0.8rem;
    line-height: 1.4;
}
.rc-meta-err.hidden { display: none; }



.findings-scroll {
    overflow-y: auto;
    flex: 1;
    padding: 10px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* #489 INSTANCES — one findings pane per open document, inside the one scroll host.
   The host is a flex column with an 8px gap and its CHILDREN used to be the finding
   cards themselves; with a pane between them the host now has a single child, so the
   pane has to carry the column layout or every card loses its spacing. It also has to
   fill the host, or a short list would not stretch and the cards would bunch at the
   top of a half-height box. Same rules, one level down. */
.rc-findings-pane {
    display: flex;
    flex-direction: column;
    gap: 8px;
    flex: 1;
    min-height: 0;
}

.no-findings {
    text-align: center;
    font-size: 0.82rem;
    color: var(--text-muted);
    padding: 24px 12px;
}
/* #483 — the LOADING state, distinct from the empty one. A pulse says "still
   coming" where the plain empty state says "there is nothing"; without it the two
   read identically and the user cannot tell a slow document from an empty one.
   Honours prefers-reduced-motion — the words already carry the meaning, so the
   animation is an enhancement rather than the signal. */
.no-findings.rc-findings-loading { animation: rc-findings-pulse 1.6s ease-in-out infinite; }
@keyframes rc-findings-pulse { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  /* the still state shown in place of the motion (#1903: the motion itself
     is off for everything, by the one global rule near the top) */
  .no-findings.rc-findings-loading { opacity: 0.85; }
}

/* #754 — THE QMS LIBRARY'S LOADING STATE, DISTINCT FROM ITS EMPTY ONE.
   The same split the findings pane already makes (.rc-findings-loading above,
   #483): a pulse says "still coming" where the plain empty state says "there
   is nothing". Without it the two read identically, and on this surface the
   wrong one reads as data loss.
   prefers-reduced-motion honoured for the same reason as there - the words
   carry the meaning and the animation is an enhancement, not the signal. */
.qma-empty.qma-empty-loading { animation: qma-empty-pulse 1.6s ease-in-out infinite; }
@keyframes qma-empty-pulse { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  /* the still state shown in place of the motion (#1903: the motion itself
     is off for everything, by the one global rule near the top) */
  .qma-empty.qma-empty-loading { opacity: 0.85; }
}

/* ── #434 — THE CARD-FILTER BANNER ───────────────────────────────────────────
   A filtered list with nothing saying it is filtered reads as a document with
   two findings rather than a document showing two of nineteen. Sticky, because
   the thing it qualifies scrolls under it. Tokens only — it inherits the
   drawer's light/dark palette rather than carrying a second one. */
/* #505 — THE BANNER IS FULL-BLEED, so nothing can pass beside or above it.
   Craig, on the A10 card drawer: "as the findings move behind the banner on the
   drawer you can see them wither side and abpve th banner needs to stop that as
   it looks very odd."

   The cause is geometry, not layering: `.findings-scroll` has `padding: 10px`,
   so a banner with `top: 0` sticks to the CONTENT-box top and leaves a 10px
   transparent strip above it, and sits 10px in from each edge. Cards scrolling
   under it were visible through all three gaps. The background was already
   opaque — adding z-index or a stronger fill would not have touched it, which
   is exactly why the guardrail says this class gets a STRUCTURAL fix.

   So it spans the scroller: negative margins cancel the container's padding and
   the padding is put back on the banner itself, which keeps the content in the
   same place while the BAR reaches every edge. It reads as a header bar for the
   list now rather than a floating chip, so the rounding and the side/top borders
   go with it and a single bottom rule separates it from the first card. */
.rc-cardfilter-banner {
    position: sticky;
    top: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin: -10px -10px 0;
    padding: 12px 20px 9px;
    border: 0;
    border-bottom: 1px solid var(--border);
    border-radius: 0;
    background: var(--card);
    font-size: 0.78rem;
    color: var(--text-muted);
}
/* #375 — the label must SHRINK so the controls stay on its row. A long step
   title ("step 1.1.4 “File final approved Global”") has a large min-content
   width, which pushed Clear filter onto a line of its own — detached from the
   thing it clears and below where the eye goes. Craig, testing v4.2.471:
   "There is no clear filter button". It was there; it did not read as one and
   it was not where a control belongs. */
.rc-cardfilter-what { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.rc-cardfilter-what strong { color: var(--text); font-weight: 600; }
/* #375 — READ AS A CONTROL AT REST, not only on hover. This was a transparent
   pill with muted-grey text and a muted-grey border, sitting on a muted-grey
   banner, with no icon — indistinguishable from the label beside it until the
   pointer was already on it. The app's own standard is icon + LABEL by default
   with a hover tooltip on every button; this had neither the icon nor a border
   that separated it from its background. */
.rc-cardfilter-banner button {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 10px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--bg-white);
    color: var(--text);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}
.rc-cardfilter-banner button .ic {
    width: 12px; height: 12px; flex-shrink: 0;
    stroke: currentColor; fill: none; stroke-width: 2.4;
    stroke-linecap: round; stroke-linejoin: round;
}
/* --primary-fg, NOT --primary: the brand navy is a BACKGROUND role and does not
   lift in dark mode. The -fg twin exists precisely for text/border/accent use
   and reads on both themes. */
.rc-cardfilter-banner button:hover {
    border-color: var(--primary-fg);
    color: var(--primary-fg);
}

/* Finding cards */
.finding-card {
    border-radius: 7px;
    padding: 11px 12px;
    cursor: pointer;
    border: 1px solid transparent;
    transition: transform 0.12s, box-shadow 0.12s;
}

/* #1665 — THE FLAT PER-CATEGORY RULES ARE GONE, and their removal is the whole
   fix for Craig's report that "the finding cards do not match the sandbox look
   where the color wss to bleed across from the left and fade out quite
   quickly".

   THE GRADIENT WAS BUILT CORRECTLY AND NEVER WON. `.rc-drawer .finding-card`
   (above) sets the gradient, the rail and the radius, so a category needs to
   declare only `--card-bar`. Five flat rules stood here setting a flat
   `background` and a `border-color` on the same elements:

       .rc-drawer .finding-card   two classes   (0,2,0)
       .finding-card.conflict     two classes   (0,2,0)

   IDENTICAL SPECIFICITY, and at a tie the LATER rule wins. These sat 368 lines
   further down the file, so the rule written to replace them was silently
   overridden by them. Nothing errored, nothing logged, the stylesheet parsed
   cleanly — seeing it required computing specificity on two selectors a third
   of a file apart and then remembering that ties fall to source order.

   DELETED RATHER THAN OUT-SPECIFIED. Raising specificity or adding !important
   would have left two competing definitions of a card's background and made
   the next one harder to see. The losing machinery goes.

   THE ITEM SAID EIGHT; THERE WERE FIVE — conflict, incomplete, vague,
   unnecessary_detail and ai_suggestion. The other `.finding-card.*` rules in
   this file (`card-active`, `resolved`, `rejected`) are STATES rather than
   categories and are deliberately untouched: deleting a state rule because it
   looked like a category rule is exactly the silent regression this pass
   exists to avoid. */

.finding-card:hover {
    transform: translateY(-1px);
    box-shadow: 0 3px 10px rgba(0,0,0,0.08);
}

/* #398 — Craig: "the highlighting on a finding in the finding drawer of the
   document surface needs to be stronger".

   It was a 2px outline in --primary, which on a card that ALREADY carries a
   category tint and a coloured border reads as one more line among several.
   The selected card has to be findable at a glance in a scrolling list of
   twenty, so it now carries three signals that do not compete with the category
   colour rather than one that does:

     * a thicker ring PLUS a soft halo, so it separates from its neighbours
     * a left accent bar — the one edge the category border does not emphasise
     * a lift, matching the hover idiom the list already uses

   Colour alone was never going to be enough here: the category tint owns colour
   on this card, so the selection has to speak in weight and depth instead. Uses
   the same --rc-anchor-ring token as the in-document anchor highlight (#322), so
   "this is the selected finding" looks the same in the drawer and on the page. */
.finding-card.card-active {
    outline: 3px solid var(--rc-anchor-ring, #2563eb);
    outline-offset: 2px;
    box-shadow: 0 0 0 5px rgba(37, 99, 235, .16), 0 4px 14px rgba(15, 23, 42, .18);
    transform: translateY(-1px);
    position: relative;
}
.finding-card.card-active::before {
    content: "";
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 4px;
    border-radius: 3px 0 0 3px;
    background: var(--rc-anchor-ring, #2563eb);
}
/* Hover must not out-shout the selection — without this, moving the mouse over a
   neighbour makes it look more selected than the selected one. */
.finding-card.card-active:hover { transform: translateY(-1px); }

@media (prefers-color-scheme: dark) {
    .finding-card.card-active {
        outline-color: #60a5fa;
        box-shadow: 0 0 0 5px rgba(96, 165, 250, .22), 0 4px 14px rgba(0, 0, 0, .5);
    }
    .finding-card.card-active::before { background: #60a5fa; }
}

/* #613 — THIS ROW WRAPS. It carried `flex-wrap: nowrap` from v3.3.92, when it
   held an id badge, a category and one or two icons. It now accumulates the id
   badge, an info button, engine/category/severity/action/conflict/FDA badges,
   rejected/acted/provenance/debate-outcome/disposition pills AND the per-card
   buttons — a variable count that depends on how much state a finding carries.
   On F003 (four badges + Dismiss) the row overran the card and clipped Dismiss.

   Clipping the LAST item is what makes nowrap the wrong choice here rather than
   merely tight: the last items are the ACTIONS. The user can see something is
   there and cannot reach it. A taller card on a heavily-badged finding is the
   cheaper cost, so the row wraps and the buttons are pushed to the end of the
   line they land on. */
.card-top {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-bottom: 7px;
    flex-wrap: wrap;
    row-gap: 5px;
    min-width: 0;
}
/* Keep the actions together at the trailing edge once the row has wrapped, so
   they read as a group rather than as two more badges. */
.card-top > button { margin-left: auto; }
.card-top > button ~ button { margin-left: 0; }

/* #1653 — SENTENCE CASE, AND THE LETTER-SPACING GOES. The category badge that
   used this is now the chip; what is left here are the card's OTHER markers —
   the rule code, the Insert after / Insert before / Comment action chips, the
   overlap warning and the GENERAL marker. They kept weight 700, UPPERCASE with
   0.4px tracking and a 10px capsule, which is the same four dials turned up at
   once that made the pills read "a little 1980s". Turning them down here is
   what stops a card pairing a 2026 chip with a 1980s badge.
   The solid saturated fill stays on these: they are markers, not categories,
   and they are the only coloured thing left on the card besides its rail. */
.cat-badge {
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0;
    padding: 3px 7px;
    border-radius: 3px;
    line-height: 1;
    color: #fff;
}

/* WT-24 (#189, Craig 2026-07-19) — the badge label ink is CONTRAST-DERIVED from
   the badge's own --{cat}-bar background, never hardcoded white: dark ink on a
   light bar (VAGUE gold, GAP orange), white on a dark bar. The static defaults
   below are the authoritative WCAG ink choices applied to the default bars;
   applyFindingColors (app.js) re-derives --{cat}-badge-ink from the authoritative
   contrast contract
   whenever an admin re-colours a category, so any admin-chosen colour stays
   readable. Applies to the badge label path generally, not one colour. */
.cat-badge.conflict    { background: var(--conflict-bar); color: var(--conflict-badge-ink, #fff); }
.cat-badge.incomplete { background: var(--gap-bar); color: var(--gap-badge-ink, #1a202c); }
/* `missing` had NO rule until 2026-08-31, because the analyser had never
   emitted the category — the contract offered four of the six and this was
   not one of them. The first `missing` finding ever produced (F020 on
   GCD-SOP-405.00) therefore drew an unstyled badge, and the only coloured
   thing left on the card was its `Insert after` ACTION chip: Craig read the
   card as having an insertion instruction where its category should be.
   Same shape as the mixed-cell detector that was also absent for this
   category — a value nothing could produce gets styled by nobody. */
.cat-badge.missing     { background: var(--missing-bar); color: var(--missing-badge-ink, #fff); }
.cat-badge.vague       { background: var(--vague-bar); color: var(--vague-badge-ink, #1a202c); }
.cat-badge.unnecessary_detail { background: var(--unnecessary-bar); color: var(--unnecessary-badge-ink, #fff); }
.cat-badge.ai_suggestion      { background: var(--ai-bar); color: var(--ai-badge-ink, #fff); }
/* T-182 — GENERAL (document-level, unanchored-by-design) finding marker. Neutral
   slate so it reads as an attribute, not a severity; theme-aware for dark mode. */
.cat-badge.general-badge { background: light-dark(#475569, #64748b); }
/* #1674 — an outside reviewer's finding and its adoption. Tokens only: the
   slate and the deep blue both carry white text at AA in either theme. */
.cat-badge.external-badge { background: var(--pq-slate-700); }
.cat-badge.external-adopted { background: var(--pq-blue-900); }
.finding-adopt-btn {
    font: inherit; font-size: 0.72rem; font-weight: 600; line-height: 1;
    padding: 3px 8px; cursor: pointer;
    border: 1px solid var(--border); border-radius: var(--radius-sm);
    background: var(--bg-white); color: var(--primary-ink);
    display: inline-flex; align-items: center; gap: 4px;
}
.finding-adopt-btn:hover { border-color: var(--primary-ink); }
.finding-adopt-btn .ic { width: 12px; height: 12px; }

.sev-badge {
    font-size: 0.64rem;
    font-weight: 600;
    text-transform: uppercase;
    padding: 2px 6px;
    border-radius: 10px;
    margin-left: auto;
    flex-shrink: 0;
    /* #2325 — a level's colours come from the domain data as --sev-bg / --sev-fg;
       the stylesheet decides how they are drawn, so dark mode can draw them too. */
    background: var(--sev-bg, transparent);
    color: var(--sev-fg, inherit);
}
@media (prefers-color-scheme: dark) {
    /* #2325 — the data's light tint would be a light patch on a dark card: draw a
       dark tint of the level's own colour behind a lightened version of it. */
    .sev-badge {
        background: color-mix(in srgb, var(--sev-fg, currentColor) 28%, transparent);
        color: color-mix(in srgb, var(--sev-fg, currentColor) 45%, #fff);
    }
}

/* Axis-model severity bands (legacy fallback tint; the data-driven badge path
   uses inline colours from _domainConfig.qe_classification). material keeps the
   old "high" amber, minor the old "medium" yellow, advisory the old "low" green. */
.sev-critical { background: light-dark(#fed7d7, #3b1a1a); color: light-dark(#9b2c2c, #fc8181); }
.sev-material { background: light-dark(#feebc8, #3a2a12); color: light-dark(#c05621, #f6ad55); }
.sev-minor    { background: light-dark(#fefcbf, #35300f); color: light-dark(#975a16, #e9c46a); }
.sev-advisory { background: light-dark(#c6f6d5, #13301f); color: light-dark(#276749, #68d391); }

.card-quote {
    font-size: 0.77rem;
    font-style: italic;
    color: var(--quote-text);          /* #1859 — a themed pair, never a literal */
    margin-bottom: 7px;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    padding: 5px 7px;
    background: var(--quote-surface);
    border-radius: 4px;
    line-height: 1.5;
}

.card-explain {
    font-size: 0.78rem;
    line-height: 1.5;
    color: var(--text);
    margin-bottom: 6px;
}

.card-ref-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
.card-ref-row .engine-badge {
    flex: 0 0 auto;
}
.card-ref {
    font-size: 0.72rem;
    color: var(--text-muted);
    display: flex;
    gap: 4px;
    align-items: flex-start;
    flex: 1 1 auto;
    min-width: 0;
}
.card-ref-clickable {
    cursor: pointer;
}
.card-ref-clickable:hover { color: var(--primary-fg); }
/* "Worth a look" flag (v3.14.81) — the cited CFR has FDA-483 enforcement
   history. Theme-aware danger colour overrides the muted ref colour; the
   clickable child keeps its own hover. A visual cue, not a score. */
.card-ref-flagged { color: var(--danger); font-weight: 600; cursor: help; }
/* N? (Craig 2026-06-26) — non-CFR citation detail ("Appendix D, Assurance 5")
   reads as full body text, not a greyed/disabled control between the blue chips. */
.card-ref-detail { color: var(--text); }
/* #1839 — a finding that cites a superseded regulation version says so beside the reference.
   Informational, not a warning colour: the citation was right when raised. */
.card-ref-superseded {
    display: inline-flex; align-items: center; gap: 3px;
    margin-left: 4px; padding: 0 6px;
    font-size: 0.85em; color: var(--text-muted);
    border: 1px solid var(--border); border-radius: 999px;
    white-space: nowrap;
}
.card-ref-superseded .ic { width: 0.95em; height: 0.95em; }

/* ═══ N2 — semantic button tiers (Craig 2026-06-27; design: button-semantic-sandbox.html).
   ★ THIS IS THE CANONICAL STATEMENT OF THE HOUSE BUTTON STANDARD (#758, ruled by
   Craig 2026-08-21). Two other blocks used to restate it — one of them saying the
   opposite — and two statements of one rule at different strengths is charter 6.
   They point here now. If the rule changes, it changes HERE and nowhere else.
   Default is NEUTRAL soft-outline; colour is the semantic exception and each colour
   means ONE thing. Primary = brand-blue #276BB2 (filled, affirmative CTA);
   Caution = golden (reversible-destructive, e.g. Revoke); Danger = coral (irreversible,
   e.g. Delete/Reset). Sunset-palette grounded. Foundation for the app-wide rollout. ═══ */
:root {
    --btn-primary: var(--pq-blue-900); --btn-primary-h:#3274C2;
    --btn-caution:#b9770e; --btn-caution-bg:#fff7e9;
    --btn-danger:#c0392b;  --btn-danger-bg:#fdecea;
}
@media (prefers-color-scheme: dark) {
    :root {
        --btn-primary:#4C8ED8; --btn-primary-h:#5294DC;
        --btn-caution: var(--pq-gold-400); --btn-caution-bg:rgba(255,190,105,0.12);
        --btn-danger:#ef6a5a;  --btn-danger-bg:rgba(239,106,90,0.12);
    }
}
/* #2326 — THE ONE BUTTON. .opt-action-btn was a second spelling of this with its own
   rule; every use is .btn-sem now. The font was `font:600 0.82rem/1 inherit`, which is
   invalid ('inherit' cannot sit inside the shorthand), so the browser dropped the whole
   declaration and these buttons drew in its default font, size and weight. Longhand. */
.btn-sem {
    cursor:pointer; font-family:inherit; font-size:0.82rem; font-weight:600;
    display:inline-flex; align-items:center;
    gap:6px; padding:8px 14px; border-radius:8px; border:1px solid var(--border);
    background:var(--card); color:var(--text); transition:background .12s, border-color .12s, color .12s;
    white-space:nowrap;
}
.btn-sem .ic { width:15px; height:15px; }
.btn-sem:hover { background:var(--hover-bg); }   /* #1938 — hover is neutral; blue is the primary action */
/* #1732 — a disabled button LOOKS disabled (Save semantics: nothing to do). It
   had no rule, so a disabled Map read exactly like an enabled one. */
.btn-sem:disabled, .btn-sem:disabled:hover { opacity:.45; cursor:not-allowed; }
.btn-sem.primary:disabled:hover { background:var(--btn-primary); border-color:var(--btn-primary); }
.btn-sem.primary { border-color:var(--btn-primary); background:var(--btn-primary); color:#fff; }
.btn-sem.primary:hover { background:var(--btn-primary-h); border-color:var(--btn-primary-h); color:#fff; }
.btn-sem.caution { border-color:var(--btn-caution); color:var(--btn-caution); }
.btn-sem.caution:hover { background:var(--btn-caution-bg); border-color:var(--btn-caution); color:var(--btn-caution); }
.btn-sem.danger { border-color:var(--btn-danger); color:var(--btn-danger); }
.btn-sem.danger:hover { background:var(--btn-danger); border-color:var(--btn-danger); color:#fff; }


/* Per-ref clickable chip — multi-ref findings (e.g.
   "21 CFR 312.52(b); 21 CFR 312.50") render each ref as its own chip
   so left-click (view text) and right-click (FDA citation history)
   target the specific ref the user clicked, not the concatenated
   string. */
.card-ref-chip {
    cursor: pointer;
    color: var(--primary-ink, #2b6cb0);
    text-decoration: underline;
    text-decoration-style: dotted;
    text-underline-offset: 2px;
    padding: 0 1px;
    border-radius: 2px;
    transition: background-color 0.12s ease;
}
.card-ref-chip:hover {
    background-color: rgba(43, 108, 176, 0.10);
    text-decoration-style: solid;
}
.card-ref-chip:active {
    background-color: rgba(43, 108, 176, 0.20);
}
/* #228 — visible inspection-citation badge beside a CFR chip. Left-clickable (and
   keyboard-operable) affordance that opens the FDA 483 citation history, so the
   detail is reachable without a right-click (Craig's Magic Mouse). */
.card-ref-cite {
    cursor: pointer;
    color: var(--primary-ink, #2b6cb0);
    text-decoration: none;
    margin-left: 1px;
    padding: 0 2px;
    border-radius: 2px;
    font-weight: 600;
    line-height: 1;
    transition: background-color 0.12s ease;
}
.card-ref-cite sup { font-size: 0.72em; }
.card-ref-cite:hover,
.card-ref-cite:focus-visible { background-color: light-dark(#2b6cb024, rgba(99, 179, 237, 0.18));
    outline: none;
}
@media (prefers-color-scheme: dark) {
    .card-ref-cite { color: var(--primary-ink, #63b3ed); }
    
}
/* Cited-CFR frequency band (L-031 display heuristic) — a subtle enforcement-
   density cue on the ref chip, keyed to the finding's FDA 483 citation count.
   Deliberately NOT the severity red: a warm underline + faint wash so it reads
   as "how often regulators cite this reg", distinct from the finding's severity. */
.card-ref-chip.card-ref-freq-high { background-color: light-dark(#c056211f, rgba(246, 173, 85, 0.16)); text-decoration-color: light-dark(#c05621, #f6ad55);
    text-decoration-style: solid;
}
.card-ref-chip.card-ref-freq-medium { background-color: light-dark(#b7791f1a, rgba(233, 196, 106, 0.13)); text-decoration-color: light-dark(#b7791f, #e9c46a);
}
.card-ref-chip.card-ref-freq-low { background-color: light-dark(#7a7a0014, rgba(214, 214, 0, 0.10)); text-decoration-color: light-dark(#7a7a00, #d6d600);
}

/* ── Regulation text popup ── */
.reg-popup {
    position: fixed;   /* was absolute — viewport-clamp + draggable so it can't
                          render half off-screen or strand on scroll (Craig img#28) */
    z-index: 5000;
    width: 460px;
    max-width: calc(100vw - 32px);
    background: Canvas;
    color: CanvasText;
    border: 1px solid var(--border, #d1d5db);
    border-radius: 8px;
    box-shadow: 0 8px 32px rgba(0,0,0,0.22);
    font-size: 0.82rem;
    overflow: hidden;
}
.reg-popup-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 12px;
    background: var(--primary, #2b6cb0);
    color: #fff;
}
.reg-popup-title {
    font-weight: 700;
    font-size: 0.84rem;
    flex: 1;
}


.reg-popup-body {
    padding: 12px;
    max-height: 380px;
    overflow-y: auto;
    white-space: pre-wrap;
    line-height: 1.6;
    font-family: inherit;
}
.reg-popup-loading { color: var(--text-muted); font-style: italic; }
/* #2388 — "Showing the first N of M characters": in the regulation text popup and
 * the Frameworks pane, both of which lay their text out pre-wrap. */
.reg-text-cut {
    white-space: normal;
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px;
    margin: 0 0 10px; padding: 7px 10px;
    border: 1px solid var(--border); border-radius: 6px;
    background: var(--surface-2, #f7f8fb);
    color: var(--text-secondary);
    font-size: 0.8rem; line-height: 1.4;
}
.reg-popup-error   { color: light-dark(#b91c1c, color-mix(in oklab, #b91c1c 45%, white)); }
/* The regulation unit model's text: one line per unit of the section the citation
 * sits in, the cited lines marked; provenance above, and a note when the text
 * has changed since the finding was raised. */
.reg-unit-prov { white-space: normal; color: var(--text-secondary); font-size: 0.78rem; margin: 0 0 8px; }
.reg-unit-changed {
    white-space: normal;
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px;
    margin: 0 0 10px; padding: 7px 10px;
    border: 1px solid light-dark(#d69e2e, #b7791f); border-radius: 6px;
    background: light-dark(#fffaf0, color-mix(in oklab, #b7791f 18%, Canvas));
    color: var(--text); font-size: 0.8rem; line-height: 1.4;
}
.reg-unit-choices { white-space: normal; display: flex; flex-wrap: wrap; gap: 4px; margin: 0 0 8px; }
.reg-unit-choices .is-on { border-color: var(--primary, #2b6cb0); color: var(--primary-ink, #2b6cb0); }
.reg-unit-lines { white-space: normal; }
.reg-unit-line { white-space: pre-wrap; margin: 0 0 4px; border-left: 3px solid transparent; padding-right: 4px; }
.reg-unit-target {
    border-left-color: var(--primary, #2b6cb0);
    background: light-dark(rgba(43, 108, 176, 0.08), rgba(99, 160, 230, 0.14));
}
.reg-unit-label { font-weight: 700; }
.reg-unit-heading { font-weight: 600; }
.card-ref-unmatched { color: var(--text-secondary); font-size: 0.78em; white-space: nowrap; }

/* The finding card's sources (Craig and Karen 2026-10-24). Style 1, the labelled
 * chip: one line per source, a basis chip then the citation. Style 2, side by
 * side: the document's words beside each source's words. The basis colours are
 * the design page's, light and dark. */
.card-basis {
    display: inline-flex; align-items: center; gap: 4px; flex: none;
    font-size: 0.7rem; font-weight: 600; line-height: 1.4;
    padding: 1px 7px; border-radius: 999px; white-space: nowrap;
}
.card-basis .ic { width: 12px; height: 12px; }
.card-basis-reg { color: light-dark(#1d4f91, #9cc2f2); background: light-dark(#e2ebf7, #1c2c42); }
.card-basis-ext { color: light-dark(#0f6b62, #7fd3c8); background: light-dark(#ddf1ee, #143430); }
.card-basis-int { color: light-dark(#6a3d9a, #c9a8ef); background: light-dark(#efe6f8, #2e2240); }
.card-basis-bg  { color: light-dark(#5b6470, #b4bcc6); background: light-dark(#eceef1, #2c3036); }
.card-src-stack { display: grid; gap: 4px; margin-top: 6px; }
.card-src-line { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
.card-src-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
.card-src-btn { font-size: 0.78rem; padding: 2px 8px; }
.card-src-btn .ic { width: 13px; height: 13px; }
.card-sbs { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 6px; }
.card-sbs > div { min-width: 0; border-radius: 4px; padding: 6px 8px; font-size: 0.92em; overflow-wrap: anywhere; }
.card-sbs-mine { background: var(--surface-2, #f4f2ee); font-style: italic; }
.card-sbs-theirs { border: 1px solid var(--border); display: grid; gap: 7px; }
.card-sbs-lab {
    display: flex; align-items: center; gap: 4px; font-style: normal;
    font-size: 0.74rem; font-weight: 700;
    color: var(--text-secondary); margin-bottom: 2px;   /* never upper-cased: 211.84(d) is not 211.84(D) */
}
.card-sbs-lab .ic { width: 12px; height: 12px; }
.card-sbs-loading { color: var(--text-secondary); }
@media (max-width: 520px) { .card-sbs { grid-template-columns: 1fr; } }

/* D-017 — live step-redirect link (stepRef node) + navigate flash */
.rc-stepref {
    color: var(--primary-ink, #2b6cb0);
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
    font-weight: 600;
    white-space: nowrap;
    border-radius: 3px;
    padding: 0 1px;
}
.rc-stepref:hover { background: light-dark(#eef2ff, color-mix(in oklab, #eef2ff 14%, var(--pq-surface))); }
.rc-stepref-flash {
    outline: 2px solid #f59e0b;
    outline-offset: 2px;
    background: light-dark(#fff7e6, color-mix(in oklab, #fff7e6 14%, var(--pq-surface)));
    transition: background 0.3s, outline-color 0.3s;
}

/* FDA Form 483 citation-history popup — right-click on a finding card's
   regulation reference. Visually parallel to .reg-popup so the two
   surfaces look like a related family but distinct (different header
   colour distinguishes the two right-click vs left-click affordances). */
.fda-citations-popup {
    position: fixed;        /* W1-B (#4): viewport-anchored — pane scrolling can't strand it off-screen */
    z-index: 10050;         /* v4.1.44 — above the graph finding popup (9999) / edit popover (10000) so the citation drill-in isn't hidden behind them */
    width: 720px;
    max-width: calc(100vw - 32px);
    background: Canvas;
    color: CanvasText;
    border: 1px solid var(--border, #d1d5db);
    border-radius: 8px;
    box-shadow: 0 8px 32px rgba(0,0,0,0.22);
    font-size: 0.82rem;
    overflow: hidden;
}
.fda-citations-popup-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: 8px 12px;
    background: #4a5568;        /* slate — distinct from the blue reg-popup header */
    color: #fff;
}
.fda-citations-popup-title {
    font-weight: 700; font-size: 0.84rem; flex: 1;
}

.fda-citations-popup-body {
    /* W1-B (#4): cap to the viewport so the popup never exceeds the screen;
       its own scroll keeps long citation tables fully reachable. */
    padding: 12px; max-height: min(420px, calc(100vh - 140px)); overflow-y: auto;
}
.fda-citations-loading { color: var(--text-muted); font-style: italic; }
.fda-citations-error   { color: light-dark(#b91c1c, color-mix(in oklab, #b91c1c 45%, white)); }
.fda-citations-ref {
    font-family: monospace; font-size: 0.78rem;
    padding: 4px 8px;
    background: light-dark(#f1f5f9, var(--pq-elevated)); border-radius: 4px 4px 0 0;
    color: light-dark(#334155, var(--pq-ink));
}
/* v3.9.47 — descriptive section title rendered under the ref chip
   when the backend resolved it from cached eCFR text. Visually
   continuous with .fda-citations-ref above (shared bg, no border
   between, rounded only on the bottom).
   v3.9.48 — when a section-level finding triggers a roll-up across
   subsections, the title sits between the ref chip and the
   .fda-citations-rollup banner; remove the bottom radius + bottom
   margin so the three pieces stack visually as one block. */
.fda-citations-section-title {
    padding: 4px 8px 6px;
    background: light-dark(#f1f5f9, var(--pq-elevated)); border-radius: 0;
    color: light-dark(#475569, var(--pq-ink-muted)); font-size: 0.85rem; font-style: italic;
    border-top: 1px solid var(--border);
    margin-bottom: 0;
}
.fda-citations-section-title:last-child {
    border-radius: 0 0 4px 4px;
    margin-bottom: 10px;
}
/* v3.9.48 — section-level findings aggregate citations across all
   matched subsections. This banner tells the reviewer which
   subsections contributed so they don't think the popup ran a
   precise per-subsection lookup. */
.fda-citations-rollup {
    padding: 5px 8px 6px; margin-bottom: 10px;
    background: light-dark(#fef9c3, color-mix(in oklab, #fef9c3 14%, var(--pq-surface))); border-radius: 0 0 4px 4px;
    color: light-dark(#713f12, color-mix(in oklab, #713f12 45%, white)); font-size: 0.78rem;
    border-top: 1px solid light-dark(#fde68a, color-mix(in oklab, #fde68a 30%, var(--pq-surface)));
}


/* Description column on each FDA citation row. Constrains width so
   the table doesn't blow out for unusually long descriptions; row's
   title attribute carries the long_description for hover-on-row. */
.fda-citations-desc-cell {
    color: light-dark(#1e293b, var(--pq-ink));
    line-height: 1.3;
    word-wrap: break-word;
    overflow-wrap: anywhere;
}
/* Subtle visual cue that the row is hover-able for more detail. */
.fda-citations-tbl tbody tr[title]:hover {
    background-color: light-dark(#f8fafc, var(--pq-elevated));
    cursor: help;
}
.fda-citations-norm {
    color: var(--text-muted); font-style: italic;
}
.fda-citations-empty {
    padding: 12px; color: var(--text-muted);
    background: light-dark(#f8fafc, var(--pq-elevated)); border-radius: 4px;
    text-align: center;
}
.fda-citations-hint {
    margin-top: 6px; font-size: 0.75rem; opacity: 0.85;
}
.fda-citations-tbl {
    width: 100%; border-collapse: collapse; font-size: 0.78rem;
    /* Fixed layout so the Citations column never gets squeezed to
       zero by a long Description. Each column's width is set
       explicitly via the colgroup-style nth-child rules below. */
    table-layout: fixed;
}
.fda-citations-tbl th {
    text-align: left; padding: 4px 8px;
    background: light-dark(#f1f5f9, var(--pq-elevated)); color: light-dark(#475569, var(--pq-ink-muted)); font-weight: 600;
    border-bottom: 1px solid var(--border, #d1d5db);
}
.fda-citations-tbl th.num { text-align: right; }
.fda-citations-tbl td {
    padding: 4px 8px; border-bottom: 1px solid light-dark(#f1f5f9, var(--pq-border));
}
.fda-citations-tbl td.num {
    text-align: right; font-variant-numeric: tabular-nums;
}
/* Column widths — total ~688px to match the 720px popup minus 32px
   of body padding. Description is the elastic column (rest of width). */
.fda-citations-tbl th:nth-child(1),
.fda-citations-tbl td:nth-child(1) { width: 60px; }              /* FY */
.fda-citations-tbl th:nth-child(2),
.fda-citations-tbl td:nth-child(2) { width: 110px; }             /* Program */
.fda-citations-tbl th:nth-child(4),
.fda-citations-tbl td:nth-child(4) { width: 80px; }              /* Citations */
.fda-citations-tbl tr.fda-citations-year-first td {
    border-top: 1px solid var(--border, #d1d5db);
}
.fda-citations-tbl tr.fda-citations-year-total td {
    background: light-dark(#fafbfc, var(--pq-elevated)); color: light-dark(#475569, var(--pq-ink-muted));
}
.fda-citations-footer {
    margin-top: 12px; padding-top: 10px;
    border-top: 1px solid var(--border, #d1d5db);
    font-size: 0.78rem;
}
.fda-citations-footer > div { margin-bottom: 3px; }
.fda-citations-source {
    margin-top: 6px; color: var(--text-muted);
    font-size: 0.72rem; font-style: italic;
}
.fda-citations-subtotal {
    margin-top: 6px; margin-bottom: 16px;
    padding: 6px 10px;
    background: light-dark(#f8fafc, var(--pq-elevated)); border-radius: 4px;
    font-size: 0.78rem; color: light-dark(#475569, var(--pq-ink-muted));
}
.fda-citations-grand-footer {
    background: light-dark(#f1f5f9, var(--pq-elevated)); padding: 10px;
    border-radius: 6px; border-top: 2px solid #4a5568;
}

.card-more-row {
    margin-top: 6px;
    text-align: right;
}

.card-more-btn {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 0.72rem;
    color: var(--primary-fg);   /* N6 — brighter, readable in dark (was --primary-light, too dim) */
    padding: 0;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.card-more-btn:hover {
    color: var(--primary-fg);
}

.card-suggestion {
    margin-top: 8px;
    border-top: 1px dashed var(--border);
    padding-top: 8px;
}

.suggestion-label {
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.4px; color: light-dark(#276749, #6ee7b7);
    margin-bottom: 4px;
}

.suggestion-intro {
    font-size: 0.74rem;
    color: var(--text-muted);
    font-style: italic;
    margin-bottom: 5px;
}

.suggestion-text {
    font-size: 0.79rem;
    line-height: 1.55;
    color: var(--text); background: light-dark(#ffffff99, rgba(255,255,255,0.05));
    border-radius: 4px;
    padding: 6px 8px;
    border-left: 3px solid var(--primary-light);
}

/* #906 / §15.1.4 — which model produced the proposal, at the point it is read.
   Deliberately QUIET: it is provenance a reader consults, not a headline. Sits
   directly beneath the proposed text so the two are read together, and uses
   existing tokens rather than new literals — the visual-standards ratchet
   (#743) counts a new hardcoded colour here as debt, and it would be right to. */
.suggestion-model {
    font-size: 0.68rem;
    color: var(--text-muted);
    margin-top: 4px;
    letter-spacing: 0.01em;
}

.suggestion-replacement { border-left-color: light-dark(#276749, #6ee7b7); background: light-dark(#f0fff4, #14342a); color: light-dark(#1a202c, #e6eaf0);
    font-family: inherit;
    white-space: pre-wrap;
}

.suggestion-original { border-left-color: light-dark(#b7791f, #d4a23c); background: light-dark(#fffaf0, #2a2412); color: light-dark(#1a202c, #e6eaf0);
    font-family: inherit;
    white-space: pre-wrap;
}

/* N7 (Craig 2026-06-27) — the proposed-replacement box family is hardcoded light:
   suggestion-text rides a white overlay; suggestion-replacement/-original use a
   light bg + near-black text; suggestion-label is dark green. Flip for dark so the
   header is a brighter green and the box stops "still being in light mode". */

/* ── Expert Review formatted response ── */
.er-section-label {
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--primary-fg);
    margin: 14px 0 6px;
}
.er-section-label:first-child { margin-top: 0; }
.er-list {
    margin: 0 0 4px 0;
    padding-left: 20px;
    display: flex;
    flex-direction: column;
    gap: 5px;
}
.er-list li {
    font-size: 0.88rem;
    line-height: 1.6;
    color: var(--text);
}
.er-para {
    font-size: 0.88rem;
    line-height: 1.7;
    color: var(--text);
    margin: 0 0 8px;
}
.er-gap { height: 8px; }
.er-hr { border: none; border-top: 1px solid var(--border); margin: 10px 0; }


.er-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.83rem;
    margin: 8px 0;
}
.er-table th, .er-table td {
    padding: 5px 10px;
    border: 1px solid var(--border);
    text-align: left;
    line-height: 1.4;
}
.er-table th {
    background: var(--surface-2);
    font-weight: 600;
}

/* ── Expert Review 3-panel modal layout ── */


.er-header-logo {
    height: 28px;
    width: auto;
    object-fit: contain;
    flex-shrink: 0;
}
.er-body {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-height: 0;
}
.er-reg-panel {
    padding: 12px 20px;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}
.er-reg-meta {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 8px;
}
.er-reg-ref {
    font-size: 0.78rem;
    color: var(--text-muted);
    margin-left: auto;
}
/* Table variant: no italic or quotation marks — table fills the space */
.er-reg-quote-table {
    font-style: normal;
    padding: 4px 0;
    overflow-x: auto;
}
.er-reg-quote-table .er-table { font-size: 0.8rem; }

.er-reg-quote {
    font-style: italic;
    font-size: 0.9rem;
    color: var(--text);
    margin-bottom: 6px;
    padding-left: 12px;
    border-left: 3px solid var(--border);
    line-height: 1.5;
}
.er-reg-explanation {
    font-size: 0.83rem;
    color: var(--text-muted);
    line-height: 1.5;
}

.er-panel {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-width: 0;
}


.er-panel-content {
    flex: 1;
    overflow-y: auto;
    padding: 14px 16px;
    min-height: 0;
}

.er-discuss-btn {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    border: 2px solid var(--primary-fg);
    background: var(--surface);
    color: var(--primary-fg);
    font-size: 1.1rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.12s, color 0.12s;
    padding: 0;
}
.er-discuss-btn:hover:not(:disabled) {
    background: var(--primary);
    color: #fff;
}
.er-discuss-btn:disabled {
    opacity: 0.3;
    cursor: not-allowed;
}
/* Conclusion strip in main expert review modal */
.er-debate-section {
    border-top: 1px solid var(--border);
    flex-shrink: 0;
    max-height: 180px;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}


.er-conclusion-text {
    font-size: 0.86rem;
    line-height: 1.6;
    color: var(--text);
}

/* Expert Review — three-pane floating window */
.er-fullscreen-overlay {
    position: fixed;
    inset: 0;
    z-index: 300;
    pointer-events: none;   /* transparent — clicks pass through to the app */
}
.er-fullscreen-overlay.hidden { display: none; }
.er-fullscreen-box {
    position: fixed;
    left: calc(50% - 44vw);
    top: 6vh;
    width: 88vw;
    height: 84vh;
    min-width: 640px;
    min-height: 340px;
    display: flex;
    flex-direction: column;
    background: var(--bg-white, #fff);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 8px 40px rgba(0,0,0,0.28);
    resize: both;
    overflow: hidden;
    pointer-events: all;
}
/* #1968 — THE WINDOW-CONTROL GROUP (was .er-win-*, the expert-review window's own):
   minimise and maximise beside THE close (.rc-close, #1700), shared by the expert-
   review window and the document window so a window's controls look one way.
   Default (macOS): the three dots on the LEFT, close / minimise / maximise; their
   glyph shows when the pointer is over the group, as the OS does. The dot colours
   are the operating system's own, so they are literals in both themes. */
.rc-win-controls {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-right: 6px;
    flex-shrink: 0;
    order: -1;   /* macOS: left of the title */
}
.rc-win-btn {
    width: 13px;
    height: 13px;
    border-radius: 50%;
    border: none;
    cursor: pointer;
    padding: 0;
    transition: filter 0.12s;
    flex-shrink: 0;
    font-size: 0;   /* the glyph (data-label) shows off the Mac only */
    line-height: 13px;
    color: rgba(0, 0, 0, 0.55);
}
.rc-win-btn:hover { filter: brightness(0.8); }
.rc-win-min { background: #febc2e; }
.rc-win-max { background: #28c840; }
/* a window that cannot grow greys its green dot, as the Mac does */
.rc-win-btn:disabled { background: var(--border); cursor: default; filter: none; }
body.os-mac .rc-win-controls:hover .rc-win-btn:not(:disabled)::before {
    content: attr(data-mac-label);
    font-size: 11px;
    font-weight: 700;
}

/* Windows / Linux: the controls move RIGHT and show their glyphs */
body.os-win .rc-win-controls,
body.os-other .rc-win-controls {
    order: 10;
    margin-right: 0;
    margin-left: 4px;
    gap: 0;
    align-self: stretch;
}
body.os-win .rc-win-btn,
body.os-other .rc-win-btn {
    width: auto;
    height: auto;
    min-width: 36px;
    border-radius: 0;
    background: transparent;
    color: var(--text);
    font-size: 0.85rem;
    padding: 0 10px;
    border-left: 1px solid var(--border);
}
body.os-win .rc-win-btn::before,
body.os-other .rc-win-btn::before {
    content: attr(data-label);
    font-size: 0.85rem;
}
body.os-win .rc-win-btn:hover:not(:disabled),
body.os-other .rc-win-btn:hover:not(:disabled) { background: var(--surface-2); filter: none; }
body.os-win .rc-win-btn:disabled,
body.os-other .rc-win-btn:disabled { background: transparent; opacity: 0.4; }
body.os-win .rc-win-controls > .rc-close,
body.os-other .rc-win-controls > .rc-close { margin-left: 6px; align-self: center; }

/* Every other window's close takes its OS treatment from .rc-close (#1700, at the end of this file). */


.er-fullscreen-header {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 14px;
    border-bottom: 1px solid var(--border);
    background: var(--surface-2);
    cursor: move;
    user-select: none;
    border-radius: 8px 8px 0 0;
}
.er-fullscreen-header button { cursor: pointer; }
.er-fullscreen-title {
    flex: 1;
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--primary-fg);
}
.er-fullscreen-header-actions {
    display: flex;
    align-items: center;
    gap: 8px;
}
.er-fullscreen-body {
    flex: 1;
    display: flex;
    min-height: 0;
    overflow: hidden;
}


/* Reg panel (Finding box) scrolls independently */

.er-left-discuss-bar {
    flex-shrink: 0;
    padding: 10px 16px;
    border-top: 1px solid var(--border);
    background: var(--surface-2);
    display: flex;
    justify-content: center;
}
.er-left-discuss-bar .er-discuss-btn {
    width: auto;
    padding: 7px 20px;
    font-size: 0.85rem;
}
/* Pane resize handle (shared by both handles) */
.er-pane-resize-handle {
    width: 5px;
    cursor: col-resize;
    background: var(--border);
    flex-shrink: 0;
    transition: background 0.15s;
}
.er-pane-resize-handle:hover,
.er-pane-resize-handle.dragging { background: var(--primary); }

/* Pane 3: Discussion transcript */


.er-debate-bubbles {
    flex: 1;
    padding: 16px 20px;
    display: flex;
    flex-direction: column;
    gap: 14px;
    overflow-y: auto;
    background: var(--surface-2);
}
.er-debate-placeholder {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 40px 20px;
}
.er-debate-placeholder-text {
    color: var(--text-muted);
    font-size: 0.88rem;
    text-align: center;
    max-width: 220px;
    line-height: 1.6;
}


#er-debate-modal .modal-header,
#admin-modal .modal-header {   /* v3.12.12 — drag UI Polish */
    cursor: grab;
    user-select: none;
}
#er-debate-modal .modal-header:active,
#admin-modal .modal-header:active { cursor: grabbing; }

/* Admin modal — fixed position so it can be dragged */
/* v3.9.40 — widened from 1340px to 1700px so the 8-column Audit Log
   table (with the new column-aligned in-`<th>` filter row from T-091)
   fits without horizontal scroll on typical desktop viewports. The
   min(…, 96vw) cap still prevents overflow on narrow screens. */
#admin-modal:not(.hidden) { display: block; }
/* N1 (Craig 2026-06-26) pinned this to 10300 so the admin modal cleared the
   Process-Graph header (10100) and the top nav (10200), because .modal-overlay's
   default was 200. REMOVED 2026-08-16: the default is now 11500, which clears
   both by a wide margin, so the pin is doing nothing except stating a number
   that would silently become a CEILING if the base ever rose past it. */
/* Detail cards are launched from WITHIN the admin modal (double-click an Audit
   row or a Billing "By call" row). The generic .modal-overlay default z-index
   was 200 — far BELOW #admin-modal's then-10300 — so the detail rendered BEHIND
   the panel it was opened from (Craig's screenshot #4). A detail modal opened
   from over another modal must still stack ABOVE it, and that is the ONE thing
   this rule still says now that the base is right.
   Coherent layer order: progress(11000-11060) < overlays(11500)
                         < detail-over-a-modal(11510) < rc-tell(12000). */
#audit-detail-modal { z-index: 11510; }
/* #1630 — THE SETTINGS WINDOW IS A FIXED FRAME AND NEVER RESIZES ON A TAB SWITCH.
   Craig 2026-09-14: "the windows change size as you switch beteween tabs, which
   means it always feels crowded. So ... a fixed size and cleaner, n=morre empty
   look and feel for the settings pages."

   THE DEFECT was that nothing here stated a HEIGHT. The shared `.modal-box` rule
   gives every dialog `max-height: 82vh`, which is a CEILING, not a size — so each
   of the 23 admin tabs sized the frame to its own content and the window jumped
   on every switch.

   `height` is stated HERE and never on the shared `.modal-box`: that rule is used
   by 34 dialogs across the app, and a fixed height on it would resize every one of
   them. The scoping IS the fix, not a detail of it.

   The width was previously stated TWICE — this rule said 1700px while an inline
   style on the element said 1200px and, being inline, silently won. The live value
   (1200px) is kept and the inline copy is deleted, so there is now one definition.
   `vh` rather than a pixel height so the frame tracks the viewport instead of
   overflowing a short screen. */
#admin-modal .modal-box {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    max-width: 1200px;
    width: min(1200px, 96vw);
    /* The binding constraint. Content adapts to the frame; the frame never
       adapts to the content. `.modal-body` already scrolls (`flex:1;
       overflow:auto`), so a dense tab scrolls inside this box and a light one
       sits in whitespace — and the box is the same size either way. */
    height: 82vh;
}

/* #1630 — THE DENSITY IDIOM, APPLIED ONCE FOR THE WHOLE TAB FAMILY.
   From the Usage view shipped as the exemplar on #1628: generous whitespace,
   hairline separators, no boxed panels.

   ONE implementation rather than 23. Every tab builds its content from
   `.opt-section` (75 uses in index.html, 13 more built in app.js), so restyling
   that class UNDER `#admin-modal` reaches the entire family in one rule — and a
   tab added later inherits the idiom instead of having to be remembered. Editing
   23 tabs' markup would have been 23 copies of one decision, which is the thing
   that drifts.

   Scoped to `#admin-modal`: `.opt-section` is used outside Settings too, and
   those surfaces keep the boxed look they have today. */
#admin-modal .opt-section {
    /* No boxed panels. The box was a background, a border and a corner radius
       drawing a frame around content that needs no frame; a hairline above each
       section separates them with far less ink. */
    background: transparent;
    border: none;
    border-radius: 0;
    border-top: 1px solid var(--border);
    /* Generous whitespace. Horizontal padding drops to 0 so every section's text
       aligns on one left edge down the whole panel, rather than each box insetting
       its own contents by 18px. */
    padding: 22px 0 6px;
    margin-bottom: 0;
}

/* The first section in a panel sits directly under the tab strip, so a rule above
   it would read as a second underline to the strip rather than as a separator. */
#admin-modal [id^="admin-tab-panel-"] > .opt-section:first-child,
#admin-modal .opt-section:first-child {
    border-top: none;
    padding-top: 6px;
}

/* A section heading names its group; it is not a title bar. Lighter weight and a
   wider letter-spacing at a smaller size reads as a label rather than a banner. */
/* #1630 re-test (Craig 2026-09-25: Email, Periodic Review, Workflow,
   Instrumentation, Data Load and Structural Rules "use different fonts and
   weights"). Those six tabs head their sections with <h4>, which this rule did
   not reach, so they kept the browser's bold default. 14 of the family's 44
   section headings are h4; they get the same treatment rather than a rewrite of
   the markup. */
#admin-modal .opt-section > h3,
#admin-modal .opt-section > h4 {
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin: 0 0 14px;
}

/* Thin bars. The controls keep their semantics and their hit area; what comes off
   is visual mass. */
#admin-modal .opt-section .btn-sem,
#admin-modal .opt-section .btn-sem {
    padding-top: 5px;
    padding-bottom: 5px;
}


/* ── Conclusion block (top of pyramid) ─────────────────────────────── */
.er-conclusion-block {
    flex-shrink: 0;
    border-bottom: 1px solid var(--border);
}
.er-outcome-panel {
    padding: 16px 20px;
}
.er-outcome-label {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    margin-bottom: 10px;
    display: flex;
    align-items: center;
    gap: 6px;
}
.er-outcome-label-consensus { color: light-dark(#2d7a4f, color-mix(in oklab, #2d7a4f 45%, white)); }
.er-outcome-label-impasse   { color: light-dark(#b45309, color-mix(in oklab, #b45309 45%, white)); }
.er-consensus-actions {
    margin-top: 10px;
    display: flex;
    justify-content: flex-end;
    gap: 8px;
}
/* Equal-width buttons in consensus action bar */
.er-consensus-actions .btn-sem {
    min-width: 130px;
    text-align: center;
}
.er-outcome-text {
    font-size: 0.9rem;
    line-height: 1.65;
    color: var(--text);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 12px 16px;
}
.er-impasse-cards {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
    margin-bottom: 12px;
}
.er-impasse-card {
    border-radius: 8px;
    border: 1px solid var(--border);
    overflow: hidden;
    font-size: 0.86rem;
}
.er-impasse-card-hdr {
    padding: 6px 12px;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.er-impasse-card-qe       .er-impasse-card-hdr { background: var(--primary-pale, #eef4fb); color: var(--primary-fg); border-bottom: 1px solid light-dark(#c3d9f0, color-mix(in oklab, #c3d9f0 30%, var(--pq-surface))); }
.er-impasse-card-inspector .er-impasse-card-hdr { background: light-dark(#fff3e8, color-mix(in oklab, #fff3e8 14%, var(--pq-surface))); color: light-dark(#c05621, color-mix(in oklab, #c05621 45%, white)); border-bottom: 1px solid light-dark(#f6d2b0, color-mix(in oklab, #f6d2b0 30%, var(--pq-surface))); }
.er-impasse-card-body {
    padding: 10px 12px;
    line-height: 1.6;
    color: var(--text);
}
.er-extra-round-bar {
    display: flex;
    align-items: center;
    gap: 12px;
    padding-top: 4px;
}
.er-extra-round-hint {
    font-size: 0.8rem;
    color: var(--text-muted);
}

/* ── Replacement text section ───────────────────────────────────────── */
.er-replacement-section {
    flex-shrink: 0;
    border-top: 1px solid var(--border);
    padding: 14px 20px;
    background: var(--surface);
}
.er-replacement-label {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: light-dark(#276749, color-mix(in oklab, #276749 45%, white));
    margin-bottom: 8px;
}
.er-replacement-text {
    font-size: 0.88rem;
    line-height: 1.65;
    color: var(--text);
    background: light-dark(#f0fff4, var(--pq-elevated));
    border: 1px solid light-dark(#9ae6b4, color-mix(in oklab, #9ae6b4 30%, var(--pq-surface)));
    border-radius: 6px;
    padding: 10px 14px;
    word-break: break-word;
}
.er-replacement-text .repl-para {
    margin: 0 0 10px;
}
.er-replacement-text .repl-para:last-child {
    margin-bottom: 0;
}
.er-replacement-text .repl-bullet {
    padding-left: 1.2em;
    text-indent: -1.2em;
    margin: 2px 0;
}
.er-replacement-text .repl-bullet::before {
    content: "•  ";
    color: light-dark(#276749, color-mix(in oklab, #276749 45%, white));
}
.er-replacement-text .repl-numbered {
    margin: 2px 0;
}
.er-replacement-text table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.83rem;
    margin: 4px 0;
}
.er-replacement-text table th,
.er-replacement-text table td {
    border: 1px solid var(--border-color, #d1d5db);
    padding: 4px 8px;
    text-align: left;
    vertical-align: top;
}
.er-replacement-text table th {
    background: var(--surface-2, #f3f4f6);
    font-weight: 600;
}
.er-agreed-actions-text table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.83rem;
    margin: 4px 0;
}
.er-agreed-actions-text table th,
.er-agreed-actions-text table td {
    border: 1px solid #e8c96a;
    padding: 4px 8px;
    vertical-align: top;
}
.er-agreed-actions-text table th {
    background: light-dark(#fef3c7, color-mix(in oklab, #fef3c7 14%, var(--pq-surface)));
    font-weight: 600;
}
.er-gen-thinking {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 0.82rem;
    color: var(--text-muted);
    font-style: italic;
}

.er-agreed-actions-section {
    margin-top: 12px;
    border-top: 1px solid var(--border);
    padding-top: 10px;
}
.er-agreed-actions-label {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: light-dark(#C05621, color-mix(in oklab, #C05621 45%, white));
    margin-bottom: 6px;
}
.er-agreed-actions-text {
    font-size: 0.85rem;
    background: light-dark(#FFFBEB, color-mix(in oklab, #FFFBEB 14%, var(--pq-surface)));
    border: 1px solid #F6D860;
    border-radius: 6px;
    padding: 10px 14px;
    white-space: pre-wrap;
    word-break: break-word;
    color: light-dark(#7A4F1A, color-mix(in oklab, #7A4F1A 45%, white));
}


.er-bubble-row {
    display: flex;
    flex-direction: column;
    max-width: 78%;
}
.er-bubble-row-inspector { align-self: flex-start; }
.er-bubble-row-qe        { align-self: flex-end; align-items: flex-end; }
.er-bubble-speaker {
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-bottom: 4px;
}
/* ── Debate voice palette — SINGLE SOURCE OF TRUTH (B-129, rule 6). Both the
   voice CARDS (.er-voice-* headers) and the transcript BUBBLES (.er-bubble-*)
   consume these custom properties — the debate-4voice-sandbox pattern restored
   after the S1 bake-in wrote two independent blocks and the Process Owner /
   Inspector bubbles drifted (Chakra purple / 2-voice-era orange). Card values
   are the sandbox-faithful truth. Dark values honour prefers-color-scheme. */
:root{
  --dv-ia-bg:#e6fffa;   --dv-ia-fg:#0e7c86;   --dv-ia-bd:#b2f5ea;
  --dv-insp-bg:#fcecea; --dv-insp-fg:#c0392b; --dv-insp-bd:#f2c6c0;
  --dv-qe-bg:#e8f1fb;   --dv-qe-fg: var(--pq-blue-900);   --dv-qe-bd:#c3d9f0;
  --dv-po-bg:#fbf3df;   --dv-po-fg:#b8860b;   --dv-po-bd:#ecd9a8;
  --dv-you-bg:#f0fff4;  --dv-you-fg:#2f855a;  --dv-you-bd:#c6f6d5;
}
@media (prefers-color-scheme: dark){
  :root{
    --dv-ia-bg:#123330;   --dv-ia-fg:#4fd1c5;   --dv-ia-bd:#234e52;
    --dv-insp-bg:#2a1618; --dv-insp-fg: var(--pq-rose-300); --dv-insp-bd:#5c2a2e;
    --dv-qe-bg:var(--pq-surface);   --dv-qe-fg: var(--pq-blue-350);   --dv-qe-bd:#2c5282;
    --dv-po-bg:#2a2310;   --dv-po-fg:#e9c46a;   --dv-po-bd:#6b5a23;
    --dv-you-bg:#14331e;  --dv-you-fg:#68d391;  --dv-you-bd: var(--pq-green-800);
  }
}
.er-bubble-row-inspector .er-bubble-speaker { color: var(--dv-insp-fg); }
.er-bubble-row-qe        .er-bubble-speaker { color: var(--dv-qe-fg); }
.er-bubble {
    padding: 10px 14px;
    border-radius: 16px;
    font-size: 0.86rem;
    line-height: 1.6;
    color: var(--text);
}
.er-bubble-inspector {
    background: var(--dv-insp-bg);
    border-bottom-left-radius: 4px;
    border: 1px solid var(--dv-insp-bd);
}
.er-bubble-qe {
    background: var(--dv-qe-bg);
    border-bottom-right-radius: 4px;
    border: 1px solid var(--dv-qe-bd);
}
/* ── 4-voice debate (S1, project_persona_debate_tuning §13) — protect voices
   left, enable voices right, chair centred. Colors come from the shared
   voice palette above. */
.er-bubble-row-internal_auditor { align-self: flex-start; }
.er-bubble-row-process_owner    { align-self: flex-end; align-items: flex-end; }
.er-bubble-row-you              { align-self: flex-end; align-items: flex-end; }
.er-bubble-row-chair            { align-self: center; align-items: center; max-width: 90%; }
.er-bubble-row-internal_auditor .er-bubble-speaker { color: var(--dv-ia-fg); }
.er-bubble-row-process_owner    .er-bubble-speaker { color: var(--dv-po-fg); }
.er-bubble-row-you              .er-bubble-speaker { color: var(--dv-you-fg); }
.er-bubble-row-chair            .er-bubble-speaker { color: var(--text-muted, #718096); }
.er-bubble-internal_auditor { background: var(--dv-ia-bg); border: 1px solid var(--dv-ia-bd); border-bottom-left-radius: 4px; }
.er-bubble-process_owner    { background: var(--dv-po-bg); border: 1px solid var(--dv-po-bd); border-bottom-right-radius: 4px; }
.er-bubble-you              { background: var(--dv-you-bg); border: 1px solid var(--dv-you-bd); border-bottom-right-radius: 4px; }
.er-bubble-chair            { background: var(--surface, #f7fafc); border: 1px dashed var(--border, #cbd5e0); font-style: italic; border-radius: 12px; font-size: 0.82rem; }
@media (prefers-color-scheme: dark) {
    .er-bubble-chair            { background: var(--pq-surface); }
}

/* ══════════════════════════════════════════════════════════════════════════
   #242 — THE SHARED ENGINEERING DATA BLOCK (.pg-datablock)
   ONE component (rc_data_block.js RCDataBlock), consumed by BOTH surfaces: the
   BPMN projection and the Flow / Process Map.

   Craig 2026-07-20, VERIFY FAILED on v4.2.169 — measured root cause: the block
   had NO stylesheet rule at all. Its entire box (including `display:flex`, on
   which the WT-30 contract depends — 50vh cap, pinned drag bar, internal
   scroll) lived in an inline cssText string, and TWO separate call sites
   toggled visibility with `style.display = ''`, which DELETES that inline
   declaration. Measured in headless Chrome: computed display went flex→block
   on the first toggle, collapsing the capped/scrolling body. Only the FLOW
   instance was written to, which is exactly why the BPMN one looked fine.

   The fix is structural, not a patch: the LOOK lives here, in CSS, where a
   display toggle cannot reach it; visibility is a `.hidden` class flipped by
   ONE writer (RCDataBlock.sync). Only per-surface position (left/bottom, and
   whatever the user drags it to) stays inline. */
.pg-datablock {
    position: absolute;
    z-index: 6;
    /* #461/#711 — WIDTH AND HEIGHT ARE THE USER'S NOW, within bounds.
       This was a hard `width: 330px`, which is the "long" half of Craig's
       complaint: a 96px key column inside a 330px box wraps every real value
       into a narrow ribbon. The corner grip writes inline `width`/`height`, so
       these are the FLOOR and CEILING rather than the size — exactly as
       `left`/`top` are inline and the box rules are not.
       The 50vh line keeps WT-30's contract as a CEILING: grow to at most half
       the screen, then the CONTENT scrolls internally, so the title bar (the
       drag handle) never leaves the screen and the block can always be moved. */
    width: 330px;
    min-width: 240px;
    max-width: min(720px, 60vw);
    min-height: 120px;
    max-height: 50vh;
    display: flex;
    flex-direction: column;
    background: var(--rc-panel, #fff);
    border: 1.5px solid #94a3b8;
    border-radius: 4px;
    /* #461 — the ONE text-size lever. Every size inside the block is `em` off
       this, so the A+/A− control changes one number and the whole block follows.
       It was 10.5px with five independent px literals below it, which is why
       "the text is tiny" had no single thing to turn up. */
    font-size: 10.5px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
}
/* #461/#711 — THE CORNER GRIP. Deliberately its own element rather than CSS
   `resize: both`: the native resizer needs `overflow` set on the box, and this
   box's `overflow` is load-bearing for the WT-30 flex contract (#242 records
   what happened the last time something reached into it). Same mechanism as the
   title-bar drag — a pointer handler writing inline styles — so there is one
   interaction model on this component, not two. */
.pg-datablock .rc-db-grip {
    position: absolute;
    right: 0;
    bottom: 0;
    width: 14px;
    height: 14px;
    cursor: nwse-resize;
    z-index: 2;
    background:
        linear-gradient(135deg, transparent 50%, var(--rc-db-dim, #94a3b8) 50%,
                        var(--rc-db-dim, #94a3b8) 62%, transparent 62%,
                        transparent 74%, var(--rc-db-dim, #94a3b8) 74%,
                        var(--rc-db-dim, #94a3b8) 86%, transparent 86%);
    border-radius: 0 0 3px 0;
}
.pg-datablock .rc-db-grip:hover { background-color: var(--rc-db-head, #e2e8f0); }
.pg-datablock.hidden { display: none; }
.pg-datablock .rc-db-body { overflow-y: auto; flex: 1; min-height: 0; }
/* #461 — THE ROW GRAMMAR, which had no stylesheet rule at all.
   `_metaRow` emits `.rc-db-row` / `.rc-db-k` / `.rc-db-v` and only the three
   #677 authored rows had any styling, through `.rc-db-meta` further down this
   file. Every other row carried its layout inline, so the key column was a
   hardcoded 96px in two separate builders. Stating it once here is what lets a
   wider block actually use the width — which #461's own body asks first: "does
   a wider block make the strip readable without a text change?"
   The key column is now proportional, so widening the block widens the VALUE
   rather than leaving a 96px label beside a 600px ribbon. */
.pg-datablock .rc-db-row {
    display: flex;
    border-bottom: 1px solid var(--rc-db-line, #e2e8f0);
}
.pg-datablock .rc-db-k {
    flex: 0 0 30%;
    max-width: 132px;
    padding: 0.38em 0.76em;
    font-weight: 700;
    color: var(--rc-db-mute, #64748b);
    border-right: 1px solid var(--rc-db-line, #e2e8f0);
}
.pg-datablock .rc-db-v {
    flex: 1;
    min-width: 0;
    padding: 0.38em 0.76em;
    color: var(--rc-db-fg, #0f172a);
    overflow-wrap: anywhere;
}
.pg-datablock .rc-db-v.rc-db-empty { color: var(--rc-db-dim, #94a3b8); font-style: italic; }
/* #461 — the counts strip and the notes block, in `em` so the one lever reaches
   them. They were 10px and 9.5px literals, which is why turning the container
   size up left the two smallest things on the block exactly as they were. */
.pg-datablock .rc-db-counts {
    display: flex; flex-wrap: wrap; gap: 0.38em 0.95em;
    padding: 0.57em 0.76em;
    border-top: 2px solid var(--rc-db-rule, #cbd5e1);
    color: var(--rc-db-fg, #0f172a);
    font-size: 0.95em;
}
.pg-datablock .rc-db-notes {
    padding: 0.48em 0.76em;
    color: var(--rc-db-mute, #64748b);
    font-size: 0.9em;
    line-height: 1.35;
    border-top: 1px solid var(--rc-db-line, #e2e8f0);
}
/* #649 — the block's Edit / Review footer. Pinned below the scroll body, like the
   drag bar is pinned above it, so the two doors into the metadata window are
   always reachable however long the block's contents run. */
.pg-datablock .rc-db-foot {
    display: flex; gap: 6px; flex: none;
    padding: 6px 8px;
    border-top: 1px solid var(--rc-db-rule, #cbd5e1);
    background: var(--rc-db-head, #e2e8f0);
    border-radius: 0 0 3px 3px;
}
.pg-datablock .rc-db-btn {
    display: inline-flex; align-items: center; gap: 5px;
    font: 700 10px/1.4 inherit;
    padding: 3px 9px;
    border: 1px solid var(--rc-db-dim, #94a3b8);
    border-radius: 3px;
    background: var(--rc-db-btn-bg, #f8fafc);
    color: var(--rc-db-btn-fg, #334155);
    cursor: pointer;
}
.pg-datablock .rc-db-btn:hover { border-color: var(--btn-primary); color: var(--btn-primary); }
.pg-datablock .rc-db-btn .ic { width: 11px; height: 11px; }
/* #461 — pushes the text-size pair to the right of the footer, so the two DOORS
   into the record and the two controls that change how it LOOKS read as separate
   groups rather than one row of four. */
.pg-datablock .rc-db-foot-sp { flex: 1; }
/* An icon-only button needs no label gap and should stay square. */
.pg-datablock .rc-db-btn.rc-db-text { padding: 3px 6px; gap: 0; }


/* #258 — the palette ⓘ toggle shows an active face while the block is open */
.pg-palette button[data-tool="datablock"].is-active {
    color: var(--accent, #4f6ddb);
}

/* #295 (Craig 2026-07-24) — the Level 1 process-overview surface host */
.pg-level1 { position: absolute; inset: 0; overflow: auto; background: var(--bg-white, #fff); z-index: 2;
    /* #1903 (FE-F17) - Level 1's palette, read by level1.js; dark below (#1903 block) */
    --rc-l1-bg: #eef3f0; --rc-l1-card: var(--pq-white); --rc-l1-line: #dbe4dd;
    --rc-l1-ink: #16283b; --rc-l1-eyebrow: var(--pq-blue-900); }
.pg-level1.hidden { display: none; }


/* #295 — enlarge the pie-halo handles so the icons read (Craig 2026-07-24: too small).
   Two-class specificity (.joint-halo .handle) beats the runtime joint-plus.css. */
.joint-halo .handle {
    width: 32px; height: 32px;
    background-size: 24px 24px; background-repeat: no-repeat; background-position: center;
}


/* #330 Drop 2 — connector-menu escape hatches (Reset route / Reset label) */
.rc-halo-linkbtn { margin-top: 6px; width: 100%; text-align: left; padding: 8px 10px; border: 1px solid var(--border, #cbd5e0); border-radius: 7px; background: transparent; color: var(--primary-fg, #1a2b45); font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; }
.rc-halo-linkbtn:hover { background: rgba(39, 107, 178, .10); }

.rc-halo-pop, .rc-halo-inspector {
    background: var(--bg-white, #fff); border: 1px solid var(--border, #cbd5e0);
    border-radius: 10px; box-shadow: 0 12px 30px rgba(8, 20, 40, .24); padding: 10px;
    font-family: var(--sans, system-ui, sans-serif);
}
.rc-halo-colour { display: flex; align-items: center; gap: 8px; }
.rc-halo-swrow { display: grid; grid-template-columns: repeat(6, 1fr); gap: 5px; }
/* #414 — the rename popover's hint line. It used to borrow .rc-halo-swrow, the
   colour picker's six-column swatch GRID, which stretched the panel far past its
   340px textarea — Craig: "far too much unused space to the right hand side,
   needs to be closer in size to the edit box". A hint is one line of text, so it
   gets a plain block, and the panel sizes to its widest real child. */
.rc-halo-hint { margin-top: 6px; font-size: 12px; line-height: 1.35; }
.rc-halo-rename { width: max-content; max-width: min(92vw, 380px); }
.rc-halo-sw { width: 22px; height: 22px; border-radius: 5px; border: 1px solid rgba(0,0,0,.18); cursor: pointer; padding: 0; }
.rc-halo-sw:focus-visible { outline: 3px solid #89c0f5; outline-offset: 1px; }
.rc-halo-pick { width: 34px; height: 26px; border: 1px solid var(--border, #cbd5e0); border-radius: 6px; background: var(--bg-white, #fff); cursor: pointer; padding: 0; }
.rc-halo-input { width: 240px; font: inherit; font-size: 14px; padding: 8px 10px; border: 1px solid var(--border, #cbd5e0); border-radius: 7px; background: var(--bg-white, #fff); color: var(--primary-fg, #1a2b45); }
.rc-halo-input:focus-visible { outline: 3px solid #89c0f5; outline-offset: 1px; }

/* #382 — THE APPEARANCE PANEL. `.rc-halo-colour` stays a single flex row for the
   fill-only callers it has always served; the moment the panel carries more than
   one section it stacks, so Fill / Text colour / Text style read as a short form
   rather than a widening strip. One rule change, both shapes. */
.rc-halo-appearance { flex-direction: column; align-items: stretch; gap: 4px; min-width: 176px; }
.rc-halo-appearance .rc-hi-label:first-of-type { margin-top: 0; }
.rc-halo-aptitle { font-size: 12px; font-weight: 700; color: var(--primary-fg, #1a2b45); margin-bottom: 2px; }
/* The current value is stated, not implied — without it the panel cannot answer
   "what is this set to now", which is the first thing anyone asks of a picker. */
.rc-halo-sw.is-current { outline: 2px solid var(--primary-fg, #1a2b45); outline-offset: 2px; }
.rc-halo-stylerow { grid-template-columns: repeat(2, 1fr); gap: 6px; }
.rc-halo-styletog {
    height: 26px; border-radius: 6px; cursor: pointer; padding: 0;
    border: 1px solid var(--border, #cbd5e0); background: var(--bg-white, #fff);
    color: var(--primary-fg, #1a2b45); font-size: 14px; line-height: 1;
}
.rc-halo-styletog:hover { border-color: var(--primary-fg, #1a2b45); }
.rc-halo-styletog.is-on { background: var(--primary-fg, #1a2b45); color: light-dark(#fff, #0b1523); border-color: var(--primary-fg, #1a2b45); }
.rc-halo-styletog:focus-visible { outline: 3px solid #89c0f5; outline-offset: 1px; }

.rc-halo-inspector { width: 320px; max-height: 60vh; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; }
.rc-hi-title { font-family: var(--serif, Georgia, serif); font-size: 16px; font-weight: 700; color: var(--primary-fg, #1a2b45); margin-bottom: 6px; }
.rc-hi-label { font-size: 10.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--text-muted, #64748b); margin-top: 8px; }
.rc-hi-list { margin: 4px 0 0; padding-left: 18px; font-size: 13px; color: var(--primary-fg, #1a2b45); }
.rc-hi-list li { margin: 2px 0; line-height: 1.35; }
.rc-hi-empty { font-size: 12.5px; color: var(--text-muted, #94a3b8); font-style: italic; margin-top: 2px; }
.rc-hi-notes { width: 100%; min-height: 64px; margin-top: 4px; font: inherit; font-size: 13px; padding: 8px 10px; border: 1px solid var(--border, #cbd5e0); border-radius: 7px; background: var(--bg-white, #fff); color: var(--primary-fg, #1a2b45); resize: vertical; }
@media (prefers-color-scheme: dark) {
    /* #1655 — THE PIE ITSELF. Craig 2026-09-26: "The BPMN Halo is all white".
       joint-plus.css paints the pie's disc (`.handles`) white with a black rim
       and its toggle white, and nothing here answered them, so in dark the
       ring's light-ink icons (rc_halo.js switches them) sat on a white disc.
       `:root` lifts these above the library's four-class rules, which load
       after this file and would otherwise win the tie. Same colours as the
       halo's own popovers below, so the ring and what it opens match. */
    :root .joint-halo.joint-theme-default.pie .handles { background-color: var(--pq-elevated); border-color: var(--pq-border); }
    :root .joint-halo.joint-theme-default.pie .slice { stroke: #33456a; }
    :root .joint-halo.joint-theme-default.pie .slice:hover,
    :root .joint-halo.joint-theme-default.pie .handle.selected .slice { fill: #263452; }
    :root .joint-halo.joint-theme-default.pie .pie-toggle { background-color: var(--pq-elevated); border-color: var(--pq-border); }
    .rc-halo-pop, .rc-halo-inspector { background: var(--pq-elevated); border-color: var(--pq-border); }
    .rc-halo-pick, .rc-halo-input, .rc-hi-notes { background: var(--pq-surface); border-color: var(--pq-border); color: #e6edf7; }
    .rc-hi-title, .rc-hi-list { color: #e6edf7; }
    /* #382 — the appearance panel in dark. The current-swatch ring and the style
       toggles both key off --primary-fg, which is near-black, so both go invisible
       here without their own values. */
    .rc-halo-aptitle { color: #e6edf7; }
    .rc-halo-sw.is-current { outline-color: #89c0f5; }
    .rc-halo-styletog { background: var(--pq-surface); border-color: var(--pq-border); color: #e6edf7; }
    .rc-halo-styletog:hover { border-color: #89c0f5; }
    .rc-halo-styletog.is-on { background: #89c0f5; border-color: #89c0f5; }
}

/* ── #330 (Craig 2026-07-24) — BPMN halo shape-type picker + editing chrome ── */
.rc-halo-shapes { display: flex; flex-direction: column; gap: 3px; min-width: 230px; }
.rc-halo-shape { text-align: left; padding: 7px 10px; border: 1px solid transparent; border-radius: 7px; background: transparent; color: var(--primary-fg, #1a2b45); font: inherit; font-size: 13.5px; cursor: pointer; }
.rc-halo-shape:hover { background: rgba(39, 107, 178, .10); }
.rc-halo-shape.is-current { border-color: #276bb2; font-weight: 700; }
.rc-halo-shape.is-disabled { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; cursor: default; color: var(--text-muted, #94a3b8); }
.rc-halo-shape.is-disabled:hover { background: transparent; }
.rc-halo-shape.is-disabled em { font-style: italic; font-size: 11px; opacity: .85; white-space: nowrap; }

/* #1768 — the lane-change question is a blocking modal, not a popover (was z-index 700). */
.pg-bpmn-confirm-back { position: fixed; inset: 0; background: rgba(8,20,40,.35); z-index: calc(var(--z-dialog) + 10); display: flex; align-items: center; justify-content: center; }
.pg-bpmn-confirm { width: 420px; max-width: 92vw; background: var(--bg-white, #fff); border: 1px solid var(--border, #cbd5e0); border-radius: 12px; box-shadow: 0 20px 50px rgba(8,20,40,.35); padding: 18px 20px; font-family: var(--sans, system-ui, sans-serif); color: var(--primary-fg, #1a2b45); }
.pg-bpmn-confirm-h { font-family: var(--serif, Georgia, serif); font-size: 17px; font-weight: 700; margin-bottom: 8px; }
.pg-bpmn-confirm-b { font-size: 13.5px; line-height: 1.5; color: var(--text-muted, #475569); margin-bottom: 12px; }
.pg-bpmn-confirm-list { margin: 4px 0 8px 18px; font-size: 13px; }   /* #1846: the steps and findings a Delete orphans */
.pg-bpmn-confirm-l { display: block; font-size: 10.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--text-muted, #64748b); margin-bottom: 4px; }
.pg-bpmn-confirm-reason { width: 100%; font: inherit; font-size: 14px; padding: 8px 10px; border: 1px solid var(--border, #cbd5e0); border-radius: 7px; background: var(--bg-white, #fff); color: var(--primary-fg, #1a2b45); box-sizing: border-box; }
.pg-bpmn-confirm-reason:focus-visible { outline: 3px solid #89c0f5; outline-offset: 1px; }
.pg-bpmn-confirm-row { display: flex; justify-content: flex-end; gap: 8px; margin-top: 14px; }
.pg-bpmn-confirm-row button { font: inherit; font-size: 13.5px; font-weight: 600; padding: 8px 16px; border-radius: 8px; cursor: pointer; }
.pg-bpmn-confirm-cancel { background: transparent; border: 1px solid var(--border, #cbd5e0); color: var(--primary-fg, #1a2b45); }
.pg-bpmn-confirm-ok { background: #276bb2; border: 1px solid #276bb2; color: #fff; }
.pg-bpmn-confirm-ok:hover { background: #1f5a99; }

@media (prefers-color-scheme: dark) {
    .pg-bpmn-confirm { background: var(--pq-elevated); border-color: var(--pq-border); color: #e6edf7; }
    .rc-halo-shape { color: #e6edf7; }
    .pg-bpmn-confirm-reason { background: var(--pq-surface); border-color: var(--pq-border); color: #e6edf7; }
    .pg-bpmn-confirm-cancel { color: #e6edf7; border-color: var(--pq-border); }
    .pg-bpmn-confirm-b { color: #aebbd0; }
}

/* Fail-loud surface — shown when a requested level cannot render (no fallback). */
.pg-proc-error {
    position: absolute; inset: 0; display: none; align-items: center;
    justify-content: center; padding: 32px; z-index: 5;
    background: var(--bg-white, #fff);
}
.pg-proc-error-box {
    max-width: 520px; text-align: center; border: 1px solid var(--border, #cbd5e0);
    border-left: 4px solid #b8860b; border-radius: 10px; padding: 24px 28px;
    background: var(--mist, #f7f9fc);
}
.pg-proc-error-h { font-family: var(--serif); font-size: 18px; font-weight: 700;
    color: var(--primary-fg, #1a2b45); margin-bottom: 10px; }
.pg-proc-error-p { font-size: 14px; line-height: 1.5; color: var(--text-muted, #55647e); }

@media (prefers-color-scheme: dark) {

    .pg-proc-error { background: var(--pq-surface); }
    .pg-proc-error-box { background: var(--pq-elevated); border-color: var(--pq-border); }
    .pg-proc-error-h { color: #e6edf7; }
    .pg-proc-error-p { color: #9fb0cc; }
}

/* ── D-037 BPMN view — the canvas host (U-4, v4.2.92) ──────────────────────
   Craig 2026-07-13: "No scroll bars, the zoom in and zoom out buttons dont work".
   The host had NO rule at all — it was an unstyled div inside an absolutely-
   positioned, overflow:hidden .pg-canvas-host, so the paper had no viewport to
   scroll inside and anything past its edge was simply unreachable. The tab strip
   is chrome and stays put. Since #1846 the adopted editor scrolls its own
   PaperScroller inside .rc-bpmn-editor-host; the hand-built view's scroll
   viewport and its scrollbar rules were burned with bpmn_view.js. */
.pg-bpmn-host {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: var(--bg);
    --rc-bpmn-paper: var(--pq-white);
    --rc-bpmn-gutter: var(--pq-mist-200);
    --rc-bpmn-gutter-line: var(--pq-slate-750);
    --rc-bpmn-band-fg: var(--pq-night-950);
    --rc-bpmn-task-fill: var(--pq-white);
    --rc-bpmn-task-stroke: #5b7ab5;
    --rc-bpmn-call-stroke: var(--pq-navy-800);
    --rc-bpmn-task-num: var(--pq-slate-750);
    --rc-bpmn-task-title: #2d3748;
    --rc-bpmn-gw-fill: #fff8ec;
    --rc-bpmn-gw-stroke: #c08015;
    --rc-bpmn-gw-fg: #7c4a03;
    --rc-bpmn-ev-fill: var(--pq-white);
    --rc-bpmn-ev-start: #16a34a;
    --rc-bpmn-ev-end: #e11d2a;
    --rc-bpmn-ev-start-fill: #dcfce7;
    --rc-bpmn-ev-end-fill: #fee2e2;
    --rc-bpmn-ev-stroke: #111827;
    --rc-bpmn-ev-link: var(--pq-navy-800);
    --rc-bpmn-ev-glyph: #b91c1c;
    --rc-bpmn-ev-text: #000000;
    --rc-bpmn-ev-caption: var(--pq-night-950);
    --rc-bpmn-note-fill: var(--pq-white);
    --rc-bpmn-note-stroke: var(--pq-slate-750);
    --rc-bpmn-note-fg: var(--pq-night-950);
    --rc-bpmn-art-fill: #f8fafc;
    --rc-bpmn-art-top: #eef2f7;
    --rc-bpmn-art-stroke: #64748b;
    --rc-bpmn-art-fg: var(--pq-slate-750);
    --rc-bpmn-link: var(--pq-navy-800);
    --rc-bpmn-link-lbl-bg: var(--pq-white);
    --rc-bpmn-link-lbl-bd: #7d96b4;
    --rc-bpmn-link-lbl-fg: var(--pq-night-950);
    --rc-bpmn-port-fill: #dbeafe;
    --rc-bpmn-port-stroke: #1d4ed8;
    --rc-bpmn-select: var(--pq-blue-900);
    --rc-bpmn-finding: #b91c1c;
}

/* ── D-037 BPMN view — multi-process tabs (monochrome, Craig M3 gate) ── */
/* #1553 — `.doc-surface-tabs` / `.doc-surface-tab` share these declarations
   rather than copying them. Craig, on the quality report: "make it like the sub
   process tab", so the visual language is deliberately the same one.

   TWO NAMES, ONE RULE SET, and the reason is the note under .pg-bpmn-pagetabs
   below: this file deliberately does NOT give the page tabs the phase strip's
   class, because a phase and a page are different KINDS of choice and one visual
   language for both would say they are the same. Choosing between the document
   and its quality report IS the same kind of choice as choosing a sub-process
   page — peer views of one document — which is why sharing is right here and was
   wrong there. Sharing the CLASS itself would have put a BPMN name on the
   document surface; sharing the SELECTOR keeps both names honest and leaves one
   place to change the look. */
.doc-surface-tabs{display:flex;gap:4px;padding:6px 10px 0;background:#fff;border-bottom:1px solid #444;}
.doc-surface-tab{border:1px solid #444;border-bottom:none;background:#fff;color:#000;
    border-radius:6px 6px 0 0;padding:4px 14px;font-size:12px;cursor:pointer;}
.doc-surface-tab.on{background:#000;color:#fff;font-weight:600;}

/* #1553 — THE QUALITY REPORT PANEL. A sibling of .doc-sheet, so it deliberately
   does NOT take the sheet's page chrome: the sheet is the controlled document and
   this is a statement about it. Colours come from the theme tokens rather than
   literals so it follows prefers-color-scheme with the rest of the app. */
/* #2113 — the panel's width, padding and card come from the sheet's rule (.doc-sheet,
   .doc-qr), so its edges are the header row's. Only its own text colour is here. */
.doc-qr{ color:var(--text, #111827); }
.doc-qr-head{ display:flex; align-items:center; justify-content:space-between; gap:12px; }
.doc-qr-head h2{ margin:0; font-size:1.1rem; }
.doc-qr-note{ font-style:italic; opacity:.85; max-width:70ch; }
.doc-qr-warn{ font-weight:600; color:var(--conflict-text, #b42318); max-width:70ch; }
.doc-qr-table{ border-collapse:collapse; margin:14px 0; width:100%; font-size:13px; }
.doc-qr-table th, .doc-qr-table td{
    border:1px solid var(--border, #d0d5dd); padding:5px 9px; text-align:left;
    vertical-align:top;
}
.doc-qr-table th{ background:var(--surface-2, #f3f4f6); font-weight:600; }

/* #1647 — THE FINDINGS TABLE CARRIES PROSE NOW, so it needs column widths the
   summary can actually use. Craig asked for "up to 3 lines long by 1 or 2 would
   be better": the width is what decides that, so the three short columns are
   held narrow and the two prose columns take the rest. `table-layout:fixed` is
   what makes the percentages hold rather than being renegotiated by the longest
   cell, which is how a summary column ends up 40 characters wide. */
.doc-qr-findings{ table-layout:fixed; }
.doc-qr-findings th:nth-child(1), .doc-qr-findings td:nth-child(1){ width:9%; }
.doc-qr-findings th:nth-child(2), .doc-qr-findings td:nth-child(2){ width:14%; }
.doc-qr-findings th:nth-child(3), .doc-qr-findings td:nth-child(3){ width:14%; }
.doc-qr-findings th:nth-child(4), .doc-qr-findings td:nth-child(4){ width:38%; }
.doc-qr-findings th:nth-child(5), .doc-qr-findings td:nth-child(5){ width:25%; }
/* Long quoted passages and unbroken identifiers must wrap rather than force the
   table wider than its host. */
.doc-qr-findings td{ overflow-wrap:anywhere; }

/* An ABSENCE that states which absence it is (#1647). Muted, because it is not a
   value — but present, because a blank cell is a column that answers nothing,
   which is the defect the paragraph anchor was reported for. */
.doc-qr-none{ font-style:italic; opacity:.7; }

/* #1846 pass 4c — THE BPMN SURFACE IS THE ADOPTED EDITOR (bpmn_surface.js). The
   kit's frame expects a full-height flex column (its demo's #app), so the editor
   host fills the BPMN host above the page tabs. The Objects button shows or hides
   the editor's own docked palette. */
.rc-bpmn-surface .rc-bpmn-editor-host{
    flex:1 1 auto; min-height:0; position:relative;
    display:flex; flex-direction:column; width:100%;
}
.rc-bpmn-surface.rc-hide-stencil .stencil-container{ display:none; }

/* #1846 (regcheck's measurement on dev v4.3.1484) — ONE ROW OF CHROME ABOVE THE
   DRAWING. It was three: the phase tabs (names wrapping to two lines), then the
   editor's print / "Save as: PNG" bar, then its Export .bpmn strip, leaving the
   drawing about 520px. Now the phase tabs (lent to the editor's frame by
   bpmn_surface.js _dockPhaseTabs), the print / Save-as tools and the Export
   strip share the first row of a grid, and the drawing has the rest. The row's
   ground and rule are the phase strip's own look, drawn once behind the whole
   row (::before), so the row reads as one bar whether or not there are phases. */
.rc-bpmn-surface .rc-bpmn-editor{
    display:grid;
    grid-template-columns:minmax(0, 1fr) auto auto;
    grid-template-rows:auto minmax(0, 1fr);
}
.rc-bpmn-surface .rc-bpmn-editor::before{
    content:""; grid-area:1 / 1 / 2 / -1;
    background:#fff; border-bottom:1px solid #444;
}
/* #1956 — the process navigator takes the slot the phase tabs had. */
.rc-bpmn-surface .rc-bpmn-editor > .rc-bpmn-procnav{
    grid-area:1 / 1; min-width:0; align-self:center;
}
.rc-bpmn-surface .rc-bpmn-editor > .app-toolbar{ grid-area:1 / 2; min-height:0; align-self:center; }
.rc-bpmn-surface .rc-bpmn-editor > .app-toolbar .joint-toolbar{
    width:auto; height:auto; min-height:32px; padding:2px 6px;
    background:transparent; border-bottom:none;
}
/* The Export strip ends clear of the Findings tab rail and any open drawer:
   --rc-drawer-occupied is the measured width of both (RCDrawers.applyReserve),
   the same measurement the minimaps move by. */
.rc-bpmn-surface .rc-bpmn-editor > .rc-editor-strip{
    grid-area:1 / 3; align-self:center;
    padding-right:calc(8px + var(--rc-drawer-occupied, 0px));
}
.rc-bpmn-surface .rc-bpmn-editor > .app-body{ grid-area:2 / 1 / 3 / -1; }
/* #1956 — "Go to process": a native select, so the OS draws its list and its
   type-ahead; theme tokens so it follows the colour scheme. */
.rc-bpmn-procnav{ display:flex; align-items:center; gap:6px; padding:0 10px;
    font-size:12px; color:var(--text, #111827); white-space:nowrap; }
.rc-bpmn-procnav.hidden{ display:none; }
.rc-bpmn-procnav-select{ max-width:320px; min-width:0; font-size:12px; padding:2px 4px;
    color:var(--text, #111827); background:var(--card, #fff);
    border:1px solid var(--border, #9ca3af); border-radius:4px; }
/* The band gone to is outlined for a moment; the one global prefers-reduced-motion rule stills it. */
.rc-phase-band.rc-phase-flash > rect:first-of-type{ stroke:var(--primary, #2563eb); stroke-width:4;
    animation:rc-phase-flash 1.6s ease-out; }
@keyframes rc-phase-flash{ from{ stroke-opacity:1; } to{ stroke-opacity:0; } }
/* #343 — the width grip at the right edge of a band's header (moved there from
   the phase tab, #1956): quiet until the pointer arrives. */
.rc-phase-grip{ fill:transparent; cursor:col-resize; transition:fill .12s ease; }
.rc-phase-grip:hover,.rc-phase-grip.is-dragging{ fill:light-dark(#0000002a, rgba(255,255,255,.22)); }
@media (prefers-color-scheme: dark){
    .rc-bpmn-surface .rc-bpmn-editor::before{ background:var(--surface, #1f2329); border-bottom-color:var(--pq-border); }
}

/* #1493 — THE PAGE TABS, along the bottom like a spreadsheet's sheet tabs.
   Craig 2026-09-07: "like browser tabs or excel sheets (along the bottom)".
   Deliberately NOT the same class as the phase strip above: a phase is a band
   within one page, a page is a level of the process, and one visual language for
   both would say they are the same kind of choice. So these tuck UP into the
   canvas (radius on the bottom corners) where the phase tabs tuck down. */
.pg-bpmn-pagetabs{
    display:flex;gap:4px;padding:0 10px 6px;background:#fff;
    border-top:1px solid #444;flex:0 0 auto;overflow-x:auto;
}
/* The strip's own 10px left padding is all the inset there is. The hand-built
   view once pushed the tabs right from JS (GUTTER_W + POOL_W) to clear its lane
   gutter; that gutter and the JS were burned with bpmn_view.js (#1846), and the
   adopted editor draws lane names inside the pool, so nothing needs clearing. */
.pg-bpmn-pagetab{
    border:1px solid #444;border-top:none;background:#fff;color:#000;
    border-radius:0 0 6px 6px;padding:4px 12px;font-size:12px;cursor:pointer;
    white-space:nowrap;max-width:280px;overflow:hidden;text-overflow:ellipsis;
}
.pg-bpmn-pagetab.on{background:#000;color:#fff;font-weight:600;}
/* The main process is the document, not a sheet beside the others. */
.pg-bpmn-pagetab.main{font-weight:600;}

/* ── #1675 — THE STEP THE SELECTED FINDING IS ON ────────────────────────────
   Craig 2026-09-20, on several steps carrying findings: "you cant tell which
   one. Sa[m]e as the document surface there needs to be some extra
   highlighting applied to the appropriate step".

   THE SAME GESTURE AS THE DOCUMENT SURFACE, NOT THE SAME CSS. `.rc-finding
   .is-active` marks the page with a ring, an inset rail and a one-shot pulse —
   but that is CSS on HTML, and a BPMN step is a JointJS `<g>` in SVG where
   `outline` and `box-shadow` do not apply. `filter: drop-shadow()` DOES apply
   to SVG and follows the shape's real outline rather than its bounding box,
   which is the closer reading of the gesture for a diamond or an event circle.
   So: same ring colour, same pulse timing, same one-at-a-time rule, drawn the
   way this surface can draw it.

   The colour is `--rc-anchor-ring` — the token the document surface and the
   drawer's selected card already read (#322), so "selected" is one decision in
   one place rather than three surfaces agreeing by coincidence.

   The ring and glow are held in their OWN variables so the dark override below
   only restates two colours. Overriding `filter` directly would not work: the
   keyframes set `filter` too, and an animation beats a plain declaration while
   it plays, so a dark-mode rule written that way would be ignored for the first
   900ms — visible exactly when the user is looking. */
.rc-bpmn-finding-focus {
    --rc-bpmn-focus-ring: var(--rc-anchor-ring, #2563eb);
    --rc-bpmn-focus-glow: rgba(37, 99, 235, 0.40);
    filter: drop-shadow(0 0 1px var(--rc-bpmn-focus-ring))
            drop-shadow(0 0 4px var(--rc-bpmn-focus-ring))
            drop-shadow(0 0 10px var(--rc-bpmn-focus-glow));
    animation: rc-bpmn-finding-pulse 900ms ease-out 1;
}
@keyframes rc-bpmn-finding-pulse {
    0%   { filter: drop-shadow(0 0 1px var(--rc-bpmn-focus-ring))
                   drop-shadow(0 0 2px var(--rc-bpmn-focus-ring))
                   drop-shadow(0 0 0 transparent); }
    70%  { filter: drop-shadow(0 0 1px var(--rc-bpmn-focus-ring))
                   drop-shadow(0 0 8px var(--rc-bpmn-focus-ring))
                   drop-shadow(0 0 18px var(--rc-bpmn-focus-glow)); }
    100% { filter: drop-shadow(0 0 1px var(--rc-bpmn-focus-ring))
                   drop-shadow(0 0 4px var(--rc-bpmn-focus-ring))
                   drop-shadow(0 0 10px var(--rc-bpmn-focus-glow)); }
}
/* The canvas follows the app theme (#304 — `--rc-bpmn-paper` flips to #0f141a),
   so the mark has to as well. `--rc-anchor-ring` does NOT flip on its own
   (`--pq-accent-2` is the same blue in both schemes), which is why the document
   surface states its dark ring explicitly too. */
@media (prefers-color-scheme: dark) {
    .rc-bpmn-finding-focus {
        --rc-bpmn-focus-ring: #60a5fa;
        --rc-bpmn-focus-glow: rgba(96, 165, 250, 0.45);
    }
}


/* #1520 — the two-factor schedule on the Organisation tab. The number sits with
   its unit, because "7" and "1" mean nothing apart from "days" and "%". */
.org-mfa-row{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;}
.org-input.org-mfa-num{width:88px;flex:0 0 auto;}
#org-mfa-section input:disabled{opacity:.6;cursor:not-allowed;}


/* The DIAGRAM stays a white page in both themes — it is a printed artifact and
   Craig's M3 gate is explicitly monochrome-on-white. The CHROME around it (tab
   strip, scrollbars) follows the OS theme like the rest of the app. */
@media (prefers-color-scheme: dark) {
    /* #1553 — the document-surface tabs ride the same rules in dark mode too;
       a shared look that diverged in one colour scheme would be worse than two
       honest looks. */
    .doc-surface-tabs { background: var(--surface, #1f2329); border-bottom-color: var(--pq-border); }
    .pg-bpmn-pagetabs{ background: var(--surface, #1f2329); border-top-color:var(--pq-border); }
    .pg-bpmn-pagetab { background: var(--surface, #1f2329); color: var(--text, #e5e7eb);
                       border-color:var(--pq-border); }
    .pg-bpmn-pagetab.on{ background: var(--accent); color: #fff; }   /* #2325: no light block in dark mode */
    .doc-surface-tab  { background: var(--surface, #1f2329); color: var(--text, #e5e7eb);
                    border-color: var(--pq-border); }
    .doc-surface-tab.on { background: var(--accent); color: #fff; }   /* #2325 */
    
}

/* ── S2a intent-to-respond / next-speaker panel (from debate-4voice-sandbox,
   Craig 2026-07-11: visible to ANY user behind the "Next speaker" toggle). ── */
.er-need-panel{position:fixed;top:118px;right:32px;z-index:60;width:274px;background:var(--surface,#fff);
    border:1px solid var(--border,#cbd5e0);border-radius:12px;box-shadow:0 10px 30px rgba(10,20,35,.18);display:none}
.er-need-panel.show{display:block}
.er-need-hd{display:flex;align-items:center;gap:8px;padding:9px 12px;background:#2d3748;color:#eaf1f9;border-radius:12px 12px 0 0;user-select:none}
.er-need-t{font-weight:600;font-size:12px}
.er-need-sp,.er-need-nsp{flex:1}

.er-need-bd{padding:11px 12px;display:flex;flex-direction:column;gap:9px}
.er-need-row{display:flex;flex-direction:column;gap:3px}
.er-need-nl{display:flex;align-items:center;gap:6px;font-size:11.5px;color:var(--text,#2d3748)}
.er-need-nl b{font-variant-numeric:tabular-nums}
.er-need-bar{height:7px;border-radius:5px;background:var(--border,#e2e8f0);overflow:hidden}
.er-need-bar > i{display:block;height:100%;border-radius:5px;transition:width .5s ease;width:0}
.er-need-row[data-voice="internal_auditor"] .er-need-bar>i{background:var(--dv-ia-fg)}
.er-need-row[data-voice="inspector"] .er-need-bar>i{background:var(--dv-insp-fg)}
.er-need-row[data-voice="qe"] .er-need-bar>i{background:var(--dv-qe-fg)}
.er-need-row[data-voice="process_owner"] .er-need-bar>i{background:var(--dv-po-fg)}
.er-need-row.next .er-need-nl{font-weight:700}
.er-need-row.next .er-need-nl::after{content:"· next";color:var(--dv-you-fg,#2f855a);font-weight:700;font-size:10px}
.er-need-ft{padding:7px 12px 10px;font-size:10.5px;color:var(--text-muted,#718096);border-top:1px solid var(--border,#e2e8f0)}
#er-need-toggle.is-active{background:var(--dv-you-bg,#f0fff4);border-color:var(--dv-you-fg,#2f855a);color:var(--dv-you-fg,#2f855a)}
@media (prefers-color-scheme: dark){
    .er-need-panel{background:var(--pq-surface);border-color:var(--pq-border)}
    .er-need-nl{color:#e2e8f0}
    .er-need-bar{background:var(--pq-line)}
}

/* ── 4-voice debate modal layout (S1 bake-in of debate-4voice-sandbox) ── */
.er-4v-body{display:flex;flex-direction:column;overflow:hidden;min-height:0}
.er-finding-bar{padding:10px 16px;border-bottom:1px solid var(--border);background:var(--surface-2,#f7f8fa);flex:0 0 auto}
/* #469 — EQUAL COLUMNS. `1fr` is `minmax(auto, 1fr)`, and a grid item's automatic
   minimum is its MIN-CONTENT size — so the Quality Expert's header, which alone
   carries lens chips and was set `white-space:nowrap`, pushed its own column to
   roughly twice the width of its three peers. `minmax(0, 1fr)` removes the
   content floor, and the four voices are then sized by the grid, not by how much
   furniture each header happens to carry. They are peers in the debate model;
   one looking weightier than the rest before a word is read is the opposite of
   what a four-voice panel is for. */
.er-voices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));grid-template-rows:auto 1fr auto;gap:11px;padding:13px 14px;flex:0 0 auto;background:var(--surface);border-bottom:1px solid var(--border);overflow-y:auto;max-height:40vh}
@media (max-width:980px){ .er-voices{grid-template-columns:repeat(2,minmax(0,1fr))} }
.er-voice{border:1px solid var(--border);border-radius:11px;background:var(--surface);display:flex;flex-direction:column;min-height:170px;max-height:300px}
/* #469 supersedes B-130. B-130 kept every header on ONE line and ellipsized the
   QE's lens chips so the four stayed uniform. Craig 2026-07-29 reverses the
   trade-off: "If another line is needed for each of the participants so that the
   QE person can get the lenses in then so be it, but col width should be
   consistent." So the header WRAPS and shows the lenses in full. */
.er-voice-hd{display:flex;align-items:center;gap:7px;padding:8px 11px;border-bottom:1px solid var(--border);border-radius:11px 11px 0 0;font-weight:600;font-size:0.82rem;flex-wrap:wrap;row-gap:4px;white-space:normal;min-height:37px}
.er-voice-frame{font-weight:500;font-size:0.62rem;opacity:.8;text-transform:uppercase;letter-spacing:.4px;flex:0 0 auto}
#er-qe-active-lens{flex:0 1 auto;min-width:0}
/* …and all four headers take the height of the TALLEST, at render time — not a
   constant, which would leave a permanent empty second line on a document whose
   QE has no lenses. Subgrid makes the four cards share the parent's row tracks,
   so header/body/rung line up across the panel. Guarded: without subgrid the
   cards stay plain flex columns and only the header heights differ. */
@supports (grid-template-rows: subgrid) {
    .er-voice{display:grid;grid-row:span 3;grid-template-rows:subgrid;max-height:none}
    .er-voice-body{min-height:0}
}
.er-voice-hd .er-spinner{margin-left:auto} .er-voice-hd .er-popout-btn{margin-left:6px}
.er-voice-internal_auditor .er-voice-hd{background:var(--dv-ia-bg);color:var(--dv-ia-fg)}
.er-voice-inspector .er-voice-hd{background:var(--dv-insp-bg);color:var(--dv-insp-fg)}
.er-voice-qe .er-voice-hd{background:var(--dv-qe-bg);color:var(--dv-qe-fg)}
.er-voice-process_owner .er-voice-hd{ background: var(--dv-po-bg); color: var(--dv-po-fg)}
@media (prefers-color-scheme: dark) { .er-voice-process_owner .er-voice-hd { color: #e9c46a; background: #2a2310; } }
.er-voice-body{padding:9px 11px;overflow:auto;flex:1;font-size:0.8rem;line-height:1.5}
.er-voice-rung{padding:6px 11px;border-top:1px solid var(--border);font-size:0.66rem;color:var(--text-muted);display:flex;align-items:center;gap:6px}
.er-voice-rung:empty{display:none}
.er-rung{font-size:0.6rem;font-weight:700;text-transform:uppercase;letter-spacing:.4px;padding:2px 7px;border-radius:6px;border:1px solid;white-space:nowrap}
/* Axis-model disposition tints (voice-landing chips): dismiss=grey, monitor=blue, remediate=green. */
.er-disp-dismiss{ color: light-dark(#64748b, #94a3b8); border-color: light-dark(#64748b, #94a3b8)} .er-disp-monitor{ color: light-dark(#1e40af, #63b3ed); border-color: light-dark(#1e40af, #63b3ed)} .er-disp-remediate{ color: light-dark(#276749, #68d391); border-color: light-dark(#276749, #68d391)}
.er-thread-pane{flex:1 1 0;min-height:0;overflow-y:auto;display:flex;flex-direction:column;background:var(--surface-2,#f7f8fa)}
.er-thread-hd{display:flex;align-items:center;gap:10px;padding:10px 16px 4px;font-size:0.9rem}
.er-thread-t{font-weight:600;display:inline-flex;align-items:center;gap:6px}
.er-composer{display:flex;gap:9px;align-items:flex-end;padding:10px 16px 12px;border-top:1px solid var(--border);background:var(--surface);flex:0 0 auto}
.er-composer-field{flex:1;display:flex;flex-direction:column;gap:4px}
.er-composer-lbl{font-size:0.68rem;color:var(--text-muted)}
.er-composer textarea{width:100%;resize:none;border:1px solid var(--border);border-radius:9px;background:var(--surface);color:var(--text);font:inherit;font-size:0.85rem;padding:8px 11px;min-height:38px;max-height:110px}
.er-composer textarea:focus{outline:none;border-color:#2e7d5b}
.er-rung-strip{display:flex;flex-wrap:wrap;gap:7px;margin:6px 0 10px;align-items:center;font-size:0.72rem;color:var(--text-muted)}
.er-rung-strip:empty{display:none}
.er-rung-strip .er-rc{display:inline-flex;align-items:center;gap:5px;background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:2px 8px}
.er-agreed-rung{margin-left:8px}
/* B-192 — "Revert first" guidance shown in place of the dead Reject button on an
   inserted finding. Colour comes from theme vars (no hardcoded palette). */
.er-revert-note{display:inline-flex;align-items:center;gap:6px;font-size:0.78rem;color:var(--text-muted)}
.er-revert-note .ic{width:14px;height:14px;flex:0 0 auto}
/* #277 engine · #289 presentation — objection ledger as flowing PROSE (Craig's
   ruling: no chip/pill furniture; the prose speaks for itself). Theme vars only. */
.er-objection-ledger{margin:6px 0 10px;padding:7px 10px;border:1px solid var(--border);border-left:3px solid var(--primary);border-radius:8px;background:var(--surface);font-size:0.78rem}
.er-objection-ledger:empty{display:none}
.er-obj-ledger-body{margin:3px 0;line-height:1.55}
.er-obj-ledger-prose{margin-top:6px;padding-top:6px;border-top:1px dashed var(--border);color:var(--text-muted);font-size:0.75rem}
/* Axis-display outcome strip (L-093) — chair decision + grouped voice landings.
   Layout only; tag colours reuse the SSOT .sev-* and .er-disp-* classes.
   (Do NOT write those two glob names with a slash between them: "*" + "/"
   closes this comment early and the leaked text voids the next rule — that
   exact typo silently dropped .er-axis-chair/.er-axis-landings's flex layout
   from v4.2.49 until the 2026-07-31 comment sweep found it.) */
.er-axis-chair,.er-axis-landings{display:flex;flex-wrap:wrap;gap:7px;align-items:center;margin:4px 0}
.er-axis-lbl{color:var(--text-muted);font-weight:600}
@media (prefers-color-scheme: dark){
  .er-voice-internal_auditor .er-voice-hd{background:#123330;color:#4fd1c5}
  .er-voice-inspector .er-voice-hd{background:#2a1618;color:#fc9aa0}
  .er-voice-qe .er-voice-hd{background:var(--pq-surface);color:#6fa8dc}
  
    
}
/* Start Discussion button — normal inline button in the thread header (was breaking) */
.er-thread-hd .er-discuss-btn{position:static;width:auto;flex:0 0 auto;white-space:nowrap;display:inline-flex;align-items:center;gap:6px;margin:0;padding:6px 13px;font-size:0.8rem;border-radius:8px}
/* copy-to-clipboard on agreed actions / replacement */
.er-replacement-label,.er-agreed-actions-label{display:flex;align-items:center;gap:6px}
.er-copy-btn{margin-left:auto;display:inline-flex;align-items:center;gap:5px;background:transparent;border:1px solid var(--border);color:var(--text-muted);border-radius:7px;padding:2px 9px;font-size:0.7rem;cursor:pointer;font-weight:600}
.er-copy-btn:hover{border-color:var(--primary);color:var(--primary-ink)}
/* consensus/outcome as a dismissable pop-out — keeps the discussion visible (Craig 2026-07-01) */
.er-outcome-overlay{position:fixed;inset:0;z-index:70;display:flex;align-items:center;justify-content:center;padding:24px}
.er-outcome-overlay.hidden{display:none}
.er-outcome-backdrop{position:absolute;inset:0;background:rgba(8,16,28,.5)}
/* #1668 — "Consensus reached / the chair is compiling the meeting notes".
   Craig 2026-09-20. A SIBLING of .er-outcome-card in the same overlay, sized and
   shaped like it so the swap reads as one surface changing its contents rather
   than two panels appearing. Tokens throughout (no literals): this panel has to
   survive dark mode, and a hand-picked colour here is exactly the drift #1655
   was written to stop. */
.er-compiling-card{width:min(420px,92vw);padding:28px 24px;display:flex;flex-direction:column;align-items:center;gap:10px;text-align:center}
.er-compiling-title{font-weight:600;font-size:1.05rem;color:var(--text)}
.er-compiling-sub{color:var(--text-muted);font-size:.92rem}
/* Elapsed, not a countdown — tabular so the digits do not jitter each second. */
.er-compiling-elapsed{color:var(--text-muted);font-size:.82rem;font-variant-numeric:tabular-nums}
.er-compiling-spinner{width:26px;height:26px;border-radius:50%;border:3px solid var(--border);border-top-color:var(--accent,var(--text-muted));animation:er-compiling-spin .9s linear infinite}
@keyframes er-compiling-spin{to{transform:rotate(360deg)}}
.er-compiling-dots{animation:er-compiling-fade 1.4s ease-in-out infinite}
@keyframes er-compiling-fade{0%,100%{opacity:.25}50%{opacity:1}}
/* The words carry the meaning; the motion is decoration. A reader who has asked
   for less of it still gets a panel that plainly says what is happening. */
@media (prefers-reduced-motion: reduce) {
  /* the still state shown in place of the motion (#1903: the motion itself
     is off for everything, by the one global rule near the top) */
  .er-compiling-dots { opacity:1; }
}
/* #1668 — the placeholder speaks to whoever is looking at it. The popup has no
   QMS library, so telling its user to open one names a control that is not on
   their screen. Hidden by default and swapped on body.popup-mode, so neither
   audience ever sees the other's sentence and no JS has to run to pick. */
.placeholder-popup{display:none}
body.popup-mode .placeholder-standalone{display:none}
body.popup-mode .placeholder-popup{display:block}
.er-outcome-card,.er-compiling-card{position:relative;background:var(--surface);border:1px solid var(--border);border-radius:14px;box-shadow:0 18px 56px rgba(20,40,70,.28)}
.er-outcome-card{position:relative;width:min(680px,94vw);max-height:84vh;display:flex;flex-direction:column;overflow:hidden}
.er-outcome-card-hd{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--border);font-weight:600}
.er-outcome-card-hd span{display:inline-flex;align-items:center;gap:8px}


.er-outcome-card-bd{padding:14px 16px;overflow:auto}

.er-debate-waiting {
    padding: 28px 16px;
    color: var(--text-muted);
    font-size: 0.9rem;
    display: flex;
    align-items: center;
    gap: 10px;
}
.er-actions-bar {
    padding: 7px 16px;
    border-top: 1px solid var(--border);
    display: flex;
    gap: 8px;
    flex-shrink: 0;
    align-items: center;
}
/* Equal-width Report/Regenerate header buttons */
.er-fullscreen-header-actions .btn-sem {
    min-width: 115px;
    text-align: center;
}
/* B-022 — the brevity <select> wears .btn-sem (white-on-blue control),
   which Chrome/Linux inherits into the native option list → white-on-white,
   unreadable. Force readable dark-on-white options via the base palette so the
   dropdown is legible (theme vars, not hardcoded). */
#er-brevity-select option {
    color: var(--text);
    background-color: var(--bg-white);
}
.er-report-btn { margin-left: auto; }
.er-insertion-box {
    padding: 10px 16px;
    border-top: 1px solid var(--border);
    background: light-dark(#f0fff4, var(--pq-elevated));
    flex-shrink: 0;
    max-height: 200px;
    overflow-y: auto;
}

.er-insertion-text {
    font-size: 0.88rem;
    line-height: 1.6;
    color: var(--text);
    white-space: pre-wrap;
    margin-bottom: 8px;
}

.er-spinner {
    color: var(--text-muted);
    font-size: 0.75rem;
    letter-spacing: 3px;
    animation: er-pulse 1.2s ease-in-out infinite;
}
@keyframes er-pulse {
    0%, 100% { opacity: 0.25; }
    50%       { opacity: 1; }
}
.er-loading {
    color: var(--text-muted);
    font-style: italic;
    font-size: 0.88rem;
}
.er-stream-partial {
    font-size: inherit;
    line-height: inherit;
    color: inherit;
}
.er-error {
    color: light-dark(#c53030, color-mix(in oklab, #c53030 45%, white));
    font-size: 0.85rem;
}

/* ── Finding action buttons (Expert / Reject / Accept) ── */
.card-expert-btn {
    margin-left: auto;
    border: none;
    border-radius: 4px;
    background: transparent;
    color: var(--text-muted);
    font-size: 0.78rem;
    cursor: pointer;
    padding: 2px 5px;
    line-height: 1;
    transition: background 0.12s, color 0.12s;
}
.card-expert-btn:hover {
    background: var(--hover-bg);
    color: var(--primary-fg);
}
/* #2085 (DU28) — Reject ✕ on an open finding card. Its grey was the light-only
   #a0aec0, so it now follows the theme like the ⋯ actions button beside it. */
.card-reject-btn {
    margin-left: 2px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.75rem;
    line-height: 1;
    padding: 2px 5px;
    background: transparent;
    color: var(--text-muted);
    transition: background 0.12s, color 0.12s;
}

.card-reject-btn:hover {
    background: light-dark(#fed7d7, color-mix(in oklab, #fed7d7 14%, var(--pq-surface)));
    color: light-dark(#c53030, color-mix(in oklab, #c53030 45%, white));
}

.card-actions-btn {
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.95rem;
    line-height: 1;
    padding: 2px 5px;
    background: transparent;
    color: var(--text-muted);
    transition: background 0.12s, color 0.12s;
}
.card-actions-btn:hover {
    background: var(--hover-bg);
    color: var(--primary-ink, #2b6cb0);
}

.finding-actions-popup {
    position: absolute;
    z-index: 900;
    background: var(--surface, #fff);
    border: 1px solid var(--border, var(--border));
    border-radius: 8px;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);
    width: 300px;
    max-width: calc(100vw - 24px);
    font-size: 0.83rem;
}
.finding-actions-popup-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 12px;
    border-bottom: 1px solid var(--border, var(--border));
    font-weight: 700;
    font-size: 0.78rem;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.finding-actions-popup-body {
    padding: 10px 14px;
    white-space: pre-wrap;
    line-height: 1.6;
    color: var(--text);
    max-height: 220px;
    overflow-y: auto;
}

.card-suggestion-actions {
    display: flex;
    gap: 6px;
    margin-top: 6px;
}

/* B-199 — a Dismiss/Monitor panel decision has NOTHING insertable: the Accept
   button is not rendered at all and this note takes its place. Colours come from
   the theme vars, so it follows light/dark like every other card affordance. */
.suggestion-noaction-note {
    margin-top: 6px;
    font-size: 0.74rem;
    color: var(--text-muted);
    background: var(--bg);
    border: 1px dashed var(--border);
    border-radius: 4px;
    padding: 5px 8px;
    line-height: 1.4;
}

.card-accept-btn {
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    padding: 3px 8px;
    transition: background 0.12s;
    background: light-dark(#c6f6d5, color-mix(in oklab, #c6f6d5 14%, var(--pq-surface)));
    color: light-dark(#276749, color-mix(in oklab, #276749 45%, white));
}

/* #2093 — the hover takes the theme: in dark the light-only #9ae6b4 put the light
   green label on pale green (1.45). Dark hovers to a deep green: visibly greener
   than the resting ground, and the label reads 4.73 (light stays 4.6). A
   HAND-WRITTEN twin (a literal dark half), so the #1655 sweep leaves it alone -
   its role table gives #9ae6b4 no dark twin, which the 1.45 disproves. */
.card-accept-btn:hover {
    background: light-dark(#9ae6b4, #1f4a36);
}

.card-revert-btn {
    display: block;
    margin-top: 6px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    padding: 3px 8px;
    background: light-dark(#fefcbf, color-mix(in oklab, #fefcbf 14%, var(--pq-surface)));
    color: light-dark(#744210, color-mix(in oklab, #744210 45%, white));
    transition: background 0.12s;
}

.card-revert-btn:hover {
    background: #faf089;
}

/* ── Unnecessary Detail icon buttons (in card-top row) ── */
.card-ud-edit-btn,
.card-ud-remove-btn {
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.85rem;
    line-height: 1;
    padding: 2px 5px;
    background: transparent;
    transition: background 0.12s, color 0.12s;
}

.card-ud-edit-btn {
    color: light-dark(#2b6cb0, var(--pq-accent));
}
.card-ud-edit-btn:hover { background: light-dark(#bee3f8, color-mix(in oklab, #bee3f8 14%, var(--pq-surface))); }

/* T-183 — Remove is a LABELLED soft-outline button (destructive tint), not a
   bare trash icon: the primary resolution for an unnecessary_detail finding is
   REMOVE, so it reads as a word, per the icon+label house standard. */
.card-ud-remove-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    border: 1px solid #e0a3a3; color: light-dark(#9b2c2c, #f8b4b4);
    font-size: 0.78rem;
    font-weight: 600;
    padding: 2px 8px;
}
.card-ud-remove-btn:hover { background: light-dark(#fed7d7, #5a2323); border-color: light-dark(#9b2c2c, #f8b4b4); }
@media (prefers-color-scheme: dark) {
    .card-ud-remove-btn { border-color: #7a2e2e; }
    
}

/* ── Edit suggestion modal ── */
.edit-suggestion-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.45);
    z-index: calc(var(--z-dialog) + 10);
    display: flex;
    align-items: center;
    justify-content: center;
}

.edit-suggestion-modal {
    background: var(--surface, #fff);
    border-radius: 8px;
    box-shadow: 0 8px 32px rgba(0,0,0,0.22);
    width: min(520px, 92vw);
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.edit-suggestion-modal h3 {
    margin: 0;
    font-size: 0.95rem;
    font-weight: 600;
}

.edit-suggestion-modal textarea {
    width: 100%;
    min-height: 120px;
    border: 1px solid var(--border, var(--border));
    border-radius: 6px;
    padding: 8px;
    font-size: 0.85rem;
    font-family: inherit;
    resize: vertical;
    box-sizing: border-box;
}

.edit-suggestion-modal-actions {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
}

.edit-suggestion-modal-actions button {
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.82rem;
    font-weight: 600;
    padding: 5px 14px;
    transition: background 0.12s;
}

/* Change-capture reason form (v3.14.83) — required reason per change at Save.
   Theme-aware (var-based) so it adapts to light/dark. */
.rc-reason-overlay {
    position: fixed; inset: 0; background: rgba(0,0,0,0.45);
    z-index: calc(var(--z-dialog) + 20); display: flex; align-items: center; justify-content: center;
}
.rc-reason-modal {
    background: var(--surface, #fff); border-radius: 8px;
    box-shadow: 0 8px 32px rgba(0,0,0,0.22);
    width: min(640px, 94vw); max-height: 88vh; padding: 20px;
    display: flex; flex-direction: column; gap: 10px;
}
.rc-reason-modal h3 { margin: 0; font-size: 0.98rem; font-weight: 600; }
.rc-reason-sub { margin: 0; font-size: 0.8rem; color: var(--text-muted); }
.rc-reason-list { overflow-y: auto; display: flex; flex-direction: column; gap: 12px; padding-right: 4px; }
.rc-reason-row {
    border: 1px solid var(--border, var(--border)); border-radius: 6px;
    padding: 10px; display: flex; flex-direction: column; gap: 6px;
}
.rc-reason-kind { font-size: 0.8rem; font-weight: 600; }
.rc-reason-blk { color: var(--text-muted); font-weight: 400; font-size: 0.72rem; }
.rc-reason-diff { font-size: 0.76rem; padding: 4px 6px; border-radius: 4px; white-space: pre-wrap; }
.rc-reason-before { background: var(--surface-2); color: var(--text-muted); }
.rc-reason-after  { background: var(--surface-2); color: var(--text-secondary); }
/* The actual changed words (theme-aware): removed = red strike, added = green. */
.rc-diff-del { background: rgba(229,62,62,0.18); color: var(--danger); text-decoration: line-through; border-radius: 2px; padding: 0 1px; }
.rc-diff-ins { background: rgba(56,161,105,0.20); color: var(--text); font-weight: 600; border-radius: 2px; padding: 0 1px; }
.rc-reason-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.rc-reason-chip {
    font-size: 0.7rem; padding: 2px 8px; border-radius: 10px;
    border: 1px solid var(--border); background: transparent;
    color: var(--text-secondary); cursor: pointer;
}
.rc-reason-chip.active { background: var(--primary); color: #fff; border-color: var(--primary-fg); }
.rc-reason-input {
    width: 100%; border: 1px solid var(--border, var(--border)); border-radius: 6px;
    padding: 6px; font-size: 0.82rem; font-family: inherit; resize: vertical; box-sizing: border-box;
}
.rc-reason-actions { display: flex; gap: 8px; justify-content: flex-end; }
.rc-reason-actions button {
    border: none; border-radius: 4px; cursor: pointer;
    font-size: 0.82rem; font-weight: 600; padding: 6px 14px;
}
.rc-reason-cancel { background: var(--surface-2); color: var(--text-secondary); }
.rc-reason-save { background: var(--primary); color: #fff; }
.rc-reason-save:disabled { opacity: 0.5; cursor: not-allowed; }

.edit-modal-insert-btn {
    background: light-dark(#c6f6d5, color-mix(in oklab, #c6f6d5 14%, var(--pq-surface)));
    color: light-dark(#276749, color-mix(in oklab, #276749 45%, white));
}

.edit-modal-insert-btn:hover {
    background: #9ae6b4;
}

.edit-modal-cancel-btn {
    background: light-dark(#e2e8f0, var(--pq-line));
    color: light-dark(#4a5568, var(--pq-ink-muted));
}

.edit-modal-cancel-btn:hover {
    background: light-dark(#cbd5e0, color-mix(in oklab, #cbd5e0 14%, var(--pq-surface)));
}

/* #325 — `.finding-card.finding-inserted` WAS HERE, dashed, and was never
   emitted by anything: dead since the card stopped carrying a private inserted
   class. It is deleted rather than re-pointed, because the grammar it used is
   now wrong.

   THE GRAMMAR, stated once so both surfaces can follow it:
       SOLID edge  = the change is IN the document (inserted).
       DASHED edge = settled, but the document is unchanged (agreed / rejected /
                     acted).
   That is the distinction #325 asks for — an agreed-but-not-yet-applied finding
   must not look like one whose text is already in. Inserted therefore keeps the
   ordinary solid card border and carries its meaning in the tint and the badge. */

/* #611 sibling sweep — the SAME defect, and a second one underneath it.

   Same shape: background set, ink left to whatever the category class put
   there, so the contrast depended on which category the finding happened to be.

   And the colour was wrong on its own terms. #282 moved Inserted into the GREEN
   family — a resolution-side state, one step lighter than Resolved — and left a
   comment beside `.stat-pill.inserted` saying it "must never share the
   structural violet family or the AI purple it wrongly wore before".
   #805ad5 IS that AI purple: the pill moved and the card badge did not, so one
   state wore two colours in two homes. Now both are green.50/600, which is the
   one-colour-in-every-home rule the same comment states. */
/* #580 — the class is now the STATE TOKEN itself (`inserted`), because the card
   emits one badge carrying whatever `_findingBoxCat` returned. It was
   `.inserted-badge`, a name that existed only because the badge block was
   additive and needed a second class to sit beside the category one. */
.cat-badge.inserted { background: light-dark(#f0fff4, #16301f); color: light-dark(#2f855a, #86efac);
}
/* #708 — the two dispositions that had a card colour and no badge of their own,
   because they were never a badge: the card wore its CATEGORY badge and changed
   its own background. One badge means they each need one now, in the colour
   they already wear everywhere else (B-215 — one colour in every home). */
.cat-badge.rejected {
    background: var(--rc-find-rejected-fill);
    color: var(--text-muted);
}
.cat-badge.acted {
    background: var(--rc-find-acted-fill);
    color: var(--rc-find-acted-edge);
}

/* Dark twins for both — same hue, lightness inverted so the ink stays legible.
   `prefers-color-scheme` is the one signal: the theme follows the computer
   (#1809; the in-app override was deleted by #1852). */
@media (prefers-color-scheme: dark) {
    .cat-badge.resolved { background: #1c3b2a; color: #9ae6b4; }
    
}

/* Finding identifier — sits on the card-top row before the category badge.
   Monospaced for compactness and so F001..F999 align nicely across cards.
   Stable per-analysis identifier; appears in audit logs and Word/Excel
   reports so reviewers can refer to a specific finding by ID. */
/* L-046 — compact, extensible per-finding metadata affordance (the model used
   + future run params). Small round "?" at the card top; hover reveals detail.
   Shared idiom across the doc analysis card + the graph finding popover. */
.finding-meta-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    padding: 0;
    font-size: 0.68rem;
    font-weight: 700;
    line-height: 1;
    color: light-dark(#5b6573, var(--pq-ink-muted));
    background: light-dark(#eef2f7, var(--pq-elevated));
    border: 1px solid light-dark(#cbd5e1, color-mix(in oklab, #cbd5e1 30%, var(--pq-surface)));
    border-radius: 50%;
    cursor: help;
    vertical-align: baseline;
    margin-right: 4px;
}
.finding-meta-btn:hover { background: light-dark(#e0e7ff, color-mix(in oklab, #e0e7ff 14%, var(--pq-surface))); border-color: #6366f1; color: light-dark(#4338ca, var(--pq-accent)); }
.finding-meta-btn .ic { width: 11px; height: 11px; stroke-width: 2.5; }   /* info glyph sized for the 16px badge (replaces the non-working "?") */

.finding-id-badge {
    display: inline-block;
    padding: 2px 6px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--text); background: light-dark(#edf2f7, var(--pq-elevated));
    border: 1px solid var(--border);
    border-radius: 3px;
    margin-right: 4px;
    vertical-align: baseline;
}
/* Dark-mode: F-id badge had light text (var(--text)) on a hardcoded light bg
   (#edf2f7) → invisible (Craig D4: "first tag can't be read"). Dark its bg. */
/* #611 — RESOLVED WAS BLACK TEXT ON DARK GREEN. Craig, 2026-08-04: "black on
   dark green is definitely not going to work".

   THE MECHANISM, because it is a class not a one-off: the badge carries BOTH
   the category class and this one —
       <span class="cat-badge incomplete resolved-badge">
   (the markup #580 replaced: the badge is ONE class now, the state token, so
   this particular collision cannot recur — but the LESSON below is general and
   is why the rule still states its own ink)
   — and the category rule is what supplied the INK (`.cat-badge.incomplete`
   sets `color: var(--gap-badge-ink, #1a202c)`, dark, because the gap bar is a
   pale orange). This rule then replaced only the BACKGROUND with a saturated
   dark green and left that dark ink in place. A rule that changes a fill must
   change its ink in the same breath, or it inherits whatever the last one set.

   The colour chosen is not new: it is exactly `.stat-pill.resolved`
   (#c6f6d5 / #276749). Craig's grammar rule (B-215) is ONE colour in every
   home — chip, pill, card badge — so RESOLVED now reads the same wherever it
   appears, and it matches the pale-fill/dark-ink siblings on the same card
   (MATERIAL, OAI, GAP) that Craig noted were all legible.

   Both themes: the HUE carries the meaning and never changes; LIGHTNESS adapts
   so the chip stays readable (documentation/design/ui/theme_colour_rules.md §7). */
.cat-badge.resolved {
    background: #c6f6d5;
    color: #276749;
}

/* v3.9.0 Phase 5 — comment-only finding badge.
   Indicates the finding is editorial / advisory; Accept logs as
   "agreed" and surfaces as a Word comment on export, but does NOT
   mutate the SOP bytes. Distinct from category badges so the user
   sees at a glance that this card behaves differently. */
.cat-badge.action-comment-only {
    background: #4a5568;
    color: #fff;
}
/* #1698 — a person's finding names its source. Tokens, so it follows the scheme. */
.cat-badge.analyst-source-badge {
    background: color-mix(in oklab, var(--pq-accent-2) 16%, var(--pq-surface));
    color: var(--primary-fg);
    border: 1px solid color-mix(in oklab, var(--primary-fg) 35%, transparent);
}

/* v3.9.0 Phase 5 — insert action badges. Visual cue that Accept
   inserts new content adjacent to (rather than replacing) the
   highlighted text. */
.cat-badge.action-insert-after,
.cat-badge.action-insert-before {
    background: #2b6cb0;
    color: #fff;
}

/* v3.9.0 Phase 5 — conflict relationship badge.
   Surfaces the persisted conflicts_with field on the card so
   reviewers see at a glance that accepting this finding will
   overwrite content currently flagged as one or more other
   findings. Hover-title lists the affected finding IDs. */
.cat-badge.conflict-relationship {
    background: var(--conflict-badge);
    color: var(--conflict-text);
    border: 1px solid var(--conflict-bar);
}

/* #325 / #708 / #580 — THE CARD READS THE SAME VALUE THE ANCHOR DOES.
   These were `.finding-card.finding-resolved` / `-rejected` / `-acted`, three
   classes the CARD emitted privately and the anchor knew nothing about. That
   asymmetry was the defect: the card could move on a disposition and the box
   round the text could not. The card now carries whatever `_findingBoxCat`
   returns — the same token the anchor uses — so they cannot disagree.

   THE `!important` IS GONE with them, and it was doing real damage: the resolved
   rule hardcoded `background: #f0fff4 !important` with NO dark counterpart, and
   the `!important` blocked any downstream correction — recorded as D6 in
   frontend/dark-mode-sandbox.html. Both themes are now carried by the same
   `--rc-find-*` tokens the anchor reads, so there is one place to change and it
   changes both surfaces. */
.finding-card.resolved {
    background: var(--rc-find-resolved-fill);
    border-color: var(--rc-find-resolved-edge);
    border-style: dashed;
    opacity: 0.92;
}
/* v4.1.7 unified disposition taxonomy — rejected finding: signed no-action.
   Muted so it reads as handled-but-not-changed, distinct from the green
   accepted state. (Replaces the T-134 Slice B waived styling.) */
.finding-card.rejected {
    background: var(--rc-find-rejected-fill);
    border-color: var(--rc-find-rejected-edge);
    border-style: dashed;
    opacity: 0.85;
}
/* Acted finding — the author's own fix; reads as handled (soft green tint). */
.finding-card.acted {
    background: var(--rc-find-acted-fill);
    border-color: var(--rc-find-acted-edge);
    border-style: dashed;
    opacity: 0.92;
}
/* #2085 — a rejected finding and a dismissed debate wear the one grey badge. */
.rejected-badge,
.debate-badge-dismissed {
    display: inline-block;
    font-size: 0.66rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    padding: 1px 7px;
    border-radius: 10px;
    color: var(--text-muted, #64748b);
    background: var(--border, #e2e8f0);
    cursor: help;
}
.acted-badge {
    display: inline-block;
    font-size: 0.66rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    padding: 1px 7px;
    border-radius: 10px;
    color: light-dark(#22543d, color-mix(in oklab, #22543d 45%, white));
    background: light-dark(#c6f6d5, color-mix(in oklab, #c6f6d5 14%, var(--pq-surface)));
    cursor: help;
}
.card-reopen-btn {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 0.95rem;
    color: var(--text-muted, #64748b);
    padding: 0 4px;
}
.card-reopen-btn:hover { color: var(--primary-ink, #2a5a8c); }
/* Reject signing modal — reason chips (6 agreed + Other). */
.reject-reason-chip {
    border: 1px solid var(--border, var(--border));
    border-radius: 14px;
    background: var(--bg, #f7f8fa);
    color: var(--text, #334);
    font: inherit;
    font-size: 0.78rem;
    padding: 4px 10px;
    cursor: pointer;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.reject-reason-chip:hover { border-color: var(--primary, #2a5a8c); }
.reject-reason-chip.active {
    background: var(--primary, #2a5a8c);
    border-color: var(--primary, #2a5a8c);
    color: #fff;
}
/* Debate outcome — DISMISSED (QE wins the no-action argument; pairs with the
   pre-filled Reject). Mirrors the impasse panel chrome in a muted grey. */
.er-dismissed-panel { border-left: 3px solid #a0aec0; }
.er-outcome-label-dismissed { color: var(--text-muted, #64748b); font-weight: 700; }
/* .debate-badge-dismissed is drawn by .rejected-badge's rule (#2085). */

.ref-doc-note {
    margin-top: 6px;
    font-size: 0.74rem;
    color: var(--gap-text);
    background: var(--gap-badge);
    border-radius: 4px;
    padding: 5px 8px;
    line-height: 1.4;
}

/* Utility */
.hidden { display: none !important; }

/* ── Diagnostic / warning banners ── */
.diag-banner {
    background: light-dark(#fff5f5, var(--pq-elevated));
    border-bottom: 2px solid var(--conflict-bar);
    padding: 10px 40px 10px 16px;
    position: relative;
    flex-shrink: 0;
}

.warn-banner {
    background: var(--gap-bg);
    border-bottom: 2px solid var(--gap-bar);
    padding: 10px 40px 10px 16px;
    position: relative;
    flex-shrink: 0;
}
/* B-267 — benign/informational notice (e.g. a Visio file converted cleanly).
   Neutral green "success" treatment instead of the red warning styling. Uses
   the shared --inserted-* tokens so it stays theme-aware in light and dark. */
.warn-banner.warn-banner-info {
    background: var(--inserted-hi);
    border-bottom-color: var(--inserted-bar);
}

.diag-inner,
.warn-inner {
    font-size: 0.82rem;
    line-height: 1.5;
}


.diag-check {
    display: flex;
    align-items: flex-start;
    gap: 7px;
    padding: 3px 0;
}

.diag-icon { flex-shrink: 0; font-size: 0.85rem; }

.diag-text strong { font-size: 0.82rem; }
.diag-text span   { font-size: 0.78rem; color: var(--text-muted); display: block; }
.diag-hint        { font-size: 0.76rem; color: var(--conflict-text); margin-top: 1px; }

/* ── Detailed error panel ── */
.error-panel {
    background: var(--bg-white);
    border: 1px solid light-dark(#fca5a5, color-mix(in oklab, #fca5a5 30%, var(--pq-surface)));
    border-left: 4px solid var(--conflict-bar);
    border-radius: 8px;
    padding: 20px 22px;
    max-width: 520px;
    margin: 0 auto;
    text-align: left;
}

/* T-092 (v3.12.16) — banner variant: shown inside the results screen as a
   sticky strip at the top so the loaded SOP stays visible underneath while
   the user reads the error message. Continue button just dismisses the
   banner; the doc never had to disappear. */
.error-panel-banner {
    position: sticky;
    top: 0;
    z-index: 50;
    max-width: 100%;
    margin: 0 0 12px;
    border-radius: 0 0 8px 8px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}

.error-panel-title {
    font-size: 1rem;
    font-weight: 700;
    color: var(--conflict-text);
    margin-bottom: 8px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.error-panel-msg {
    font-size: 0.86rem;
    color: var(--text);
    margin-bottom: 10px;
    line-height: 1.5;
}

.error-panel-hint {
    font-size: 0.82rem;
    background: light-dark(#fff5f5, var(--pq-elevated));
    border-radius: 5px;
    padding: 8px 10px;
    color: var(--conflict-text);
    line-height: 1.5;
}

.error-panel-hint::before {
    content: "Fix: ";
    font-weight: 600;
}

.error-panel-retry {
    margin-top: 14px;
    padding: 7px 16px;
    background: var(--primary);
    color: #fff;
    border: none;
    border-radius: 5px;
    font-size: 0.82rem;
    cursor: pointer;
}

.error-panel-retry:hover { background: var(--primary-light); }

/* ── eCFR hierarchy tree ── */
.ecfr-toggle-row {
    display: flex;
    align-items: flex-start;
    gap: 5px;
    padding: 3px 0;
    line-height: 1.3;
}
.ecfr-toggle-row input[type="checkbox"] {
    flex-shrink: 0;
    margin-top: 3px;
    cursor: pointer;
}
.ecfr-node-text {
    flex: 1;
    font-size: 0.78rem;
    color: var(--text);
    cursor: pointer;
}
.ecfr-node-text:hover { color: var(--primary-fg); }
.ecfr-part-row .ecfr-node-text { font-weight: 500; }
.ecfr-toggle-row .group-arrow   { flex-shrink: 0; cursor: pointer; font-size: 0.7rem; margin-top: 3px; }
.ecfr-node-wrap[data-depth="0"] > .ecfr-toggle-row { padding-top: 4px; }
.ecfr-node-wrap[data-depth="1"] { padding-left: 8px; }
.ecfr-node-wrap[data-depth="2"] { padding-left: 6px; }
.ecfr-node-wrap[data-depth="3"] { padding-left: 4px; }
.ecfr-subpart-row {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    padding: 3px 0 3px 6px;
    font-size: 0.76rem;
    cursor: pointer;
}
.ecfr-subpart-row input[type="checkbox"] { flex-shrink: 0; margin-top: 2px; }
.ecfr-subpart-row .part-num  { font-size: 0.76rem; font-weight: 500; }
.ecfr-subpart-row .part-desc { font-size: 0.72rem; color: var(--text-muted, #666); }


/* ── Copyright footer ── */
.app-copyright {
    position: fixed;
    bottom: 4px;
    right: 10px;
    font-size: 0.62rem;
    color: var(--text-muted);   /* #1880 — full strength; opacity 0.45 measured 1.85 light, 2.4 dark */
    pointer-events: none;
    user-select: none;
    z-index: 1;
}
.app-wrap::before {
    content: '';
    position: fixed;
    top: 0;
    left: 0;                           /* B-106: sidebar removed (floating palette now) — centre in the full content viewport */
    right: 0;
    bottom: 0;
    /* Watermark image is set by JS from the active domain's configured logo
       (see _watermark-url assignment in app.js after loadDomainConfig). The
       fallback is `none` so unconfigured domains render without a watermark
       — no other branding leaks through before JS runs. */
    background-image: var(--watermark-url, none);
    background-position: center;
    background-size: contain;
    background-repeat: no-repeat;
    opacity: 0.055;
    pointer-events: none;
    z-index: 0;
    transition: opacity 0.3s ease;
}
.app-wrap.has-document::before {
    opacity: 0;
}
/* Centered brand logo below the login modal (Craig 2026-06-24) */
#login-modal { flex-direction: column; }
.login-brand-logo { width: 380px; max-width: 78vw; margin-top: 52px; opacity: 0.95; pointer-events: none; }

/* ── Build stamp (header) ── */
.build-stamp {
    font-size: 0.72rem;
    font-family: 'SFMono-Regular', Consolas, monospace;
    color: var(--chrome-ink);   /* #1764; full ink, #1880 */
    white-space: nowrap;
    margin-right: 4px;
}

.org-context-wrap {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 0.78rem;
    color: var(--chrome-ink);   /* #1764; full ink, #1880 */
    margin-right: 8px;
    white-space: nowrap;
}
.org-context-wrap select {
    font-size: 0.78rem;
    padding: 2px 6px;
    border-radius: 4px;
    border: 1px solid color-mix(in srgb, var(--chrome-ink) 30%, transparent);
    background: var(--chrome-tint);
    color: var(--chrome-ink);
    cursor: pointer;
    max-width: 180px;
}

/* ── Error state (old simple version, kept for fallback) ── */
.error-box {
    background: var(--conflict-bg);
    border: 1px solid light-dark(#fca5a5, color-mix(in oklab, #fca5a5 30%, var(--pq-surface)));
    border-radius: 8px;
    padding: 16px 20px;
    color: var(--conflict-text);
    text-align: center;
    max-width: 480px;
    margin: 0 auto;
}

.error-box p + p {
    margin-top: 6px;
    font-size: 0.83rem;
    color: var(--text-muted);
}

/* ── Sub-group label (second-level collapsible inside a chapter) ── */
.sub-group-label {
    padding-left: 14px;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--primary-light);
    background: none;
    border-top: none;
}
.sub-group-label:hover { background: rgba(42,90,140,0.07); }

/* ── Agency section titles ── */
.agency-title {
    display: flex;
    align-items: baseline;
    gap: 5px;
}
.agency-subtitle {
    font-size: 0.72rem;
    font-weight: 400;
    color: var(--text-muted);
}

/* ── Agency placeholder (empty coming-soon state) ── */
.agency-placeholder {
    padding: 7px 10px 7px 14px;
    font-size: 0.74rem;
    color: var(--text-muted);
    font-style: italic;
}

/* ── #1676 Help: the header control and the panel ── */
/* The Help button takes the right-hand auto margin. */
.help-btn {
    margin-left: auto;
    display: inline-flex; align-items: center; gap: 6px;
    height: 32px; padding: 0 10px; flex-shrink: 0;
    background: none; border: 1px solid color-mix(in srgb, var(--chrome-ink) 30%, transparent); border-radius: 6px;
    color: var(--chrome-ink); font-size: 0.85rem; cursor: pointer;
    transition: background 0.15s, color 0.15s;   /* #1764 — the chrome font colour, as the banner */
}
.help-btn:hover, .help-btn:focus-visible { background: var(--chrome-tint); color: var(--chrome-ink); outline: none; text-decoration: underline; }
.help-btn .ic { width: 16px; height: 16px; }

.rc-help-panel {
    position: fixed; top: 64px; right: 16px; bottom: 16px; z-index: 11000;
    width: min(760px, calc(100vw - 32px));
    display: flex; flex-direction: column;
    background: var(--surface); color: var(--text);
    border: 1px solid var(--border); border-radius: 10px;
    box-shadow: 0 12px 32px rgba(0,0,0,0.18);
}
.rc-help-panel.hidden { display: none; }
.rc-help-head { display: flex; align-items: center; justify-content: space-between;
                padding: 12px 16px; border-bottom: 1px solid var(--border); }
.rc-help-head h2 { margin: 0; font-size: 1.05rem; display: flex; align-items: center; gap: 8px; }


.rc-help-main { display: flex; min-height: 0; flex: 1; }
.rc-help-index { flex: none; width: 200px; padding: 10px 8px; border-right: 1px solid var(--border);
                 overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }
.rc-help-topic { text-align: left; background: none; border: 1px solid transparent; border-radius: 6px;
                 padding: 6px 8px; color: var(--text); cursor: pointer; font-size: 0.84rem; }
.rc-help-topic:hover, .rc-help-topic:focus-visible { border-color: var(--border); outline: none; }
.rc-help-topic.is-active { background: var(--primary-pale); font-weight: 600; }
.rc-help-body { flex: 1; overflow-y: auto; padding: 14px 20px 24px; font-size: 0.88rem; line-height: 1.55;
                max-width: 72ch; }
.rc-help-body h3 { font-size: 1.05rem; margin: 0 0 8px; }
.rc-help-body h4 { font-size: 0.92rem; margin: 18px 0 6px; }
.rc-help-body .help-summary { font-size: 0.92rem; color: var(--text); background: var(--primary-pale);
                              border-radius: 6px; padding: 8px 12px; margin: 0 0 12px; }

.rc-help-body kbd, .rc-help-body .ctl { font-family: inherit; font-weight: 600; white-space: nowrap; }
.rc-help-lead { color: var(--text-muted); }
/* #1815 — an annotated screenshot in a guide (rc_help.js _renderShots). The frame
   is the positioning box; each numbered mark outlines the control its callout
   names, at the box measured when the picture was taken. */
.rc-help-body .help-shot { margin: 12px 0 16px; }
.rc-help-body .help-shot-frame { position: relative; border: 1px solid var(--border); border-radius: 6px;
                                 overflow: hidden; background: var(--bg-subtle, var(--pq-surface)); }
.rc-help-body .help-shot-img { display: block; width: 100%; height: auto; }
.rc-help-body .help-shot-mark { position: absolute; box-sizing: border-box; border: 2px solid var(--primary-fg);
                                border-radius: 6px; pointer-events: none; }
.rc-help-body .help-shot-badge { position: absolute; left: -10px; top: -10px; min-width: 20px; height: 20px;
                                 border-radius: 10px; background: var(--primary-fg); color: light-dark(#fff, #111721);   /* dark ink on dark's light-blue --primary-fg */
                                 font-size: 12px; font-weight: 700; line-height: 20px; text-align: center; }
.rc-help-body .is-missing .help-shot-frame { padding: 10px 12px; font-size: 0.85rem; color: var(--text-muted);
                                             border-style: dashed; }
.rc-help-body .help-shot figcaption { font-size: 0.8rem; color: var(--text-muted); margin: 6px 0 4px; }
.rc-help-body .help-callouts { margin: 4px 0 0; padding-left: 22px; font-size: 0.88rem; }
@media (prefers-contrast: more) {
    .rc-help-body .help-shot-mark { border-width: 3px; border-color: var(--text); }
}
@media (max-width: 640px) {
    .rc-help-main { flex-direction: column; }
    .rc-help-index { width: auto; flex-direction: row; flex-wrap: wrap; border-right: none;
                     border-bottom: 1px solid var(--border); }
}


/* ── Modal overlay ── */
/* z-index 200 -> 11500 on 2026-08-16, and this is an ARCHITECTURE fix arriving
 * as patch three.
 *
 * A .modal-overlay is the app's BLOCKING dialog. It asks a question and nothing
 * proceeds until it is answered — so by definition it must be above everything
 * it could be asked over. It was at 200, below thirty other layers, and the
 * consequence has now been rediscovered three times:
 *
 *   #admin-modal      pinned to 10300 (N1, 2026-06-26) — the graph header
 *                     occluded its draggable title bar
 *   #audit-detail-modal pinned to 10400 — it rendered behind the admin panel
 *                     it was opened from
 *   Craig 2026-08-16  the OVERWRITE confirm painted behind the import progress
 *                     overlay (.rc-prog-overlay, 11000): *"the dialog box for
 *                     that was behind the loading progress indicator ... if
 *                     there are any buttons on it they are not reachable"*
 *
 * The third is the worst of the three, because the dialog is MODAL: the app is
 * waiting on an answer to a question the user cannot see or reach. Craig only
 * knew what it said because he remembered it from a previous time.
 *
 * Two patches in one area is the signal to ask whether the architecture is the
 * bug (D6). It is. Each pin fixed one modal and left the default wrong for
 * every other, so the next control added over a busy surface finds it again.
 * Raising the BASE is what removes the class, and it lets the two pins go — a
 * tidy that deletes a moving part rather than adding one.
 *
 * WHY 11500 AND NOT HIGHER. `.rc-tell` sits at 12000 deliberately — it is the
 * refusal/failure channel and #303 exists because it used to paint behind
 * modals; a test asserts nothing in this file outranks it. So the ceiling is
 * real and this goes just under it, above the progress family (11000-11060). */
.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.45);
    z-index: var(--z-modal);
    display: flex;
    align-items: center;
    justify-content: center;
}
.modal-overlay.hidden { display: none; }

/* ── ParaQualis "Sunset" brand: login screen background ──────────────────────
 * Per ~/.claude/brand_colors.md (2026-05-19). Renders the brand's
 * sky-at-sunset narrative literally: deep blue at top → warm orange/coral
 * at the horizon. Only applies to the login modal's overlay — the
 * generic .modal-overlay keeps its dim rgba(0,0,0,0.45) so confirm /
 * other in-app modals stay dark-dimmed against the app behind them.
 */
#login-modal.modal-overlay {
    background: linear-gradient(180deg,
        #276BB2 0%,
        #3274C2 12%,
        #4C8ED8 26%,
        #5294DC 40%,
        #89C0F5 55%,
        #B6DBF6 68%,
        #FECF81 82%,
        #FFBE69 92%,
        #FF8053 100%);
}

/* ── Modal box ── */
.modal-box {
    background: var(--bg-white);
    border-radius: 10px;
    box-shadow: 0 8px 32px rgba(0,0,0,0.28);
    width: min(900px, 92vw);
    max-height: 82vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.modal-has-logo {
    flex-direction: column;
}
.modal-split-body {
    display: flex;
    flex-direction: row;
    flex: 1;
    min-height: 0;
    overflow: hidden;
}
/* #1773 — the dialog logo column is RETIRED: Craig's UI ruling of
   2026-09-28 turned it off, and its markup went with it. */
.modal-body-inner {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    scrollbar-gutter: stable;
}


.modal-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 18px 12px;
    border-bottom: 1px solid var(--border);
    background: var(--primary);
    color: #fff;
    flex-shrink: 0;
}
.modal-title { flex: 1; }
.modal-header h2 {
    font-size: 1rem;
    font-weight: 600;
    margin: 0;
    flex: 1;
}



/* ── Modal body ── */
/* #1295 — THE GUTTER IS RESERVED IN BOTH STATES, so typing cannot resize the
   panel. Craig, setting a user's password: "the screen jumps around each time I
   put a character in ... toggling between scroll bar showing and not showing".

   A classic layout oscillation: content crosses the height threshold, the
   scrollbar appears, its width narrows the content box, the reflow removes the
   need for it, and it goes again — once per keystroke, because `input` drives
   it. Reserving the gutter removes it AT SOURCE rather than damping it.

   It is a CREDENTIAL field, which is why this is not cosmetic: the value is
   masked, so the field's position is most of the feedback the user has, and it
   was moving under them. It also violates the standing motion rule — unrequested
   reflow on every keystroke is motion nobody asked for.

   Same fix and same reasoning as `.billing-frozen-body` above. */
.modal-body {
    flex: 1;
    overflow: auto;
    scrollbar-gutter: stable;
    padding: 20px;
}

/* ── General tab ── */
.opt-section {
    background: var(--primary-pale);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 16px 18px;
    margin-bottom: 14px;
}

/* ── Billing admin tab (v4.2.148) — theme-aware via CSS tokens ─────────────── */
.billing-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 12px;
    margin-bottom: 18px;
}
.billing-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 12px 14px;
}
.billing-card .bc-label {
    font-size: 0.72rem;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    margin-bottom: 4px;
}
.billing-card .bc-value {
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--primary-fg);
}
.billing-card .bc-sub { font-size: 0.72rem; color: var(--text-muted); margin-top: 2px; }
.billing-section-title {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--primary-fg);
    margin: 18px 0 6px;
}
.billing-table { width: 100%; border-collapse: collapse; font-size: 0.78rem; }
.billing-table th {
    padding: 5px 8px;
    text-align: left;
    border-bottom: 1px solid var(--border);
    font-weight: 600;
    color: var(--text-muted);
}
.billing-table td { padding: 4px 8px; border-bottom: 1px solid var(--border-subtle, var(--border)); vertical-align: top; }
.billing-table td.num, .billing-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.billing-table tr:hover td { background: var(--hover-bg); }
.billing-table tr.billing-row-clickable { cursor: pointer; }
/* #2417 — a table of a few columns (By stage: Stage, Calls, Total Tokens) sizes to
   its content instead of the full width, which put each number a page-width away
   from its label (Craig 2026-10-25: "it does not need the full width"). The gap
   before each number column keeps the figures clear of the name beside them. */
.billing-table.billing-table-compact { width: auto; min-width: 22rem; }
.billing-table.billing-table-compact td.num,
.billing-table.billing-table-compact th.num { padding-left: 2.5rem; }

/* ── Admin tables ─────────────────────────────────────────────────────────────
 * #1618 (Craig 2026-09-13) — "Credit batches and recent ledger entry tables need
 * to look like much more professional scrollable tables each. Use the real
 * estate a bit better, line the fields up."
 *
 * THE CLASS HAD NO RULE. Six tables in app.js carried `class="admin-table"` and
 * nothing in this file or index.html ever defined it, so every one of them
 * rendered as a raw browser-default table — no borders, no cell padding, no
 * header weight, no alignment, no scroll box. Nothing had regressed; the
 * definition was simply never written, which is why the fix is one block here
 * rather than six edits over there.
 *
 * Modelled on .billing-table above, deliberately: these are the same kind of
 * object on the same kind of screen, and a second hand-tuned table style is a
 * second thing to keep in step.
 *
 * THE HEADER IS STRUCTURAL. `position: sticky` keeps the row in normal flow and
 * the browser holds it at the top of its own scroll box — it is not a painted
 * bar over the top of a scrolling table, which is the layered trick the
 * guardrails forbid. It carries a background because real rows pass underneath
 * it, and `border-collapse: separate` because a collapsed border belongs to the
 * table rather than the cell and scrolls away from a sticky header; the
 * `inset` shadow is that border, drawn where it stays put.
 *
 * #607 solved the same problem by lifting the header into a SECOND table
 * outside the scroll box. That is the stronger technique and it is right there,
 * because it also keeps two tables' columns in step. It is not right here: six
 * tables built by six code paths would each need splitting and a shared
 * colgroup, which adds moving parts to a change whose whole point was that a
 * definition was missing.
 *
 * The colours are all variables that the `prefers-color-scheme: dark` block
 * remaps, so dark mode needs no twin of this block and cannot drift from it. */
.admin-table-scroll {
    max-height: 340px;
    overflow-y: auto;
    scrollbar-gutter: stable;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg-white);
}
.admin-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: 0.78rem;
    color: var(--text);
}
.admin-table th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--bg-white);
    padding: 7px 10px;
    text-align: left;
    font-weight: 600;
    color: var(--text-muted);
    white-space: nowrap;
    box-shadow: inset 0 -1px 0 var(--border);
}
.admin-table td {
    padding: 6px 10px;
    border-bottom: 1px solid var(--border-subtle, var(--border));
    vertical-align: top;
}
.admin-table tbody tr:last-child td { border-bottom: none; }
.admin-table tbody tr:hover td { background: var(--hover-bg); }
/* Numbers align on the right edge and share a digit width, so a column of them
   can be read down rather than one at a time. */
.admin-table td.num, .admin-table th.num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}
/* A date carries a time now (#1618), so it must not be allowed to wrap into two
   lines and make every row a different height. */
.admin-table td.when, .admin-table th.when {
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* #607 — the By-call table's frozen header. STRUCTURAL, not layered: the header
   is its own table OUTSIDE the scroll box, so nothing can scroll behind it and
   no z-index/opacity trick is needed (regcheck-guardrails, Part VI).

   Alignment is DECLARED, not measured: both tables carry the same <colgroup>
   and `table-layout: fixed`, so a long document title cannot widen one table
   and not the other. `scrollbar-gutter: stable` keeps the gutter reserved in
   both states, so the two tables cannot disagree by a scrollbar's width at the
   moment the header starts to matter. */
.billing-frozen-body {
    max-height: 360px;
    overflow: auto;
    scrollbar-gutter: stable;
}
.billing-frozen .billing-table { table-layout: fixed; }
.billing-frozen .billing-table td,
.billing-frozen .billing-table th {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* The head table keeps the gutter too, so its last column lines up with the
   body's even though only the body scrolls. */
.billing-table-head { scrollbar-gutter: stable; }
/* A small clear gap under the frozen header — the guardrail's other half. The
   header's own bottom border already separates it; this stops a hovered first
   row's tint touching the heading. */
.billing-table-head th { border-bottom: 2px solid var(--border); }
.billing-frozen-body .billing-table tr:first-child td { padding-top: 6px; }


/* #290 — select-rows cost readout (just above the Total-Tokens cards) */
.billing-cost-readout {
    display: flex; flex-wrap: wrap; gap: 6px 12px; align-items: baseline;
    border: 1px solid var(--border); border-radius: 8px;
    background: var(--surface); padding: 8px 12px; margin-bottom: 10px;
    font-size: 0.82rem;
}
.billing-cost-readout .bcr-hint    { color: var(--text-muted); font-size: 0.78rem; }
.billing-cost-readout .bcr-cost    { font-weight: 700; font-size: 1.05rem; color: var(--primary-fg); font-variant-numeric: tabular-nums; }
.billing-cost-readout .bcr-label   { color: var(--text-muted); }
.billing-cost-readout .bcr-detail  { color: var(--text-muted); font-size: 0.75rem; }
.billing-cost-readout .bcr-noprice { color: var(--conflict-text, #c53030); font-size: 0.75rem; font-weight: 600; }
/* #1471 — provenance sits with the number, not in a footnote. Muted rather than
   red: an estimate computed from list rates is not an error, it is a figure
   whose authority the reader is entitled to know before relying on it. Tokened
   so it follows the light/dark scheme like everything else. */
.billing-cost-readout .bcr-provenance { color: var(--text-muted); font-size: 0.72rem; font-style: italic; display: block; margin-top: 2px; }

/* #1471 — rate history and the unpriced gap. All tokenised, so light and dark
   both follow prefers-color-scheme rather than being hardcoded. */
.rate-gap { font-size: 0.82rem; padding: 8px 10px; border-radius: 4px; margin-bottom: 10px; border: 1px solid var(--border); }
/* An incomplete accounting figure is a WARNING, not a failure: the system is
   working exactly as designed and a person needs to act. Red is reserved for
   things that are broken. */
.rate-gap-open  { border-color: var(--conflict-text, #c53030); color: var(--conflict-text, #c53030); font-weight: 600; }
.rate-gap-warn  { color: var(--text-muted); }
.rate-gap-clear { color: var(--text-muted); }
.rate-superseded td { opacity: 0.55; text-decoration: line-through; }
.rate-src-list    { font-size: 0.72rem; color: var(--text-muted); }
.rate-src-invoice { font-size: 0.72rem; font-weight: 600; }
.rate-amend       { font-size: 0.72rem; color: var(--text-muted); }

/* #290 — row-select column (checkbox) */
.billing-table th.billing-sel-col, .billing-table td.billing-sel-col { width: 26px; text-align: center; padding-left: 4px; padding-right: 4px; }
.billing-table td.billing-sel-col input { cursor: pointer; }

/* #286 — trigger subtext under the Activity cell */
.billing-trigger-sub {
    color: var(--text-muted); font-size: 0.68rem; margin-top: 1px;
    max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* #287 — By-document custom dropdown: real columns, width caps, ellipsis */
.billing-doc-dd { position: relative; max-width: 640px; margin: 2px 0 8px; }
.billing-doc-dd-btn {
    width: 100%; text-align: left; padding: 6px 10px; font-size: 0.85rem;
    border: 1px solid var(--border); border-radius: 6px;
    background: var(--surface); color: var(--text);
    cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.billing-doc-dd-btn::after { content: ' ▾'; color: var(--text-muted); }
.billing-doc-dd-panel {
    position: absolute; top: calc(100% + 2px); left: 0; right: 0; z-index: 50;
    max-height: 320px; overflow: auto;
    border: 1px solid var(--border); border-radius: 6px;
    background: var(--surface); box-shadow: 0 6px 18px rgba(0,0,0,0.12);
}
.billing-doc-dd-panel.hidden { display: none; }
.billing-doc-dd-row {
    display: grid; grid-template-columns: minmax(90px, 150px) 1fr auto;
    gap: 10px; align-items: baseline;
    padding: 5px 10px; font-size: 0.8rem; cursor: pointer;
}
.billing-doc-dd-row:hover { background: var(--hover-bg); }
.billing-doc-dd-row .bdd-code  { font-weight: 600; color: var(--primary-fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.billing-doc-dd-row .bdd-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.billing-doc-dd-row .bdd-when  { color: var(--text-muted); font-size: 0.72rem; white-space: nowrap; }
.billing-doc-dd-row.bdd-unattributed { border-top: 1px solid var(--border); }
.billing-doc-dd-row.bdd-unattributed .bdd-title { color: var(--text-muted); font-style: italic; }
.opt-section h3 {
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--primary-fg);
    margin-bottom: 6px;
}
/* #1630 re-test — `.opt-help` and paragraph `.opt-note` had NO rule anywhere, so
   the same six tabs printed their explanations as bare browser paragraphs (full
   size, default margins): the crowding Craig saw. They are the same thing as
   `.opt-desc`, so they share its one definition rather than getting a copy. */
.opt-desc,
#admin-modal .opt-help,
#admin-modal p.opt-note {
    font-size: 0.82rem;
    color: var(--text-muted);
    margin-bottom: 12px;
    line-height: 1.5;
}
/* ⚠ #758 — THIS BLOCK DESCRIBES A LEGACY COMPONENT, NOT THE HOUSE STANDARD.
   ────────────────────────────────────────────────────────────────────────────
   It used to open "Default .btn-sem = filled PRIMARY", and that sentence
   was one of TWO contradictory statements of the house rule, both live: the
   other two (`.btn-sem` at the semantic-tier block, and the metadata window's
   button block) say soft-outline with ONE filled primary per context.

   CRAIG RULED 2026-08-21: **soft-outline is the default, and `.btn-sem` is the
   implementation.** The canonical statement is the `.btn-sem` block — search
   `N2 — semantic button tiers`. Nothing else may restate it; reference it.

   `.btn-sem` IS NOW SOFT-OUTLINE, and the history matters because it was
   deliberate for a long time. Flipping it in one edit was offered and DECLINED
   on 2026-08-21: 229 buttons changing at once would leave the genuine primary
   action on every screen looking like everything else UNTIL THE MARKING PASS IS
   DONE, which is a use-error risk in a GxP tool.

   Craig asked for the item to be completed on 2026-08-27, and the flip and the
   marking landed in ONE edit, which is what makes it safe: the condition was
   never "do not flip", it was "do not flip alone". Both markers already existed
   in the markup — `.opt-save-btn` on 36 affirmative actions, wearing an
   off-palette green, and `.opt-action-primary` on 12 which HAD NO CSS RULE AT
   ALL and was therefore inert. Both are the brand-blue primary now.

   Two other things that were the whole problem in 2026-08-21's measurement are
   also gone: every button carries a `title=` (v4.2.866, asserted at zero), and
   no button hard-codes an emoji or an entity close (v4.2.872/876).

   What this block still correctly documents about the component as it stands:
   pair with .opt-action-btn-secondary for the outline look; .icon = muted square
   icon-only button; .danger = red-on-hover (delete). Icons are the shared
   svg.ic line set (currentColor) — never per-call emoji. */
/* #2326 — the base rule and its hover are the one .btn-sem rule above (#758's neutral
   soft-outline). #1641: a DISABLED button does not answer the pointer, so the hover
   below never lights a disabled one. */
.btn-sem:hover:not(:disabled)    { background: var(--hover-bg); }

/* #758 — THE MARKING PASS, the half Craig's decision was waiting for. Flipping
   the default without it leaves the genuine primary looking like everything
   else, which is precisely what he declined.

   BOTH MARKERS ALREADY EXISTED IN THE MARKUP and are now what they claimed to
   be. `.opt-action-primary` was on 12 buttons and HAD NO CSS RULE AT ALL — the
   marking was inert, because the default was already filled. `.opt-save-btn`
   marked 36 affirmative actions in an off-palette GREEN the canonical statement
   does not list. Both are the brand-blue primary now, so 48 contexts get their
   one filled CTA and the other 180 go quiet. */
.btn-sem.opt-action-primary,
.btn-sem.opt-save-btn {
    border-color: var(--btn-primary);
    background: var(--btn-primary);
    color: #fff;
}
.btn-sem.opt-action-primary:hover:not(:disabled),
.btn-sem.opt-save-btn:hover:not(:disabled) {
    background: var(--btn-primary-h);
    border-color: var(--btn-primary-h);
    color: #fff;
}

/* Icon-only (row/inline actions) — quiet until hovered. */
.btn-sem.icon {
    background: transparent;
    color: var(--text-muted, #6b7685);
    border-color: var(--border, var(--border));
    padding: 0; width: 32px; height: 32px; justify-content: center;
}
.btn-sem.icon:hover { background: transparent; border-color: var(--primary-fg); color: var(--primary-fg); }
.btn-sem.danger.icon:hover { background: transparent; }

/* Shared inline line-icon (Feather/Lucide, MIT) — inherits the button/text colour
   via currentColor, so it themes + sizes consistently everywhere. */
svg.ic { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2;
         stroke-linecap: round; stroke-linejoin: round; vertical-align: middle; flex: none; }
.btn-sem.icon svg.ic { width: 17px; height: 17px; }

/* v4.1.23 (U3 chunk 4, Group C) — dirty-state save button. The button's
   availability is the primary save-state signal (feedback_dirty_state_save_pattern):
   when there are unsaved changes the controller adds .is-dirty, giving the
   enabled button an attention accent so the "needs saving" state reads at a
   glance; a clean button is simply :disabled (dimmed, unavailable). */
.btn-sem.is-dirty {
    border-color: var(--accent, #e07b39);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent, #e07b39) 35%, transparent);
}

.opt-action-btn-secondary {
    background: var(--bg-white) !important; color: var(--primary-ink, #2b6cb0) !important;
    border: 1px solid var(--primary, #2b6cb0) !important;
}
/* #673 — was `#e8edf5`, a hardcoded light hex on a hover state, so in dark mode
   the outline button flashed near-white under the pointer. A tint mixed from the
   token follows whichever palette is in force. */
.opt-action-btn-secondary:hover {
    background: color-mix(in srgb, var(--primary) 12%, transparent) !important;
}
.audit-action-btn { height: 34px; box-sizing: border-box; }

/* T-074(11) — Shared sizing for primary action buttons inside Admin Panel tabs.
   Use alongside .btn-sem (and .opt-save-btn for green-primary). Keeps
   side-by-side button rows visually consistent (e.g. + New Key vs Refresh on
   API Keys tab). v3.12.15 — height locked (was min-height) + padding fixed
   + line-height normalised so emoji-prefixed labels render at the same height
   as text-only ones across the row. */
.admin-panel-btn {
    height: 34px;
    min-width: 120px;
    padding: 0 14px;
    line-height: 1;
    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.confirm-cancel-btn {
    background: var(--bg);
    border-color: var(--border);
    color: var(--text);
}
.confirm-cancel-btn:hover { background: var(--border); }
/* #1655 — THREE LITERALS, ONE BUTTON, AND THEY HAVE TO MOVE TOGETHER. This
   carried `border:1px solid #aaa`, `background:#f0f0f0` and `color:#1a1a1a`:
   a light-grey button with near-black ink, which on a dark page is a pale
   slab in the middle of a dark dialog. The paired rule matters more than the
   individual swaps — `theme_colour_rules.md` puts it as a fixed text colour
   requiring a fixed background and vice versa, so tokenising the background
   and leaving the ink literal would have produced black-on-dark, which is
   worse than the light slab it replaced. Background, ink and border move as
   one set, onto tokens that already invert. */
.dialog-btn {
    padding: 6px 16px;
    border-radius: 5px;
    border: 1px solid var(--border);
    background: var(--surface-2);
    color: var(--text);
    font-size: 0.85rem;
    font-weight: 400;
    cursor: pointer;
    transition: background 0.15s;
}
.dialog-btn:hover { background: color-mix(in srgb, currentColor 12%, var(--surface-2)); }
.dialog-btn-primary {
    background: var(--primary);
    border-color: var(--primary-light);
    color: #fff;
}
.dialog-btn-primary:hover { background: var(--primary-light); }

/* ── ICH URL tab toolbar ── */
.ich-url-toolbar {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
}
.ich-url-save-status {
    font-size: 0.8rem;
    margin-left: 6px;
}
.ich-url-save-status.save-ok   { color: light-dark(#276749, color-mix(in oklab, #276749 45%, white)); }
.ich-url-save-status.save-fail { color: var(--conflict-text); }
/* #1783 — a refusal (the server understood and declined) is a warning, not a fault. */
.ich-url-save-status.save-refused { color: var(--gap-text); }

.reg-last-checked {
    font-size: 0.78rem;
    color: var(--text-muted);
    margin-bottom: 8px;
    min-height: 1.2em;
}

/* ── ICH URL table ── */
.ich-url-table-wrap {
    overflow: auto;
    border: 1px solid var(--border);
    border-radius: 6px;
    max-height: calc(82vh - 200px);
}
.ich-url-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.8rem;
}
.ich-url-table thead th {
    position: sticky;
    top: 0;
    background: var(--primary-pale);
    border-bottom: 1px solid var(--border);
    padding: 7px 10px;
    text-align: left;
    font-weight: 600;
    color: var(--primary-fg);
    white-space: nowrap;
}
.ich-url-table tbody tr:nth-child(even) { background: light-dark(#fafafa, var(--pq-elevated)); }
.ich-url-table tbody tr:hover           { background: var(--hover-bg); }
.ich-url-table td {
    padding: 5px 10px;
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
}
.url-code { font-weight: 600; white-space: nowrap; color: var(--primary-fg); min-width: 80px; }
.url-cat  { white-space: nowrap; color: var(--text-muted); min-width: 100px; }
.url-title { color: var(--text); }
.url-edit-cell { min-width: 280px; }
.url-input-wrap {
    display: flex;
    align-items: center;
    gap: 0;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--bg-white);
    overflow: hidden;
}
.url-input-wrap:focus-within {
    border-color: var(--primary-light);
    box-shadow: 0 0 0 2px rgba(42, 90, 140, 0.15);
}
.url-scheme {
    padding: 3px 5px;
    font-size: 0.72rem;
    font-family: 'SFMono-Regular', Consolas, monospace;
    color: var(--text-muted);
    background: var(--bg);
    border-right: 1px solid var(--border);
    white-space: nowrap;
    user-select: none;
}
.url-input {
    width: 100%;
    padding: 3px 6px;
    border: none;
    outline: none;
    font-size: 0.75rem;
    font-family: 'SFMono-Regular', Consolas, monospace;
    color: var(--text);
    background: transparent;
}
.status-th    { width: 56px; text-align: center; }
.url-status-cell { text-align: center; display: flex; align-items: center; justify-content: center; gap: 4px; }
.url-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    font-size: 0.75rem;
    font-weight: 700;
}
.url-badge-unknown  { background: light-dark(#e2e8f0, var(--pq-line)); color: var(--text-muted); }
.url-badge-checking { background: light-dark(#bee3f8, color-mix(in oklab, #bee3f8 14%, var(--pq-surface))); color: light-dark(#2b6cb0, var(--pq-accent)); }
.url-badge-ok       { background: light-dark(#c6f6d5, color-mix(in oklab, #c6f6d5 14%, var(--pq-surface))); color: light-dark(#276749, color-mix(in oklab, #276749 45%, white)); }
.url-badge-fail     { background: light-dark(#fed7d7, color-mix(in oklab, #fed7d7 14%, var(--pq-surface))); color: light-dark(#c53030, color-mix(in oklab, #c53030 45%, white)); cursor: help; }
.url-local-label {
    display: flex; align-items: center; gap: 6px;
    padding: 3px 6px; background: light-dark(#ebf8f2, var(--pq-elevated)); border-radius: 5px;
    border: 1px solid light-dark(#9ae6b4, color-mix(in oklab, #9ae6b4 30%, var(--pq-surface)));
}
.url-local-tag {
    font-size: 0.72rem; font-weight: 700; color: light-dark(#276749, color-mix(in oklab, #276749 45%, white));
    text-transform: uppercase; letter-spacing: 0.04em; flex-shrink: 0;
}
.url-local-filename {
    font-size: 0.75rem; color: light-dark(#2d6a4f, color-mix(in oklab, #2d6a4f 45%, white)); overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap; flex: 1;
}
.url-local-remove {
    background: none; border: none; color: #9ae6b4;
    cursor: pointer; font-size: 0.75rem; padding: 0 2px; flex-shrink: 0;
}
.url-local-remove:hover { color: light-dark(#c53030, color-mix(in oklab, #c53030 45%, white)); }
.url-upload-btn {
    cursor: pointer; font-size: 0.88rem; padding: 1px 3px;
    color: var(--text-muted); line-height: 1;
}
.url-upload-btn:hover { color: var(--primary-fg); }

.ich-new-row td { background: light-dark(#fffbeb, color-mix(in oklab, #fffbeb 14%, var(--pq-surface))) !important; }
.ich-new-input { width: 100%; font-size: 0.82rem; padding: 3px 6px; border: 1px solid #d97706;
    border-radius: 4px; background: var(--bg-white); font-family: inherit; }
.ich-new-input:focus { outline: none; border-color: #b45309; box-shadow: 0 0 0 2px #fde68a; }
.ich-new-cat { cursor: pointer; }
.ich-remove-btn { background: none; border: none; color: light-dark(#b91c1c, color-mix(in oklab, #b91c1c 45%, white)); font-size: 1.1rem;
    cursor: pointer; line-height: 1; padding: 0 4px; font-weight: 700; }
.ich-remove-btn:hover { color: light-dark(#7f1d1d, color-mix(in oklab, #7f1d1d 45%, white)); }
/* #1446 — the glyph became icon('x'). Base `.ic` is 18x18; this button's `&times;`
   sat at font-size 1.1rem with line-height 1, whose VISIBLE mark is nearer 12px,
   so inheriting 18 would grow a control that lives in a dense table row. 14px
   keeps its weight. `stroke: currentColor` on `.ic` means the red and the hover
   red carry over with no extra rule — which is the point of the shared icon and
   the reason the swap is not just cosmetic. */
.ich-remove-btn .ic { width: 14px; height: 14px; }

/* right-click context menu for ICH table rows (#1901: the menu itself is RCMenu) */
.ich-ctx-cell { cursor: context-menu; }

.url-version-hint { min-height: 0; }
.url-version-suggest { display: inline-flex; align-items: center; gap: 6px;
    font-size: 0.75rem; color: light-dark(#b45309, color-mix(in oklab, #b45309 45%, white)); margin-top: 4px; }
.url-version-accept-btn { font-size: 0.72rem; padding: 1px 7px; border-radius: 10px;
    background: #b45309; color: #fff; border: none; cursor: pointer; font-weight: 600;
    transition: background 0.15s; }
.url-version-accept-btn:hover { background: #92400e; }
.url-version-applied { display: inline-block; font-size: 0.75rem; color: light-dark(#276749, color-mix(in oklab, #276749 45%, white));
    margin-top: 4px; }

.table-loading {
    text-align: center;
    padding: 20px;
    color: var(--text-muted);
    font-size: 0.82rem;
}

/* ── App menu (OS-style dropdown) ── */
.app-menu-wrap {
    position: relative;
    flex-shrink: 0;
}
.app-menu-btn {
    background: none;
    border: 1px solid color-mix(in srgb, var(--chrome-ink) 25%, transparent);   /* #1764 */
    color: var(--chrome-ink);
    border-radius: 5px;
    padding: 4px 10px;
    font-size: 0.82rem;
    font-weight: 500;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s;
}
.app-menu-btn:hover { background: color-mix(in srgb, var(--chrome-ink) 15%, transparent); }
.app-menu-dropdown {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    background: var(--bg-white);
    border: 1px solid var(--border);
    border-radius: 7px;
    box-shadow: 0 6px 24px rgba(0,0,0,0.18);
    min-width: 220px;
    /* L-048 (Craig 2026-06-18) — the ☰ menu is top-level nav and must paint
       ABOVE everything: the graph/doc palettes (≤1300) and the launch/framework
       floats (.doc-launch-pop 9998, finding popup 9999, edit popover 10000).
       Was z-index:300 → the palette +/−/🔍 buttons poked through the open menu. */
    z-index: 10120;
    padding: 4px 0;
}
.app-menu-dropdown.hidden { display: none; }
.app-menu-item {
    display: block;
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    padding: 7px 16px;
    font-size: 0.84rem;
    color: var(--text);
    cursor: pointer;
    transition: background 0.1s;
}
.app-menu-item:hover { background: var(--hover-bg); color: var(--primary-fg); }
/* #443 — PARKED menu items. Craig 2026-07-30: "turn 4, 5 and 6 off. We will get to
   them later, make no changes to them except to reorganize the menu."
   A CSS-only off switch, deliberately: Browse Saved Analyses / Save Analysis / Save
   to QMS Document have show/hide logic scattered through app.js (~7 sites — grep the
   three menu-*-btn ids; line numbers drift too fast to cite), and editing any of it
   would be "changing them". This wins
   over all of it without touching a line, and removing the attribute in index.html
   turns them back on. It is a PARK, not a fallback — the items still work, they are
   simply not offered while Save's semantics are being decided (a deep clone of the
   object model rather than a filename-keyed analysis record). */
.app-menu-item[data-parked] { display: none !important; }


.app-menu-sep {
    height: 1px;
    background: var(--border);
    margin: 4px 0;
}
.app-menu-setting {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    cursor: default;
}
.app-menu-setting:hover { background: var(--hover-bg); }
.app-menu-setting select {
    font-size: 0.78rem;
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 2px 4px;
    background: var(--surface);
    color: var(--text);
    cursor: pointer;
}


/* ── Org Profile tab ── */
.org-grid {
    display: grid;
    grid-template-columns: 100px 1fr;
    gap: 8px 12px;
    align-items: center;
    margin-bottom: 4px;
}
.org-field-label { font-size: 0.82rem; color: var(--text-muted); font-weight: 600; }
/* #1616 — a stated value sitting among filter controls. It reads as what it is:
   the organisation you are already in, not a field to type into. Keeps the
   controls' type scale so the row still lines up, and drops the border, the
   input background and the field padding that made a read-only span look
   editable. Weighted, because it IS the answer rather than a label. */
.fo-scope-value {
    display: inline-block;
    font-size: 0.83rem;
    font-weight: 600;
    color: var(--text);
    padding: 5px 0;
}

.org-input {
    padding: 5px 8px;
    border: 1px solid var(--border);
    border-radius: 4px;
    font-size: 0.83rem;
    background: var(--input-bg, #fff);
    color: var(--text);
    width: 100%;
}
/* Dark-mode: .org-input bg used --input-bg (undefined → white #fff) while text
   flipped to light → light-on-white, worst on the login Domain <select> (Craig
   D8). Dark the input surface; fixes login + all org forms consistently. */
@media (prefers-color-scheme: dark) {
    .org-input { background: var(--card); border-color: var(--border); }
    .org-input::placeholder { color: var(--text-muted); }
    .org-input option { background: var(--card); color: var(--text); }
}
/* Required-field validation state — red border when a compulsory field
   is left empty on submit (cleared on the next keystroke). */
.org-input.input-invalid {
    border-color: var(--conflict-text, #c53030);
    box-shadow: 0 0 0 1px var(--conflict-text, #c53030);
}
.org-checks {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 16px;
}
.org-checks label {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 0.82rem;
    cursor: pointer;
    white-space: nowrap;
}
.org-textarea {
    width: 100%;
    padding: 7px 9px;
    border: 1px solid var(--border);
    border-radius: 4px;
    font-size: 0.82rem;
    font-family: inherit;
    resize: vertical;
    background: var(--input-bg, #fff);
    color: var(--text);
    line-height: 1.5;
}

/* ── Lined textarea (numbered lines) ── */
.lined-wrap {
    display: flex;
    border: 1px solid var(--border);
    border-radius: 4px;
    overflow: hidden;
    min-height: 228px;
    max-height: 400px;
    background: var(--bg-white);
}


.org-textarea.lined-ta {
    flex: 1;
    border: none;
    border-radius: 0;
    resize: none;
    padding: 7px 9px;
    font-family: 'Courier New', Courier, monospace;
    font-size: 0.82rem;
    line-height: 1.5;
    min-height: 228px;
    overflow-y: auto;
    background: var(--bg-white);
}
.cal-lined-wrap {
    min-height: 80px;
    max-height: 160px;
}
.cal-textarea.lined-ta {
    min-height: 80px;
    overflow-y: auto;
    font-family: inherit;
    font-size: 0.8rem;
}
.opt-field-hint {
    font-size: 0.72rem;
    font-weight: 400;
    color: var(--text-muted);
    margin-left: 6px;
}
.org-save-row { display: flex; align-items: center; gap: 12px; }

/* ── Calibration tab ── */
.cal-loading { padding: 16px; color: var(--text-muted); font-size: 0.85rem; }
.cal-principle {
    border: 1px solid var(--border);
    border-radius: 6px;
    margin-bottom: 12px;
    overflow: hidden;
}
.cal-principle-name {
    background: var(--sidebar-bg);
    padding: 7px 12px;
    font-size: 0.83rem;
    font-weight: 700;
    color: var(--primary-fg);
    border-bottom: 1px solid var(--border);
}
.cal-fields {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0;
}
.cal-field { padding: 10px 12px; }
.cal-field:first-child { border-right: 1px solid var(--border); }
.cal-label {
    display: block;
    font-size: 0.75rem;
    font-weight: 700;
    margin-bottom: 5px;
    text-transform: uppercase;
    letter-spacing: 0.4px;
}
.cal-label.never  { color: light-dark(#c62828, color-mix(in oklab, #c62828 45%, white)); }
.cal-label.always { color: light-dark(#2e7d32, color-mix(in oklab, #2e7d32 45%, white)); }
.cal-textarea {
    width: 100%;
    padding: 6px 8px;
    border: 1px solid var(--border);
    border-radius: 4px;
    font-size: 0.8rem;
    font-family: inherit;
    resize: vertical;
    background: var(--input-bg, #fff);
    color: var(--text);
    line-height: 1.5;
}

/* ── SUCCESS section ── */
.success-label {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 7px 6px;
    border-radius: 5px;
    cursor: pointer;
    transition: background 0.15s;
    margin-bottom: 2px;
}
.success-label:hover { background: var(--hover-bg); }
.success-label input[type="checkbox"] { margin-top: 3px; flex-shrink: 0; }
.success-hint {
    font-size: 0.68rem;
    color: var(--text-muted);
    margin-top: 1px;
    line-height: 1.3;
}
.success-run-btn {
    display: block;
    width: 100%;
    margin-top: 10px;
    padding: 8px 0;
    background: linear-gradient(135deg, #1565c0, #0d47a1);
    color: #fff;
    border: none;
    border-radius: 6px;
    font-size: 0.82rem;
    font-weight: 600;
    cursor: pointer;
    letter-spacing: 0.3px;
    transition: opacity 0.15s;
}
.success-run-btn:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}
.success-run-btn:not(:disabled):hover { opacity: 0.88; }

/* ── Password show/hide ── */
.pw-wrap {
    position: relative;
    display: flex;
    align-items: center;
}
.pw-wrap .org-input {
    flex: 1;
    padding-right: 2.2rem;
}
.pw-toggle-btn {
    position: absolute;
    right: 6px;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 1rem;
    color: var(--text-muted);
    padding: 2px 4px;
    line-height: 1;
    opacity: 0.6;
}
.pw-toggle-btn:hover { opacity: 1; }

/* ── Auth modals ── */
.auth-form {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 4px 0;
}
.auth-label {
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-muted);
    margin-bottom: -4px;
}
.auth-error {
    color: var(--conflict-text);
    background: var(--conflict-badge);
    border: 1px solid light-dark(#fed7d7, color-mix(in oklab, #fed7d7 30%, var(--pq-surface)));
    border-radius: 4px;
    padding: 6px 10px;
    font-size: 0.82rem;
}
.auth-ok {
    color: light-dark(#276749, color-mix(in oklab, #276749 45%, white));
    background: light-dark(#c6f6d5, color-mix(in oklab, #c6f6d5 14%, var(--pq-surface)));
    border: 1px solid light-dark(#9ae6b4, color-mix(in oklab, #9ae6b4 30%, var(--pq-surface)));
    border-radius: 4px;
    padding: 6px 10px;
    font-size: 0.82rem;
}
.auth-link-btn {
    background: none;
    border: none;
    color: var(--primary-fg);   /* #1880 — --primary-light measured 3.4 on white */
    font-size: 0.82rem;
    cursor: pointer;
    text-decoration: underline;
    padding: 4px 0;
    text-align: center;
}
.auth-link-btn:hover { text-decoration-thickness: 2px; }
.admin-users-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.82rem;
}
.admin-users-table th {
    background: var(--primary-pale);
    padding: 6px 10px;
    text-align: left;
    font-weight: 600;
    border-bottom: 1px solid var(--border);
}
.admin-users-table td {
    padding: 6px 10px;
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
}
.admin-users-table tr:last-child td { border-bottom: none; }
.user-disabled { opacity: 0.55; }

/* ── Users table (admin users tab) ── */
.user-badge.warn   { background: #c05621; color: #fff; font-size: 0.72rem; font-weight: 700; padding: 1px 5px; border-radius: 8px; }
.user-badge.sys    { background: var(--primary); color: #fff; font-size: 0.72rem; font-weight: 700; padding: 1px 5px; border-radius: 8px; }
.user-badge.domain { background: #5b21b6; color: #fff; font-size: 0.72rem; font-weight: 700; padding: 1px 5px; border-radius: 8px; }
.user-badge.org    { background: #065f46; color: #fff; font-size: 0.72rem; font-weight: 700; padding: 1px 5px; border-radius: 8px; }
/* #1737 — Quality Lead, a category held beside the class badge. Colours as its siblings. */
.user-badge.ql     { background: #1e40af; color: #fff; font-size: 0.72rem; font-weight: 700; padding: 1px 5px; border-radius: 8px; }

/* ── Org list cards (Admin Panel → Organisations) ───────────────────────────
   Modernised look: white surface, breathing room, clear button hierarchy.
   The same .user-card markup is reused for users elsewhere; org-specific
   tweaks live under .org-card. */
#admin-orgs-list { display: flex; flex-direction: column; gap: 10px; }

.org-card {
    background: var(--bg-white);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 14px 16px;
    transition: box-shadow 0.15s ease, transform 0.15s ease, border-color 0.15s ease;
}
.org-card:hover {
    box-shadow: 0 4px 12px rgba(26, 58, 92, 0.08);
    border-color: var(--border);
}
.org-card-row {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 16px;
    align-items: start;
}
.org-card-main { min-width: 0; }   /* allow children to truncate cleanly */
.org-card-title-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 6px;
}
.org-card-name {
    font-size: 1rem;
    font-weight: 600;
    color: var(--primary-fg);
    line-height: 1.3;
}
.org-card-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 14px 18px;
    font-size: 0.78rem;
    color: var(--text-muted);
    line-height: 1.4;
    margin-top: 2px;
}
.org-card-meta code {
    font-family: 'Courier New', monospace;
    font-size: 0.78rem;
    background: var(--primary-pale);
    color: var(--primary-fg);
    padding: 1px 6px;
    border-radius: 4px;
}
.org-card-id {
    font-family: 'Courier New', monospace;
    font-size: 0.72rem;
    color: var(--text-muted);
}
.org-card-members {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px dashed var(--border);
}
/* #2134 — the organisation's plan and licence window (system administrators). */
.org-plan-host:empty { display: none; }
.org-plan { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 10px;
            padding-top: 10px; border-top: 1px dashed var(--border); font-size: 0.85rem; }
.org-plan-state[data-state="grace"] { color: var(--pq-warn); font-weight: 600; }
.org-plan-state[data-state="locked"] { color: var(--conflict-text, var(--text)); font-weight: 600; }
.org-plan-editor { flex-basis: 100%; display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
                   gap: 8px; align-items: end; }
.org-plan-field { display: flex; flex-direction: column; gap: 4px; font-size: 0.78rem; color: var(--text-muted); }
.org-plan-warn, .org-plan-adds { grid-column: 1 / -1; margin: 0; font-size: 0.82rem; }
.org-plan-warn { color: var(--conflict-text, var(--text)); }
.org-plan-actions { grid-column: 1 / -1; display: flex; gap: 8px; }
.org-plan-status { font-size: 0.78rem; color: var(--text-muted); }
.org-plan-status.save-ok { color: var(--ok, var(--text)); }
.org-plan-status.save-fail { color: var(--conflict-text, var(--text)); }
.org-member-chip {
    background: var(--primary-pale);
    color: var(--primary-fg);
    font-size: 0.74rem;
    padding: 2px 8px;
    border-radius: 999px;
    font-weight: 500;
}
.org-member-empty {
    font-size: 0.78rem;
    color: var(--text-muted);
    font-style: italic;
}
.org-member-more {
    font-size: 0.74rem;
    color: var(--text-muted);
    padding: 2px 4px;
    align-self: center;
}
.org-card-actions {
    display: flex;
    flex-direction: column;
    gap: 6px;
    align-items: stretch;
    min-width: 96px;
}

/* Pill-style badges for the org row — calmer palette than the user badges */
.org-pill {
    display: inline-flex;
    align-items: center;
    font-size: 0.72rem;
    font-weight: 600;
    padding: 2px 10px;
    border-radius: 999px;
    border: 1px solid transparent;
    line-height: 1.5;
    letter-spacing: 0.01em;
}
.org-pill-domain {
    background: light-dark(#e6f1fa, color-mix(in oklab, #e6f1fa 14%, var(--pq-surface)));          /* tint of --primary */
    color: var(--primary-fg);
    border-color: light-dark(#c9dcef, color-mix(in oklab, #c9dcef 30%, var(--pq-surface)));
}


/* Secondary (outlined) and danger button variants — used in org/user cards */
.btn-sem.opt-action-secondary {
    background: var(--bg-white);
    color: var(--primary-fg);
    border: 1px solid var(--primary-light);
}
.btn-sem.opt-action-secondary:hover {
    background: var(--hover-bg);
    color: var(--primary-fg);
}
.btn-sem.opt-action-danger {
    background: var(--bg-white);
    color: var(--conflict-text);
    border: 1px solid light-dark(#fbb6ae, color-mix(in oklab, #fbb6ae 30%, var(--pq-surface)));
}
.btn-sem.opt-action-danger:hover {
    background: var(--conflict-text);
    color: #fff;
    border-color: var(--conflict-text);
}
/* #146 — find, order, and a list that scrolls.
 *
 * Craig 2026-05-30: *"the list of users in the user tab of admin needs to
 * scroll as there could be hundreds, at least 5 visible … We will also need
 * find, filter and order functionality."*
 *
 * Every colour here is a token, so the tab follows the user's theme rather
 * than pinning a light palette into a surface that has a dark one. */
.users-toolbar {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 6px 0 8px;
}
.users-search {
    flex: 1 1 260px;
    max-width: 380px;
    padding: 5px 9px;
    font-size: 0.82rem;
    font-family: inherit;
    color: var(--text);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 5px;
}
.users-search:focus {
    outline: none;
    border-color: var(--accent, var(--text-muted));
}
.users-count {
    font-size: 0.78rem;
    color: var(--text-muted);
    white-space: nowrap;
}
/* THE SCROLL, and the number Craig gave. "At least 5 visible" is the floor,
 * not the target — the cap is a height rather than a row count because the
 * edit form expands inline underneath a row, and counting rows would shrink
 * the list to nothing the moment somebody opened one. */
.users-scroll {
    max-height: 52vh;
    min-height: 11rem;
    overflow-y: auto;
    overflow-x: auto;
    border: 1px solid var(--border);
    border-radius: 6px;
}
/* The header has to survive the scroll, or sorting a long list means scrolling
 * back to the top to change your mind. Structural, per the sticky-header
 * guardrail: it stays in flow and carries its own background rather than
 * being lifted out with z-index and an opaque panel. */
.users-scroll .users-table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--surface, var(--bg));
}
.users-th-sortable {
    cursor: pointer;
    user-select: none;
}
.users-th-sortable:hover { color: var(--text); }
.users-th-sortable.is-sorted { color: var(--text); }
.users-sort-caret {
    display: inline-block;
    width: 0.9em;
    margin-left: 2px;
    font-size: 0.7em;
}
.users-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.84rem;
}
.users-table thead th {
    text-align: left;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--text-muted);
    padding: 4px 10px 6px;
    border-bottom: 2px solid var(--border);
    white-space: nowrap;
}
.users-table thead th.user-col-cb { text-align: center; }
.user-row td {
    padding: 7px 10px;
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
}
.user-row.user-disabled { opacity: 0.55; }
.user-row:hover td { background: var(--surface); }
.user-col-name  { font-weight: 600; }
.user-col-meta  { color: var(--text-muted); font-size: 0.8rem; white-space: nowrap; }

.user-col-edit  { text-align: right; white-space: nowrap; }
.user-edit-btn {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 1.25rem;
    padding: 2px 6px;
    color: var(--text-muted);
    border-radius: 4px;
    line-height: 1;
}
.user-edit-btn:hover { color: var(--primary-fg); background: var(--surface); }


/* ── User inline edit form ── */
.user-edit-form {
    padding: 12px 14px;
    background: var(--surface);
}
.user-edit-grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 8px;
    align-items: end;
    margin-bottom: 8px;
}
@media (max-width: 700px) {
    .user-edit-grid { grid-template-columns: 1fr 1fr; }
}

/* (Logo drop-zone CSS retired with the /logo/ upload store — branding-graphics-review.) */

/* ── Admin: Domain Editor ── */
/* #1641 — ONE SCROLLER, THE WINDOW'S. Craig's test, 2026-09-26: "The window is
   not using all the realestate and therefore requires scrolling". This layout
   was a fixed 540px box scrolling inside the Settings frame, which is itself a
   fixed 82vh frame whose body scrolls (#1630) — two scrollers, one nested in
   the other, and dead space under the box on any tall window. Now the editor
   takes its natural height and the frame's body is the only scroller; the
   domain list stays pinned beside it and Save Changes stays in view. */
/* #1641 round 3 — TWO SCROLLERS, SIDE BY SIDE. Craig's test, 2026-09-27: "During
   the scroll the items on the left panel also scrolled, they shoud scroll
   independantly from the other panel." The sticky list inside the window's one
   scroller did not hold. So the layout now has a definite height - the space the
   window's body actually has below the tab strip, MEASURED by _sizeDomainEditor
   (app.js) because the strip wraps to one, two or three rows - and the list and
   the editor each scroll inside it, independently. The window's body no longer
   has anything to scroll on this tab. */
.domain-editor-layout {
    display: flex;
    gap: 0;
    align-items: stretch;
    border: 1px solid var(--border);
    border-radius: 6px;
    min-height: 320px;
    overflow: hidden;
}
.domain-list-col {
    display: flex;
    flex-direction: column;
    width: 190px;
    flex-shrink: 0;
    border-right: 1px solid var(--border);
    background: var(--surface);
    min-height: 0;
}
.domain-list-pane {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
}
.domain-edit-pane {
    flex: 1;
    min-width: 0;
    min-height: 0;
    overflow-y: auto;
    padding: 16px 20px 0;
}
.domain-edit-pane .de-actions {
    position: sticky;
    bottom: 0;
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
    padding: 10px 0 14px;
    background: var(--surface);
    border-top: 1px solid var(--border);
}
/* The colour section: the house range beside the domain's own fields, so a
   pick lands where it is seen; one column when the frame is narrow. */
.de-colour-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 12px 28px;
    align-items: start;
}
.pq-swatch[aria-pressed="true"] {
    outline: 2px solid var(--pq-ink);
    outline-offset: 2px;
}
.domain-list-item {
    padding: 9px 12px;
    cursor: pointer;
    border-bottom: 1px solid var(--border);
    border-left: 3px solid transparent;
    transition: background .12s;
}
.domain-list-item:hover { background: var(--bg-hover, #f0f4f8); }
.domain-list-item.active {
    background: var(--primary-ultra-light, #e8f0f8);
    border-left-color: var(--primary-fg);
}
.domain-list-name { font-weight: 600; font-size: 0.88rem; margin-bottom: 3px; }
.domain-list-meta { display: flex; align-items: center; gap: 6px; }
.domain-custom-badge {
    font-size: 0.68rem; background: light-dark(#d4edda, color-mix(in oklab, #d4edda 14%, var(--pq-surface))); color: light-dark(#155724, color-mix(in oklab, #155724 45%, white));
    padding: 1px 5px; border-radius: 3px; font-weight: 600;
}
/* T-079(3) — "template" indicator for zero-framework domains. Replaces
   the earlier behaviour of concatenating " Template" onto the brand
   name, which doubled up to "SUCCESS QMS Template Template" when the
   brand name itself ended in "Template". */
.domain-template-badge {
    font-size: 0.68rem; background: light-dark(#fef3c7, color-mix(in oklab, #fef3c7 14%, var(--pq-surface))); color: light-dark(#92400e, color-mix(in oklab, #92400e 45%, white));
    padding: 1px 5px; border-radius: 3px; font-weight: 600;
    border: 1px solid light-dark(#fde68a, color-mix(in oklab, #fde68a 30%, var(--pq-surface)));
}
.de-section-heading {
    margin: 0 0 10px; font-size: 0.8rem; text-transform: uppercase;
    letter-spacing: .05em; color: var(--text-secondary);
}
.de-label { font-size: 0.82rem; font-weight: 600; margin: 8px 0 3px; color: var(--text-secondary); }
/* #1698 — the Raise-a-finding window: the step as it reads now, and the Suggest button
   on the suggested-change label's row. Tokens only, so both themes follow. */
.rc-af-current { max-height: 10rem; overflow-y: auto; white-space: pre-wrap; padding: 8px 10px;
                 border: 1px solid var(--border); border-radius: var(--radius-md);
                 background: var(--surface-2); color: var(--text); font-size: var(--font-size-sm); }
.rc-af-label-row { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--space-xs); }
.rc-af-label-row .btn-sem { margin-bottom: 3px; }


/* ── AI Chat — open button ── */
/* HISTORY, IN THE PAST TENSE. The Chat-with-AI-Inspector button moved into a
   `.results-actions` toolbar at v3.8.28, alongside Expert/Report/Audit/Edit,
   and took `.download-btn`'s styling instead of its own `.chat-open-btn`.
   BOTH OF THOSE CLASSES ARE GONE: P-03 (Craig 2026-06-25) moved every one of
   those actions into the floating doc palette, and #1440 deleted the empty
   toolbar and its rules once nothing was left inside it. Chat is a palette
   tool now and carries the palette's own styling. Kept because it explains
   where `.chat-open-btn` went, which is otherwise unanswerable. */

/* ── Floating chat window ── */
.chat-float {
    position: fixed;
    bottom: 24px;
    right: 24px;
    width: 480px;
    height: 560px;
    min-width: 320px;
    min-height: 280px;
    display: flex;
    flex-direction: column;
    color-scheme: light dark;
    background: Canvas;
    color: CanvasText;
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 8px 32px rgba(0,0,0,0.22);
    /* #1902 — above the process map's header (.graph-view-header, 10100) and the
       canvas floats beneath it; below the top nav (10200) and the modals. At 900
       the map header covered this window's own header on the process map, so Pop
       out, Reset position and Close could not be reached. */
    z-index: 10150;
    resize: both;
    overflow: hidden;
}
.chat-float.hidden { display: none; }

.chat-float-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 9px 14px;
    background: var(--primary);
    color: #fff;
    border-radius: 9px 9px 0 0;
    cursor: grab;
    user-select: none;
    flex-shrink: 0;
}
.chat-float-header:active { cursor: grabbing; }
.chat-title { font-weight: 600; font-size: 0.88rem; color: #fff; }
.chat-header-btn {
    background: none; border: none; cursor: pointer;
    font-size: 1rem; color: rgba(255,255,255,0.8); line-height: 1; padding: 0 2px;
}
.chat-header-btn:hover { color: #fff; }


.chat-mode-bar {
    display: flex;
    gap: 0;
    padding: 5px 10px;
    background: light-dark(#f1f5f9, var(--pq-elevated));
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}
.chat-mode-btn {
    flex: 1;
    padding: 4px 0;
    font-size: 0.78rem;
    font-weight: 500;
    border: 1px solid light-dark(#cbd5e1, color-mix(in oklab, #cbd5e1 30%, var(--pq-surface)));
    background: var(--bg-white);
    color: light-dark(#475569, var(--pq-ink-muted));
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}
.chat-mode-btn:first-child { border-radius: 5px 0 0 5px; }
.chat-mode-btn:last-child  { border-radius: 0 5px 5px 0; border-left: none; }
.chat-mode-btn.active      { background: var(--primary); color: #fff; border-color: var(--primary-fg); }
.chat-mode-btn:not(.active):hover { background: light-dark(#e2e8f0, var(--pq-line)); }

.chat-messages {
    flex: 1;
    overflow-y: auto;
    padding: 12px 14px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.chat-msg { display: flex; flex-direction: column; gap: 3px; max-width: 92%; }
.chat-msg.user { align-self: flex-end; }
.chat-msg.assistant { align-self: flex-start; }
.chat-bubble {
    padding: 8px 12px;
    border-radius: 10px;
    font-size: 0.84rem;
    line-height: 1.55;
    word-break: break-word;
}
.chat-msg.user .chat-bubble {
    background: var(--primary);
    color: #fff;
    border-bottom-right-radius: 3px;
    white-space: pre-wrap;
}
.chat-msg.assistant .chat-bubble {
    background: var(--bg);
    border: 1px solid var(--border);
    color: var(--text);
    border-bottom-left-radius: 3px;
}
.chat-msg.streaming .chat-bubble { opacity: 0.8; white-space: pre-wrap; }


.chat-md { line-height: 1.55; }
.chat-md h1, .chat-md h2, .chat-md h3 { margin: 10px 0 4px; font-weight: 700; }
.chat-md h1 { font-size: 1rem; }
.chat-md h2 { font-size: 0.95rem; }
.chat-md h3 { font-size: 0.9rem; }
.chat-md code { background: var(--bg); padding: 1px 4px; border-radius: 3px; font-family: monospace; font-size: 0.85em; }

/* Markdown rendering inside assistant bubbles */
.chat-bubble p   { margin: 0 0 8px; }
.chat-bubble p:last-child { margin-bottom: 0; }
.chat-bubble ul, .chat-bubble ol { margin: 4px 0 8px 18px; }
.chat-bubble li  { margin-bottom: 3px; }
.chat-bubble strong { font-weight: 700; }
.chat-bubble em  { font-style: italic; }

/* Markdown tables */
.chat-bubble table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.8rem;
    margin: 8px 0;
}
.chat-bubble th,
.qe-bubble-asst th {   /* #2085 — one table header for both chats */
    background: var(--primary-pale);
    padding: 5px 8px;
    text-align: left;
    font-weight: 600;
    border: 1px solid var(--border);
}
.chat-bubble td {
    padding: 4px 8px;
    border: 1px solid var(--border);
    vertical-align: top;
}
.chat-bubble tr:nth-child(even) td { background: var(--bg); }

/* Suggested change card */
.chat-change-card {
    margin: 8px 0 2px;
    border: 1px solid var(--border);
    border-radius: 8px;
    overflow: hidden;
    font-size: 0.81rem;
    background: Canvas;
}
.chat-change-card.applied { border-color: #276749; background: light-dark(#f0fff4, var(--pq-elevated)); }
.chat-change-label {
    padding: 5px 10px;
    background: var(--gap-bg);
    font-weight: 600;
    font-size: 0.78rem;
    color: var(--gap-text);
    border-bottom: 1px solid var(--border);
}
.chat-change-card.applied .chat-change-label { background: light-dark(#c6f6d5, color-mix(in oklab, #c6f6d5 14%, var(--pq-surface))); color: light-dark(#276749, color-mix(in oklab, #276749 45%, white)); }
.chat-change-original {
    padding: 6px 10px;
    background: light-dark(#fff5f5, var(--pq-elevated));
    border-bottom: 1px solid var(--border);
    color: var(--conflict-text);
    font-family: monospace;
    font-size: 0.79rem;
    white-space: pre-wrap;
}
.chat-change-replacement {
    padding: 6px 10px;
    background: light-dark(#f0fff4, var(--pq-elevated));
    border-bottom: 1px solid var(--border);
    color: light-dark(#276749, color-mix(in oklab, #276749 45%, white));
    font-family: monospace;
    font-size: 0.79rem;
    white-space: pre-wrap;
}
.chat-change-rationale {
    padding: 5px 10px;
    color: var(--text-muted);
    font-size: 0.77rem;
    border-bottom: 1px solid var(--border);
}
.chat-change-actions { padding: 6px 10px; display: flex; gap: 8px; }
.chat-apply-btn {
    padding: 4px 12px;
    background: #276749;
    color: #fff;
    border: none;
    border-radius: 5px;
    font-size: 0.78rem;
    cursor: pointer;
    font-weight: 600;
}
.chat-apply-btn:hover { background: #1d4e35; }
.chat-apply-btn:disabled { opacity: 0.5; cursor: default; }
.chat-applied-badge {
    padding: 4px 10px;
    background: light-dark(#c6f6d5, color-mix(in oklab, #c6f6d5 14%, var(--pq-surface)));
    color: light-dark(#276749, color-mix(in oklab, #276749 45%, white));
    border-radius: 5px;
    font-size: 0.78rem;
    font-weight: 600;
}

.chat-input-row {
    display: flex;
    gap: 8px;
    padding: 10px 12px;
    border-top: 1px solid var(--border);
    background: var(--bg);
    flex-shrink: 0;
}
.chat-input {
    flex: 1;
    resize: none;
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 7px 10px;
    font-size: 0.84rem;
    font-family: inherit;
    background: Canvas;
    color: CanvasText;
    line-height: 1.4;
}
.chat-input:focus { outline: none; border-color: var(--primary-fg); }
.chat-send-btn {
    padding: 0 14px;
    background: var(--primary);
    color: #fff;
    border: none;
    border-radius: 6px;
    font-size: 1.1rem;
    cursor: pointer;
    align-self: flex-end;
    height: 36px;
    flex-shrink: 0;
}
.chat-send-btn:hover { background: var(--primary-dark, #1a5276); }
.chat-send-btn:disabled { opacity: 0.4; cursor: default; }


/* T-074(14) — Multi-paragraph insertion renders as one continuous highlight.
   docx-preview emits Word's <w:ins> track-changes runs as <ins> elements.
   When a paragraph_plus_list / paragraph_range finding inserts N consecutive
   paragraphs, each becomes its own <p><ins>...</ins></p> block — visually
   reading as N separated green blocks with paragraph-margin gaps between
   them. Collapse the margin between adjacent insertion paragraphs so the
   reader sees one continuous block of inserted content. */
.doc-body.docx-render .docx-wrapper p:has(ins) {
    margin-top: 6px;
    margin-bottom: 6px;
}
.doc-body.docx-render .docx-wrapper p:has(ins) + p:has(ins) {
    margin-top: 0;
}

/* (BURNED — B-115 WP3, v4.1.279) the v3.12.35 docx-render .hl block-level
   promotion rules lived here — unreachable since the overlay burn (no
   producer for either 'docx-render' or 'hl' classes). */

/* ── SOP Builder ─────────────────────────────────────────────────────────── */


.sop-field {
    margin-bottom: 10px;
}


.sop-field textarea,
.sop-field input[type="text"] {
    width: 100%;
    border: 1px solid var(--border);
    border-radius: 5px;
    padding: 7px 10px;
    font-size: 0.84rem;
    font-family: inherit;
    background: Canvas;
    color: CanvasText;
    resize: vertical;
    line-height: 1.4;
}
.sop-field textarea:focus,
.sop-field input[type="text"]:focus {
    outline: none;
    border-color: var(--primary-fg);
}


/* ── SOP Document Preview ────────────────────────────────────────────────── */


.sop-doc-step-text {
    color: light-dark(#222, var(--pq-ink));
}


/* ── SCR-004/008: Procedure table ───────────────────────────────────────── */


.sop-proc-step-text { vertical-align: top; }


/* ── Admin panel persona-model dropdown (T-046 / v3.12.4) ──
   Override the OS-native option styling so the open dropdown panel
   matches the closed select's font size. Without this, browsers fall
   back to a platform-default font that's visibly larger than 0.85rem
   and causes long model labels (e.g. "Google Gemini 2.5 Flash-Lite")
   to truncate inside the popup. */
.admin-persona-model-select { font-size: 0.85rem; font-family: inherit; }
.admin-persona-model-select option { font-size: 0.85rem; font-family: inherit; }

/* ── Admin panel tabs (T-060: file-tab style with two-row support + sticky) ── */
.admin-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 0;
    border-bottom: 2px solid var(--text-muted);   /* #1756 — a token, not a literal grey */
    margin: 0 -24px 18px;
    padding: 6px 24px 0;
    align-items: flex-end;
    position: sticky;
    top: 0;
    background: var(--surface, #fff);
    z-index: 5;
}
.admin-tab {
    flex: 0 0 auto;
    min-width: 0;
    text-align: center;
    padding: 7px 14px 8px;
    --admin-tab-line: var(--border);
    border: 1.5px solid var(--admin-tab-line);
    border-bottom: none;
    background: var(--bg, #f5f5f7);
    font-size: 0.82rem;
    font-weight: 500;
    color: var(--text-muted);
    cursor: grab;
    border-radius: 8px 8px 0 0;
    margin-bottom: 0;
    position: relative;
    transition: color 0.15s, background 0.15s, border-color 0.15s;
    white-space: nowrap;
    margin-left: -2px;
    z-index: 1;
}
.admin-tab:hover  {
    color: var(--primary-fg);
    background: var(--hover-bg);
    border-color: var(--primary-fg);
}
.admin-tab.active {
    color: var(--primary-fg);
    background: var(--bg-white, #fff);
    border: 2px solid var(--primary-fg);
    border-bottom: 2px solid var(--bg-white, #fff);
    font-weight: 700;
    z-index: 3;
    margin-bottom: -2px;
    cursor: default;          /* already selected (was a second rule for this selector) */
}
.admin-tab:first-child { margin-left: 0; }
/* #1756 — the row-based "front tab" and "tab behind" rules (#1547) are BURNED
   with the rows themselves: Settings is a navigation rail now (below),
   so no tab hangs and there is no row to put a tab in front of. The file-tab look
   above stays for the strips inside a Settings page (External Data's document
   lists). */
/* Being dragged: lifted, translucent, and above everything so it reads as the
   thing under the pointer rather than as a tab that has changed state. */
.admin-tab.is-dragging {
    opacity: 0.55;
    z-index: 9;
    cursor: grabbing;
}
/* Respect a reduced-motion preference: the tab strip has a colour transition and
   a drag; the transition is the part that can be turned off without losing
   meaning. */

/* end #406 tab strip */

.admin-tab-danger { color: var(--danger); }
.admin-tab-danger:hover { color: var(--danger); background: var(--hover-bg); }
.admin-tab-danger.active {
    color: var(--danger); background: var(--surface, #fff);
    border-color: var(--danger); border-bottom-color: var(--surface, #fff);
}

/* ── #1756 — SETTINGS IS A NAVIGATION RAIL, NOT A WRAPPING TAB STRIP ──────────
   Craig 2026-09-27: "Get a proper UI design on the tabs so they look much more
   modern and professional - check what other systems do". With 25 sections the
   file-tab strip wrapped to three rows, and a wrapped row of file tabs always
   leaves some tabs resting on nothing — no treatment of the rows fixes that
   (A, B and C all tried). What mature products do at this count: macOS System
   Settings, Windows Settings, VS Code, GitHub, Stripe, Linear and Notion all put
   the sections in a vertical list on the left and the page on the right; Office
   and browsers keep tabs only because they have few. So: a rail. It never wraps,
   so nothing can hang, and the section in front is simply the highlighted row.
   Same buttons, ids, order and drag-to-rearrange (#406) — only the layout moves.
   Theme tokens only, so light, dark and high-contrast all follow the system. */
#admin-modal .modal-body {
    display: grid;
    grid-template-columns: 220px minmax(0, 1fr);
    padding: 0;
    min-height: 0;
    overflow: hidden;           /* the rail and the page each scroll on their own */
}
#admin-modal #admin-tabs {
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    gap: 2px;
    margin: 0;
    padding: 12px 10px;
    border-bottom: 0;
    border-right: 1px solid var(--border);
    /* A slightly shaded panel in BOTH schemes (measured on v4.3.1273: --surface-2
       resolves to the page's own white, so the rail was not shaded at all). Mixed
       from tokens that flip with prefers-color-scheme, never a fixed tint. */
    background: color-mix(in oklab, var(--pq-ink) 4%, var(--pq-surface));
    position: static;
    overflow-y: auto;
    min-height: 0;
}
#admin-modal .admin-content {
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;         /* the page never scrolls sideways ... */
    padding: 16px 24px 24px;
}
/* ... a section whose content is wider than the page scrolls within itself
   (#1756: AI Models ran 21px over, measured on v4.3.1273, scrolling the whole
   page sideways). Contained per section, so the rest of the page stays put. */
#admin-modal .admin-content .opt-section { min-width: 0; overflow-x: auto; }
#admin-modal .admin-tab {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    margin: 0;
    padding: 7px 10px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--text);
    font-weight: 500;
    text-align: left;
    white-space: normal;
    line-height: 1.25;
}
#admin-modal .admin-tab .ic { flex: 0 0 auto; color: var(--text-muted); }
/* #1756 — hover and the active fill are MIXED from tokens that flip. --hover-bg
   and --accent-soft are a fixed pale blue in both schemes, which painted a
   near-white patch with pale text on the dark window (measured on v4.3.1273). */
#admin-modal .admin-tab:hover { background: color-mix(in oklab, var(--pq-ink) 8%, var(--pq-surface)); color: var(--text); }
#admin-modal .admin-tab.active {
    background: color-mix(in oklab, var(--pq-accent-2) 16%, var(--pq-surface));
    color: var(--primary-fg);
    font-weight: 600;
    box-shadow: inset 3px 0 0 var(--primary-fg);   /* the marker bar, as in the tools above */
    border: 0;
    margin: 0;
}
#admin-modal .admin-tab.active .ic { color: var(--primary-fg); }
#admin-modal .admin-tab:focus-visible { outline: 2px solid var(--primary-fg); outline-offset: -2px; }
#admin-modal .admin-tab-danger,
#admin-modal .admin-tab-danger .ic { color: var(--danger); }
#admin-modal .admin-tab-danger.active { box-shadow: inset 3px 0 0 var(--danger); }
@media (prefers-contrast: more) {
    #admin-modal .admin-tab.active { outline: 2px solid var(--primary-fg); outline-offset: -2px; }
}

/* ── Audit log table ── */
/* v3.9.41 — Fixed-layout column widths so the 8-column table stays
   inside the modal regardless of content. Earlier `width: max-content`
   let event names like `data.sensitive_read` and long Target/Detail
   strings push the table wider than the container, which surfaced as
   a horizontal scroll bar after T-091 added the in-`<th>` filter row.
   Percentages adapt to whatever modal width is available; long content
   wraps on word boundaries inside its column. */
.audit-table { width: 100%; table-layout: fixed; border-collapse: collapse; font-size: 0.78rem; }
/* v3.12.18 — Event column widened from 13% → 16% to fit long event names like
   `billing.tokens.expert_review.inspector` without overflowing into Category.
   Other Details slimmed 19% → 16% to compensate. Event cell uses
   `overflow-wrap: anywhere` so dots in dotted names act as wrap points. */
/* #720 — column 1 was the ONLY one of the eight with no wrap rule, and the
   Time cell also carried an inline `white-space:nowrap`. In a `table-layout:
   fixed` table an unwrappable 19-character timestamp cannot shrink, so it ran
   under the Event name. The 2% comes back off column 6 so the eight widths
   still sum to exactly 100 — and the width MUST be set here rather than on a
   cell, because the header and body are two separate <table> elements kept in
   column alignment SOLELY by these nth-child rules. */
.audit-table th:nth-child(1), .audit-table td:nth-child(1) { width: 14%; word-break: break-word; }   /* Time */
.audit-table th:nth-child(2), .audit-table td:nth-child(2) { width: 16%; overflow-wrap: anywhere; word-break: break-word; }   /* Event */
.audit-table th:nth-child(3), .audit-table td:nth-child(3) { width: 8%;  word-break: break-word; }   /* Category */
.audit-table th:nth-child(4), .audit-table td:nth-child(4) { width: 9%;  word-break: break-word; }   /* Severity */
.audit-table th:nth-child(5), .audit-table td:nth-child(5) { width: 11%; word-break: break-word; }   /* Actor */
.audit-table th:nth-child(6), .audit-table td:nth-child(6) { width: 17%; word-break: break-word; }   /* Target / Detail */
.audit-table th:nth-child(7), .audit-table td:nth-child(7) { width: 16%; word-break: break-word; }   /* Other Details */
.audit-table th:nth-child(8), .audit-table td:nth-child(8) { width: 9%;  word-break: break-word; }   /* Outcome */
/* #1756 — Severity and Outcome take 2% each from Target / Detail, still
   summing to 100, so their headers fit (see `.audit-table th` below). */
/* #2413 — Time takes 1% more from Target / Detail (13 -> 14, 18 -> 17), still
   summing to 100, so its time half ("03:34:08 PM") fits on its own line. */
/* #1756 — the events list fills the fixed Settings window instead of sitting
   in a 320px box with ~250px empty below it (measured on v4.3.1273). */
#admin-modal .admin-content:has(> #admin-tab-panel-audit:not(.hidden)) { display: flex; flex-direction: column; }
#admin-tab-panel-audit:not(.hidden) { flex: 1 0 auto; display: flex; flex-direction: column; }
#admin-tab-panel-audit > .opt-section { flex: 1 0 auto; display: flex; flex-direction: column; }
#admin-tab-panel-audit #audit-log-table { flex: 1 1 320px; min-height: 320px; }
/* T-074(17) v3.12.18 — header table sits OUTSIDE the body's scroll container
   so body rows physically cannot scroll into the header band. No sticky
   positioning, no opaque background workaround — clean structural separation.
   Per Craig: "Simply have the top of the scrolling list be below the header
   row, so it never scrolls that high." Backgrounds inherit from the
   surrounding panel so the original colour scheme reads correctly. */
.audit-table th {
    padding: 5px 8px;
    text-align: left;
    border-bottom: 1px solid var(--border);
    font-weight: 600;
}
/* #1756 — a HEADER never breaks inside a word ('Severit / y', 'Outcom / e',
   measured on v4.3.1273 once the rail narrowed the page). Same specificity as
   the per-column rules above and declared after them, so it overrides their
   word-break for headers only; body cells keep wrapping. */
.audit-table th:nth-child(n) { word-break: normal; overflow-wrap: normal; }
/* Body table: bottom rule under each row, no extra column-header styling. */
.audit-table td { padding: 4px 8px; border-bottom: 1px solid var(--border-subtle, #eee); vertical-align: top; }
.audit-table tr:hover td { background: var(--hover-bg); }
/* #2106 — one class per TONE of audit_log.AUDIT_OUTCOMES: success = ok; warning
   and refused = warn (a refusal is a warning, not a failure); failure = bad;
   running = neutral. light-dark() follows the reader's scheme. */
.audit-outcome-ok      { color: light-dark(#276749, color-mix(in oklab, #276749 45%, white)); font-weight: 600; }
.audit-outcome-bad     { color: light-dark(#c53030, color-mix(in oklab, #c53030 45%, white)); font-weight: 600; }
.audit-outcome-warn    { color: light-dark(#975a16, color-mix(in oklab, #975a16 45%, white)); font-weight: 600; }
.audit-outcome-neutral { color: light-dark(#4a5568, var(--pq-ink-muted)); font-weight: 600; }
.audit-security-event { color: light-dark(#b7620a, color-mix(in oklab, #b7620a 45%, white)); font-weight: 700; }
.audit-table tr.audit-security-row td { background: light-dark(#fff8f0, var(--pq-elevated)); }
.audit-table tr.audit-security-row:hover td { background: light-dark(#feebd4, color-mix(in oklab, #feebd4 14%, var(--pq-surface))); }

/* v3.7 audit-rebuild — severity pills */
.audit-sev-pill {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 10px;
    font-size: 0.70rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    line-height: 1.4;
}
.audit-sev-info     { background: light-dark(#e3f0fb, color-mix(in oklab, #e3f0fb 14%, var(--pq-surface))); color: light-dark(#1c6ec0, var(--pq-accent)); }
.audit-sev-warn     { background: light-dark(#fff3cd, color-mix(in oklab, #fff3cd 14%, var(--pq-surface))); color: light-dark(#946100, color-mix(in oklab, #946100 45%, white)); }
.audit-sev-security { background: light-dark(#fde2d8, color-mix(in oklab, #fde2d8 14%, var(--pq-surface))); color: light-dark(#b7400a, color-mix(in oklab, #b7400a 45%, white)); }
.audit-sev-critical { background: light-dark(#fcd7d4, color-mix(in oklab, #fcd7d4 14%, var(--pq-surface))); color: light-dark(#a01818, color-mix(in oklab, #a01818 45%, white)); font-weight: 700; }

/* Clipped audit cell; its full value is shown by the shared RCTip element. */
.audit-td-clip {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    position: relative;
    cursor: default;
}
/* #1444 — the cell tooltip's `::after` is DELETED. It drew `content:
   attr(data-tip)` with its own colours, delay and max-width — a THIRD tooltip
   implementation beside RCTip and the palette's (which #1443 retired). These
   cells now carry `data-rc-tip` and RCTip draws them, so the app has one
   tooltip and one set of styling for it.

   The `.audit-td-clip` rule above stays: the ellipsis clipping is what makes a
   tooltip necessary here and is a different concern from drawing one. */

/* T-039: row-level double-click affordance. The multi-line row summary now
   uses the shared RCTip element via data-rc-tip, so it can wrap and escape
   this scroll container. */
tr.audit-row-clickable {
    position: relative;
    cursor: default;
}
/* #1444 — the row tooltip's `::after` is DELETED, and it carried a live defect
   worth recording. The selector `tr.audit-row-clickable[data-tip]:hover::after`
   was declared TWICE: once with `opacity: 0` plus
   `transition: opacity 0.15s 0.6s`, and again immediately below with
   `opacity: 1`. Same selector, same specificity, so the later one won.

   The cell tooltip's pattern that this was copied from works because its BASE
   rule is not `:hover` — the pseudo-element exists at opacity 0 and transitions
   in. Here both rules were `:hover`, so the pseudo-element came into existence
   already opaque and THE 0.6s DELAY NEVER HAPPENED.

   That delay was the whole mechanism: the comment above said the row tooltip
   "appears on row hover with a longer delay so it doesn't fight cell tooltips".
   It was fighting them from the first frame.

   RCTip replaces the delay hack with a structural answer — it walks UP from the
   hovered element and takes the NEAREST tooltip, so a clipped cell shows its own
   and only an untipped cell falls through to the row summary. Two tooltips can no
   longer be on screen at once, by construction rather than by timing. */

tr.audit-row-clickable:hover {
    background: var(--hover-bg);
}

.audit-detail-row {
    display: grid;
    grid-template-columns: 140px 1fr;
    gap: 12px;
    padding: 6px 0;
    border-bottom: 1px solid var(--border-subtle, #eee);
    font-size: 0.85rem;
}
.audit-detail-row:last-child { border-bottom: none; }
.audit-detail-label {
    color: var(--text-muted, #6b7280);
    font-weight: 600;
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.audit-detail-value {
    word-break: break-word;
}
.audit-detail-code {
    background: light-dark(#f7fafc, var(--pq-elevated));
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 8px 10px;
    margin: 0;
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: 0.75rem;
    line-height: 1.5;
    white-space: pre-wrap;
    word-break: break-word;
    max-height: 200px;
    overflow-y: auto;
}


/* ════════════════════════════════════════════════════════════════════════════
   QMS Access Panel
   ════════════════════════════════════════════════════════════════════════════ */

/* Layout, rail, toolbar and buttons: see "#1938 · OPTION 1 REVISED" below. */

.qma-empty {
    color: var(--text-muted, #718096);
    font-size: 0.9rem;
    padding: 40px 0;
    text-align: center;
}
/* #726/#648 (2026-08-17) — THE ACTIONS COLUMN IS GONE (Craig: "just using
 * speace and untidy"); git holds its CSS. The table itself is styled in the
 * "#1938 · OPTION 1 REVISED" block below. */

/* Type badges — #1938: quiet and neutral, as the artboard draws them (calm
   colour throughout); the level classes stay so a level can be styled again. */
.qma-level-badge {
    display: inline-block; padding: 3px 6px; border-radius: 4px;
    font: 500 11px/1 var(--mono); color: var(--text-muted);
    border: 1px solid var(--border); background: transparent;
}

/* Status — #1938: soft pills. Effective green, Draft neutral, review due amber,
   warning red; each a pair defined for light and dark. */
.qma-status-badge {
    display: inline-block; padding: 2px 9px; border-radius: 999px;
    font-size: 12px; font-weight: 500; white-space: nowrap;
}
.qma-status-draft    { background: var(--pq-soft); color: var(--text-muted); border: 1px solid var(--border); }
.qma-status-review   { background: light-dark(#f6ecd9, #3a2f1a); color: light-dark(#8a5a0b, #e7c27d); }
.qma-status-live     { background: light-dark(#e6f1ea, #1f3328); color: light-dark(#2d6b45, #8fd0a8); }
.qma-status-warn     { background: light-dark(#f8e5e2, #3a1d1d); color: light-dark(#9a2f22, #f2a99e); }
.qma-status-inactive { background: var(--pq-soft); color: var(--text-muted); }


/* Detail drawer */
.qma-detail-drawer {
    position: absolute;
    top: 0; right: 0; bottom: 0;
    width: 520px;
    background: var(--bg-white);
    border-left: 2px solid var(--primary, #1a365d);
    display: flex;
    flex-direction: column;
    box-shadow: -4px 0 18px rgba(0,0,0,0.1);
    z-index: 10;
}


/* Form */

.qma-form-group { display: flex; flex-direction: column; gap: 3px; }
.qma-form-group label { font-size: 0.75rem; font-weight: 600; color: var(--text-muted, #555); }

.qma-input {
    padding: 6px 8px;
    border: 1px solid var(--border, #ccc);
    border-radius: 4px;
    font-size: 0.83rem;
    font-family: inherit;
    background: var(--bg-white);
    width: 100%;
    box-sizing: border-box;
}
.qma-input:focus { outline: 2px solid var(--primary, #1a365d); border-color: transparent; }
.qma-ta { resize: vertical; min-height: 56px; }


/* Uniform action buttons (Craig 2026-05-22): same padding + a shared min-width
   so the row reads as one consistent set regardless of label length
   (Metadata / Edit / Save / Delete), instead of each button hugging its text. */

.qma-detail-error {
    color: light-dark(#c53030, color-mix(in oklab, #c53030 45%, white));
    font-size: 0.82rem;
    margin-top: 8px;
    padding: 6px 10px;
    background: light-dark(#fff5f5, var(--pq-elevated));
    border-radius: 4px;
    border: 1px solid #fc8181;
}

/* Relationships */
.qma-rel-add {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--border, #eee);
    margin-bottom: 12px;
}
.qma-rel-list, .qma-rev-list { display: flex; flex-direction: column; gap: 6px; }

.qma-rel-empty {
    color: var(--text-muted, #718096);
    font-size: 0.82rem;
    padding: 12px 0;
}
.qma-rel-item {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    padding: 8px 10px;
    background: var(--pq-soft);
    border-radius: 5px;
    font-size: 0.82rem;
    gap: 8px;
}
.qma-rel-info     { display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline; }
.qma-rel-direction { font-weight: 700; color: var(--primary-ink, #1a365d); }
.qma-rel-type      { font-weight: 600; background: light-dark(#dbeafe, color-mix(in oklab, #dbeafe 14%, var(--pq-surface))); color: light-dark(#1e40af, var(--pq-accent)); padding: 1px 5px; border-radius: 3px; font-size: 0.74rem; }
.qma-rel-target    { color: var(--text); }
.qma-rel-rationale { color: var(--text-muted, #718096); font-style: italic; }
.qma-rel-delete {
    background: none;
    border: none;
    color: var(--text-muted, #999);
    cursor: pointer;
    font-size: 0.85rem;
    flex-shrink: 0;
}
.qma-rel-delete:hover { color: #e53e3e; }

/* Reviews */
.qma-rev-item {
    padding: 10px 12px;
    background: light-dark(#f9fafb, var(--pq-elevated));
    border-radius: 5px;
    border-left: 3px solid var(--primary, #1a365d);
    font-size: 0.82rem;
    margin-bottom: 6px;
}
.qma-rev-header   { display: flex; gap: 10px; align-items: center; margin-bottom: 4px; }
.qma-rev-trigger  { font-weight: 600; color: var(--primary-ink, #1a365d); }
.qma-rev-status   { padding: 2px 7px; border-radius: 10px; font-size: 0.72rem; font-weight: 600; background: light-dark(#e5e7eb, var(--pq-line)); color: light-dark(#374151, var(--pq-ink)); }
.qma-rev-date     { color: var(--text-muted, #718096); margin-left: auto; font-size: 0.78rem; }
.qma-rev-notes    { color: var(--text-muted, #555); margin-bottom: 4px; }
.qma-rev-conclusion { font-size: 0.8rem; }
.qma-rev-actions  { display: flex; gap: 8px; margin-top: 6px; }

/* Tool panels */
.qma-tool-panel {
    flex: 1;
    overflow-y: auto;
    padding: 18px;
}
.qma-tool-header {
    display: flex;
    align-items: center;
    gap: 12px;
    font-size: 1rem;
    font-weight: 700;
    color: var(--primary-ink, #1a365d);
    margin-bottom: 16px;
}

/* Integrity */
.qma-int-ok      { color: light-dark(#276749, color-mix(in oklab, #276749 45%, white)); font-weight: 600; font-size: 0.9rem; padding: 12px; background: light-dark(#f0fff4, var(--pq-elevated)); border-radius: 5px; }
.qma-int-summary { font-size: 0.85rem; color: var(--text-muted, #555); margin-bottom: 12px; }
.qma-int-issue {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 8px 12px;
    border-radius: 5px;
    font-size: 0.83rem;
    margin-bottom: 6px;
}
.qma-int-error   { background: light-dark(#fff5f5, var(--pq-elevated)); border-left: 4px solid #e53e3e; }
.qma-int-warning { background: light-dark(#fffbeb, color-mix(in oklab, #fffbeb 14%, var(--pq-surface))); border-left: 4px solid #d69e2e; }
.qma-int-badge   { font-weight: 700; font-size: 0.72rem; min-width: 52px; }
.qma-int-code    { font-family: monospace; color: var(--text-muted, #666); font-size: 0.78rem; }

/* Import drop zone */
.qma-import-label {
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.qma-import-banner {
    padding: 8px 16px;
    background: light-dark(#ebf8ff, color-mix(in oklab, #ebf8ff 14%, var(--pq-surface)));
    border-bottom: 1px solid light-dark(#bee3f8, color-mix(in oklab, #bee3f8 30%, var(--pq-surface)));
    font-size: 0.82rem;
    color: light-dark(#2b6cb0, var(--pq-accent));
}
.qma-import-banner.hidden { display: none; }


/* #1590 — `.qma-ctx-note` REMOVED, with its dark-theme partner further down this
   file. It was #725's block explanation of why menu entries are greyed; Craig
   ruled the block-level caution panel out three times (#1294, #1277, #1590) and
   the reason it carried is already on each disabled row's own `title=`. Do not
   reintroduce a rule pairing `--btn-caution-bg` with explanatory prose on a
   block element. */


/* ── #1577 — the library's folder hierarchy (QMS-DS-001 §4.26) ─────────────── */
/* #1922 — THE ONE TREE (Folders and Structure), the Finder list view. The indent
   is the WIDTH of its own element in the row's first cell, driven by the depth
   the element carries: arbitrary depth (§4.26.1) rules out a class per level,
   and it must not be cell padding — ui_theme.css's compact-density rule
   (`:root:not([data-dens="comfortable"]) .qma-table td`, specificity 0,3,1)
   resets every cell's padding and silently erased the #1577 indent. A faint
   guide line per level marks where a folder's contents end in a wide table. */
.qma-tree-indent {
    display: inline-block;
    width: calc(var(--qma-depth, 0) * 18px);
    height: 1.4em;
    vertical-align: middle;
    background-image: repeating-linear-gradient(to right,
        transparent 0 8px, var(--border, #e5e7eb) 8px 9px, transparent 9px 18px);
}
.qma-tree-caret {
    display: inline-flex; align-items: center; justify-content: center;
    width: 20px; height: 20px; padding: 0; border: 0; border-radius: 4px;
    background: none; color: var(--text-muted, #6b7280); cursor: pointer;
    vertical-align: middle;
}
.qma-tree-caret:hover, .qma-tree-caret:focus-visible { color: var(--text, #111827); background: var(--bg-subtle, #f4f5f7); }
.qma-tree-caret.is-spacer { cursor: default; background: none; }
.qma-cell-number, .qma-tree-cell { white-space: nowrap; }
/* #1938 — the Folders view's folder rows are gone with the view; the folders are
   the rail (.qms-rail-folder). */
/* The drop target under the pointer while a document or folder is dragged. */
.qma-row.is-drop-target > td {
    background: var(--primary-pale, #f0f7ff);
    box-shadow: inset 0 0 0 2px var(--primary-ink, #1d4ed8);
}
/* #2119 — the small picture under the pointer while dragging (setDragImage): an
   icon and the number, so the folder or group being dropped on stays visible. It
   must be RENDERED for the browser to snapshot it, so it is parked off-screen
   rather than hidden. */
.qma-drag-ghost {
    position: fixed; top: -1000px; left: -1000px;
    display: inline-flex; align-items: center; gap: 6px;
    max-width: 220px; padding: 4px 8px;
    font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    color: var(--text); background: var(--surface);
    border: 1px solid var(--border); border-radius: 6px;
    pointer-events: none;
}
.qma-drag-ghost .ic { width: 14px; height: 14px; flex: none; }
.qma-row[draggable="true"] { cursor: grab; }
/* The folder picker (#folder-pick-modal): the same tree, one button per folder. */
.qma-pick-folder {
    display: flex; align-items: center; gap: 6px; width: 100%;
    padding: 4px 6px; border: 1px solid transparent; border-radius: 6px;
    background: none; color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.qma-pick-folder:hover, .qma-pick-folder:focus-visible { border-color: var(--border, #e5e7eb); }
.qma-pick-folder[aria-selected="true"] { background: var(--primary-pale, #f0f7ff); border-color: var(--primary-ink, #1d4ed8); }
.qma-pick-folder.is-current { color: var(--text-muted, #6b7280); cursor: default; }
.qma-pick-here { font-size: 0.78rem; }
.qma-folder-count {
    min-width: 1.5rem;
    padding: 0 6px;
    border-radius: 999px;
    background: var(--border, #e5e7eb);
    color: var(--text-muted, #6b7280);
    font-size: 0.72rem;
    text-align: center;
}
.qma-icon-btn {
    padding: 3px 5px;
    border: 1px solid transparent;
    border-radius: 6px;
    background: none;
    color: var(--text-muted, #6b7280);
    cursor: pointer;
}
.qma-icon-btn:hover { border-color: var(--border, #e5e7eb); color: var(--text, #111827); }
.qma-icon-btn.danger:hover { border-color: var(--danger, #b91c1c); color: var(--danger, #b91c1c); }

/* ── #112 — the Structure view, Used by and the links panel (QMS-DS-001 §4.28) ── */
/* Colours are theme TOKENS only, so dark mode and the per-person palettes need no
   second rule here. The indent is the folder hierarchy's own declaration above. */
.qma-row.qma-struct-selected > td { background: var(--primary-pale); }
.qma-row.qma-struct-selected > td:first-child { box-shadow: inset 3px 0 0 var(--primary-ink); }
.qma-struct-band > td { background: var(--pq-soft); font-size: 13px; font-weight: 600; }
.qma-struct-band-note { color: var(--text-muted); font-size: 0.78rem; margin-left: 8px; }
.qma-struct-note > td { color: var(--text-muted); font-size: 0.82rem; }
/* Craig 2026-10-07: "the cols are still not showing properly, running off the page to
   the right. The space given for the version is many times what is required."
   Measured (v4.3.1601): a full-width note row ("504.1_Ad Hoc Data Request Form — not
   in the library yet ...") inherited .qma-tree-cell's nowrap, held the table at
   1,421 px against a 994-1,130 px pane, and auto layout shared the excess across
   every column. Full-width rows wrap; the short columns take their content width,
   and the slack goes to Document. */
.qma-table .qma-struct-note > td, .qma-table .qma-struct-band > td { white-space: normal; }
.qma-table th:is([data-col="number"], [data-col="type"], [data-col="status"], [data-col="version"],
                [data-col="effective"], [data-col="groups"], [data-col="usedby"]) { width: 1%; }
/* The heading filters and the Analysis cell no longer set their column's floor:
   a filter is as wide as its column (Level's options made Type 155 px for a
   120 px badge), and "Not analysed / no process" may take two lines. */
.qma-table thead .qma-th-filter { width: 100%; min-width: 0; box-sizing: border-box; }
.qma-table td[data-col="analysis"] { white-space: normal; }
.qma-struct-dashed { display: inline-block; padding: 1px 8px; border: 1px dashed var(--border); border-radius: 6px; }
.qma-struct-clause { font-family: monospace; font-size: 0.75rem; color: var(--text-muted); }
.qma-usedby-btn, .qma-link-btn { padding: 1px 6px; border: 1px solid transparent; border-radius: 6px; background: none;
    color: var(--primary-ink); font: inherit; font-size: 0.82rem; cursor: pointer; }
.qma-usedby-btn:hover, .qma-link-btn:hover { border-color: var(--border); }
.qma-usedby-btn.is-none { color: var(--text-muted); }
.qma-struct-panel { position: fixed; top: 96px; right: 16px; bottom: 16px; width: min(380px, 92vw); overflow: auto;
    padding: 12px 14px; background: var(--bg-white); border: 1px solid var(--border); border-radius: 10px;
    box-shadow: 0 6px 22px rgba(0,0,0,0.14); z-index: var(--z-sticky); }
.qma-struct-panel-hd { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.qma-struct-panel h4 { margin: 14px 0 4px; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); }
.qma-struct-panel ul { margin: 0; padding-left: 16px; }
.qma-struct-state, .qma-struct-muted { color: var(--text-muted); font-size: 0.75rem; }
.qma-struct-applies { margin: 2px 0 6px; font-size: 0.78rem; color: var(--text-muted); }
@media (prefers-reduced-motion: no-preference) { .qma-struct-panel { transition: opacity .12s; } }


/* ── T-170 — parent-child version-aware rows ───────────────────────────────── */
/* Child (in-progress Draft) + history rows are visually subordinate to their
   parent: a hairline left rail, a softened background, and an indented title. */
.qma-row-child > td,
.qma-row-history > td {
    background: var(--pq-soft);   /* #1938 — neutral; the light blue is selection only */
    border-top: 0;
}
.qma-cell-indent { padding-left: 22px !important; }
.qma-child-rail {
    color: var(--text-muted, #9ca3af);
    font-family: monospace;
    margin-right: 4px;
}
.qma-child-tag {
    font-weight: 600;
    font-size: 0.78rem;
    color: var(--primary-ink, #1a365d);
}
.qma-hist-tag {
    font-weight: 500;
    font-size: 0.78rem;
    color: var(--text-muted, #6b7280);
    font-style: italic;
}
/* The collapsible history header row (the "N earlier versions" toggle). */
.qma-row-history-toggle > td { background: transparent; }
.qma-hist-toggle-label {
    padding-left: 22px !important;
    color: var(--text-muted, #6b7280);
    font-size: 0.78rem;
    font-weight: 500;
    cursor: pointer;
    user-select: none;
}
.qma-hist-toggle-label:hover { color: var(--primary-ink, #1a365d); }
.qma-hist-caret { display: inline-block; vertical-align: -2px; margin-right: 4px; }
.qma-hist-caret .ic { width: 13px; height: 13px; }

/* ── T-170 — QMS list Settings menu ────────────────────────────────────────── */
.qma-settings-wrap { position: relative; display: inline-block; }
.qma-settings-menu {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    min-width: 260px;
    background: var(--bg-white);
    border: 1px solid var(--border, #d1d5db);
    border-radius: 8px;
    box-shadow: 0 6px 22px rgba(0,0,0,0.14);
    padding: 8px;
    z-index: 30;
}
.qma-settings-menu-title {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted, #6b7280);
    padding: 4px 8px 6px;
}
.qma-settings-menu-item {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 7px 8px;
    border-radius: 5px;
    font-size: 0.82rem;
    cursor: pointer;
}
.qma-settings-menu-item:hover { background: var(--hover-bg); }
.qma-settings-menu-item input[type="checkbox"] { margin: 0; cursor: pointer; }

@media (prefers-color-scheme: dark) {

    
    /* #1590 — `.qma-ctx-note`'s dark border rule removed with the class itself. */
    /* #1577 — only the two surfaces that name a literal need a dark value; the
       rest of the folder rules already read their colours from tokens that the
       theme block redefines. One definition, no second palette to drift. */
    .qma-folder-count { background: var(--border, #374151); color: var(--fg, #d1d5db); }
    
    
    .qma-row-child > td, .qma-row-history > td { background: rgba(255,255,255,0.03); }
    .qma-child-tag { color: var(--primary-fg); }   /* B-230 — was --primary-light: a mid-tone navy, invisible on the dark surface */
    .qma-settings-menu { background: var(--surface, #1f2937); border-color: var(--border, #374151); }
    .qma-settings-menu-item:hover { background: rgba(255,255,255,0.06); }
}

/* Document taxonomy popup (from graph right-click) */
.qma-doc-tax-popup {
    position: fixed;
    top: 80px;
    right: 20px;
    background: var(--bg-white);
    border: 1px solid var(--border, #ddd);
    border-radius: 8px;
    box-shadow: 0 6px 24px rgba(0,0,0,0.18);
    z-index: 9998;
    width: 360px;
    max-height: 60vh;
    display: flex;
    flex-direction: column;
}
.qma-doc-tax-popup.hidden { display: none; }
.qma-doc-tax-popup-hdr {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 16px;
    background: var(--primary, #1a3a5c);
    color: #fff;
    font-weight: 600;
    font-size: 0.88rem;
    border-radius: 8px 8px 0 0;
    flex-shrink: 0;
}
.qma-doc-tax-popup-body {
    padding: 16px;
    overflow-y: auto;
    flex: 1;
}
.qma-doc-tax-popup-section { margin-bottom: 14px; }
.qma-doc-tax-popup-label {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--primary-ink, #1a3a5c);
    margin-bottom: 6px;
}
.qma-doc-tax-popup-chips { display: flex; flex-wrap: wrap; gap: 5px; }
.qma-doc-tax-popup-chip {
    background: var(--primary-pale, #eef3f9);
    border: 1px solid light-dark(#b8cfe8, color-mix(in oklab, #b8cfe8 30%, var(--pq-surface)));
    color: var(--primary-ink, #1a3a5c);
    border-radius: 10px;
    padding: 2px 9px;
    font-size: 0.78rem;
}
.qma-doc-tax-popup-empty { font-size: 0.8rem; color: var(--text-muted); font-style: italic; }

/* Graph view */
.qma-graph-panel {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding: 18px;
}
.qma-graph-panel.hidden { display: none; }
.qma-graph-controls { display: flex; gap: 8px; margin-left: auto; }
.qma-graph-body {
    flex: 1;
    display: flex;
    gap: 12px;
    overflow: hidden;
    min-height: 0;
}
#qma-graph-canvas {
    flex: 1;
    border: 1px solid var(--border, #ddd);
    border-radius: 6px;
    background: light-dark(#fafafa, var(--pq-elevated));
    min-height: 400px;
}
.qma-graph-legend {
    width: 160px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 16px;
    overflow-y: auto;
    font-size: 0.78rem;
}
.qma-legend-title {
    font-weight: 700;
    font-size: 0.72rem;
    text-transform: uppercase;
    color: var(--text-muted, #666);
    margin-bottom: 6px;
    letter-spacing: 0.04em;
}
.qma-legend-item {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 4px;
    color: var(--text, #333);
}
.qma-legend-node {
    display: inline-block;
    width: 14px;
    height: 14px;
    border-radius: 2px;
    flex-shrink: 0;
}
.qma-legend-edge {
    display: inline-block;
    width: 22px;
    height: 3px;
    border-radius: 2px;
    flex-shrink: 0;
}

/* Taxonomy — chip-based UI */
.qma-tax-rebuild-btn      { font-size: 0.78rem; margin-left: auto; }
.qma-tax-section          { margin-bottom: 20px; }
/* #770 — a tag the organisation has not yet agreed. A WARNING, not an error:
   the tag is correct (the document says it), it simply does not filter yet.
   Uses the palette's OWN warning pair, `--gap-bg` / `--gap-text` — the same two
   `.rc-tell-warn` uses for a refusal, which is the identical semantic. I had
   invented `--warn-fg`/`--warn-bg`/`--warn-border`, which are declared nowhere;
   a var() with no definition is invalid at computed-value time and the
   declaration is dropped. The palette contract test caught it. Using declared
   tokens also deletes the bespoke dark-mode blocks — they theme themselves. */
.rc-mw-provisional        { color: var(--gap-text); }

/* ── #770 · the org taxonomy governance inbox ──────────────────────────────
   Pending vocabulary decisions, above the settled vocabulary, because a
   decision waiting is what needs attention and the agreed set is reference. */
.qma-tax-inbox            { margin: 0 0 18px; padding: 12px 14px;
                            border: 1px solid var(--gap-text);
                            border-left-width: 4px; border-radius: 6px;
                            background: var(--gap-bg); }
.qma-tax-inbox-hdr        { display: flex; align-items: center; gap: 6px;
                            font-size: 0.82rem; font-weight: 700;
                            color: var(--gap-text); margin-bottom: 4px; }
.qma-tax-inbox-row        { display: flex; flex-wrap: wrap; align-items: center;
                            gap: 8px 12px; padding: 8px 0;
                            border-top: 1px solid var(--border, #e3e8ee); }
.qma-tax-inbox-row:first-of-type { border-top: 0; }
.qma-tax-inbox-term       { display: flex; align-items: center; gap: 6px; }
.qma-tax-inbox-kind       { font-size: 0.7rem; text-transform: uppercase;
                            letter-spacing: 0.04em; color: var(--text-muted); }   /* #1880 — was opacity 0.7 */
/* The provenance takes the free space, because it is what the decision rests
   on: four documents is a gap in the vocabulary, one is probably a typo. */
.qma-tax-inbox-prov       { flex: 1 1 220px; font-size: 0.78rem; color: var(--text-muted); }   /* #1880 — was opacity 0.85 */
.qma-tax-inbox-actions    { display: flex; gap: 6px; flex-wrap: wrap; }
/* #1736 — a provisional term no document uses: greyed, with the day it retires. */
/* #1880 — GREYED BY COLOUR, NOT BY FADING: opacity 0.55 took 'provisional' to 2.15
   and the kind label (0.7 inside it) to 2.36. Muted ink and italics still read as set aside. */
.qma-tax-inbox-row.is-unreferenced .qma-tax-inbox-term,
.qma-tax-inbox-row.is-unreferenced .qma-tax-inbox-prov { color: var(--text-muted); font-style: italic; }
.qma-tax-inbox-gone       { flex-basis: 100%; display: flex; align-items: center; gap: 6px;
                            font-size: 0.78rem; color: var(--text-muted); }
/* #1736 — confirmed terms no document uses, kept until pruned. */
.qma-tax-unused           { margin: 0 0 18px; padding: 12px 14px; border: 1px solid var(--border);
                            border-radius: 6px; background: var(--bg-white); }
.qma-tax-unused-hdr       { font-size: 0.82rem; font-weight: 700; color: var(--text); margin-bottom: 4px; }
.qma-tax-unused-row       { display: flex; align-items: center; gap: 8px 12px; padding: 6px 0;
                            border-top: 1px solid var(--border); }
.qma-tax-unused-row .btn-sem { margin-left: auto; }
/* Greyed, never dashed: a confirmed term, and the dash means provisional (#1809). */
.qma-tax-chip2.is-unused  { opacity: 0.7; }
/* #1717 — Confirm all sits at the header's far end, seen before the list. */
.qma-tax-inbox-hdr .qma-tax-confirm-all { margin-left: auto; font-weight: 600; }
/* #1732 — the suggested term is the button that maps onto it. A link in look,
   in the row's own colour, so it reads in both schemes. */
.qma-tax-sugg-link        { font: inherit; font-weight: 700; color: inherit; background: none;
                            border: 0; padding: 0 1px; cursor: pointer;
                            text-decoration: underline; text-underline-offset: 2px; }
.qma-tax-sugg-link:hover  { text-decoration-thickness: 2px; }
.qma-tax-sugg-link:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; border-radius: 2px; }
.qma-tax-sugg-link:disabled { opacity: 0.5; cursor: progress; }

/* #1732 — the Same as… window. The list takes the height the fixed window
   leaves, so filtering never resizes it under the reader. */
.qma-tax-map-body         { display: flex; flex-direction: column; gap: 10px; min-height: 0; overflow: hidden; }
.qma-tax-map-intro        { margin: 0; font-size: 0.85rem; color: var(--text); }
.qma-tax-map-searchrow    { display: flex; align-items: center; gap: 10px; }
.qma-tax-map-searchrow .qma-input { flex: 1; font-size: 0.9rem; padding: 8px 10px; color: var(--text); }
.qma-tax-map-count        { font-size: 0.75rem; color: var(--text-muted); white-space: nowrap; }
.qma-tax-map-list         { flex: 1; min-height: 0; overflow-y: auto; border: 1px solid var(--border);
                            border-radius: 6px; background: var(--bg-white); }
.qma-tax-map-row          { display: flex; align-items: center; gap: 8px; width: 100%;
                            padding: 7px 12px; border: 0; border-bottom: 1px solid var(--border);
                            background: none; color: var(--text); font: inherit; font-size: 0.86rem;
                            text-align: left; cursor: pointer; }
.qma-tax-map-row:last-child { border-bottom: 0; }
.qma-tax-map-row:hover    { background: var(--hover-bg); }
.qma-tax-map-row.is-selected { background: var(--btn-primary); color: #fff; }
.qma-tax-map-row.is-selected .qma-tax-map-count,
.qma-tax-map-row.is-selected .qma-tax-map-hint { color: #fff; border-color: light-dark(#fff, var(--pq-border)); }
.qma-tax-map-row:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.qma-tax-map-name         { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.qma-tax-map-hint         { font-size: 0.7rem; font-weight: 600; padding: 1px 6px; border-radius: 3px;
                            border: 1px solid var(--gap-text); color: var(--gap-text); white-space: nowrap; }
.qma-tax-map-row.is-suggested:not(.is-selected) { background: var(--gap-bg); }
.qma-tax-map-empty        { margin: 0; padding: 14px 12px; font-size: 0.85rem; color: var(--text-muted); }
.qma-tax-map-outcome      { font-size: 0.82rem; color: var(--text-muted); min-width: 0; }
.qma-tax-map-btns         { display: flex; gap: 8px; flex-shrink: 0; }
@media (prefers-contrast: more) {
    .qma-tax-map-row, .qma-tax-map-list { border-color: var(--text); }
}

/* #1674 — the What came back window. */
#pqrj-return-body         { font-size: 0.88rem; color: var(--text); }
.pqrj-lead                { margin: 0 0 10px; font-weight: 600; }
.pqrj-h                   { margin: 16px 0 6px; font-size: 0.85rem; font-weight: 700; }
.pqrj-list                { margin: 0; padding-left: 20px; }
.pqrj-list li             { margin: 3px 0; }
.pqrj-muted               { color: var(--text-muted); font-size: 0.8rem; margin: 6px 0 0; }
.pqrj-props               { display: flex; flex-direction: column; gap: 6px; }
.pqrj-prop                { display: flex; gap: 8px; align-items: flex-start; padding: 6px 8px;
                            border: 1px solid var(--border); border-radius: 6px; cursor: pointer; }
.pqrj-prop input          { margin-top: 3px; }
.pqrj-ftext               { display: block; }
.pqrj-flag                { font-size: 0.7rem; font-weight: 600; padding: 0 5px; border-radius: var(--radius-xs);
                            border: 1px solid var(--gap-text); color: var(--gap-text); }
.pqrj-source              { display: flex; align-items: center; gap: 8px; font-size: 0.8rem;
                            color: var(--text-muted); min-width: 0; }
.pqrj-source input        { width: 160px; color: var(--text); }

.qma-tax-section-hdr      { font-size: 0.78rem; font-weight: 700; color: var(--primary-ink, #1a365d);
                             text-transform: uppercase; letter-spacing: 0.04em;
                             margin-bottom: 8px; display: flex; align-items: center; gap: 8px; }
.qma-tax-auto-badge       { font-size: 0.68rem; font-weight: 500; background: light-dark(#e8f4e8, var(--pq-elevated));
                             color: light-dark(#276749, color-mix(in oklab, #276749 45%, white)); border-radius: 3px; padding: 1px 6px;
                             text-transform: none; letter-spacing: 0; }
.qma-tax-chips-row        { display: flex; flex-wrap: wrap; gap: 6px; }
.qma-tax-empty-hint       { font-size: 0.8rem; color: var(--text-muted); font-style: italic; }
.qma-tax-chip2            { display: inline-flex; align-items: center; gap: 4px;
                             background: var(--primary-pale, #eef3f9);
                             border: 1px solid light-dark(#b8cfe8, color-mix(in oklab, #b8cfe8 30%, var(--pq-surface)));
                             color: var(--primary-ink, #1a3a5c);
                             border-radius: 14px; padding: 3px 10px 3px 12px;
                             font-size: 0.82rem; cursor: pointer;
                             transition: background 0.12s, border-color 0.12s; }
.qma-tax-chip2:hover      { background: light-dark(#ddeaf8, color-mix(in oklab, #ddeaf8 14%, var(--pq-surface))); border-color: var(--primary, var(--primary-fg)); }
.qma-tax-chip2.active     { background: var(--primary, #1a3a5c); color: #fff; }
.qma-tax-chip2.active .qma-tax-chip-x { color: rgba(255,255,255,0.7); }
.qma-tax-count            { background: var(--primary, #1a3a5c); color: #fff;
                             border-radius: 10px; font-size: 0.68rem;
                             padding: 0 5px; min-width: 16px; text-align: center; }
.qma-tax-chip2:hover .qma-tax-count  { background: var(--bg-white); color: var(--primary-ink, #1a3a5c); }
.qma-tax-chip2.active .qma-tax-count { background: rgba(255,255,255,0.3); color: #fff; }
.qma-tax-chip-x           { background: none; border: none; cursor: pointer; padding: 0 1px;
                             font-size: 0.75rem; color: var(--text-muted); line-height: 1; }
.qma-tax-chip-x:hover     { color: light-dark(#c53030, color-mix(in oklab, #c53030 45%, white)); }
.qma-tax-add-row          { display: flex; gap: 6px; margin-top: 16px; align-items: center; }
.qma-tax-add-select       { flex: 0 0 auto; width: 140px; font-size: 0.82rem; }
.qma-tax-add-input        { flex: 1; font-size: 0.82rem; }
.qma-tax-impact-panel     { margin-top: 20px; border-top: 1px solid var(--border);
                             padding-top: 14px; }
.qma-tax-impact-hdr       { font-size: 0.88rem; font-weight: 600; margin-bottom: 10px;
                             color: var(--primary-ink, #1a3a5c); }
.qma-tax-no-impact        { font-size: 0.82rem; color: var(--text-muted); font-style: italic; }
.qma-tax-impact-doc       { display: flex; align-items: center; gap: 10px; padding: 6px 8px;
                             border-radius: 4px; cursor: pointer; font-size: 0.84rem; }
.qma-tax-impact-doc:hover { background: var(--surface-2, #f7f8fa); }
.qma-tax-impact-num       { font-weight: 600; color: var(--text-muted); min-width: 80px; }
.qma-tax-impact-title     { flex: 1; }


/* ── QMS Library picker modal ─────────────────────────────────────────────── */
.qms-lib-picker-search {
    width: 100%;
    box-sizing: border-box;
    padding: 7px 10px;
    margin-bottom: 10px;
    border: 1px solid var(--border, #ccc);
    border-radius: 6px;
    font-size: 0.85rem;
    font-family: inherit;
}
.qms-lib-picker-list {
    flex: 1;
    overflow-y: auto;
    border: 1px solid var(--border, var(--border));
    border-radius: 6px;
}
.qms-lib-picker-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--border, var(--border));
    cursor: pointer;
    transition: background 0.12s;
}
.qms-lib-picker-row:last-child { border-bottom: none; }
.qms-lib-picker-row:hover { background: var(--hover-bg, #f0f4f8); }
.qms-lib-picker-num {
    font-size: 0.78rem;
    color: var(--muted, #718096);
    min-width: 80px;
    white-space: nowrap;
}
.qms-lib-picker-info { flex: 1; min-width: 0; }
.qms-lib-picker-title {
    font-weight: 600;
    font-size: 0.88rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.qms-lib-picker-meta {
    font-size: 0.76rem;
    color: var(--muted, #718096);
    margin-top: 2px;
}
.qms-lib-picker-row--no-file {
    cursor: default;
    opacity: 0.5;
}
.qms-lib-picker-row--no-file:hover { background: transparent; }
.qms-lib-picker-no-file {
    font-size: 0.72rem;
    color: var(--muted, #718096);
    border: 1px solid var(--border, var(--border));
    border-radius: 4px;
    padding: 1px 5px;
    white-space: nowrap;
    flex-shrink: 0;
}
.qms-lib-picker-empty {
    padding: 24px;
    text-align: center;
    font-size: 0.85rem;
    color: var(--muted, #718096);
}

/* ── #1938 · the vocabulary chips, in the toolbar's Filter picker ─────────── */
.qma-tax-group {
    margin-bottom: 10px;
}
.qma-tax-group-label {
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    /* #1938 — the picker sits on the page's ground, not the chrome sidebar it
       was written for, so the label is the page's ink. */
    color: var(--text);
    padding: 0 4px;
    margin-bottom: 4px;
}
.qma-tax-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    padding: 0 4px;
}
.qma-tax-chip {
    font-size: 0.72rem;
    padding: 2px 7px;
    border-radius: 10px;
    border: 1px solid var(--border, #ccc);
    background: var(--surface-2, #f7f8fa);
    color: var(--fg, #333);
    cursor: pointer;
    transition: all 0.12s;
    white-space: nowrap;
    max-width: 130px;
    overflow: hidden;
    text-overflow: ellipsis;
}
.qma-tax-chip:hover {
    background: var(--hover-bg, #e2e8f0);
    border-color: var(--accent, #2b6cb0);
    color: var(--accent, #2b6cb0);
}
.qma-tax-chip.active {
    background: var(--accent, #2b6cb0);
    border-color: var(--accent, #2b6cb0);
    color: #fff;
}
/* #1938 — the Filter picker, its chips and the header. The header reuses the
   metadata window's .rc-mw-glance / .rc-mw-next (one copy, #1937). */
.qma-filter-wrap { position: relative; }
.qma-filter-pop {
    position: absolute; top: calc(100% + 6px); left: 0; z-index: 30;
    width: min(360px, 80vw); max-height: 60vh; overflow-y: auto;
    padding: 12px; background: var(--bg-white); color: var(--text);
    border: 1px solid var(--border); border-radius: 10px;
    box-shadow: 0 8px 24px color-mix(in srgb, var(--text) 18%, transparent);
}
.qma-filter-pop.hidden { display: none; }
.qma-filter-search { width: 100%; margin-bottom: 10px; }
.qma-filter-none { font-size: 0.74rem; color: var(--text-muted); font-style: italic; }
.qma-filter-foot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 10px; }
/* ══ #1938 · OPTION 1 REVISED (Craig 2026-10-06, comment 21760) ═══════════════
   The artboard is documentation/design/ui/design_handoff_qms_page/Option1Revised.dc.html. ONE
   header card across the page; below it ONE card holding the rail beside the
   document pane. Neutral grounds, the light blue (--primary-pale) ONLY for the
   selected item, neutral hover, blue only for the primary action and links.
   Tokens only, so dark mode and every appearance palette follow. */
.qms-access-wrap {
    display: flex; flex-direction: column; gap: 16px;
    height: calc(100vh - 52px);
    box-sizing: border-box;
    /* The bottom clears the fixed Report-an-issue button (#1880), so the rail's
       last entries are never under it. */
    padding: 20px 24px calc(var(--report-fab-inset) + var(--report-fab-size) + 8px);
    background: var(--bg);
    overflow: hidden;
    position: relative;
}
.qms-access-wrap > .qma-head, .qms-access-wrap > .qma-body { width: 100%; max-width: 1392px; margin: 0 auto; box-sizing: border-box; }

/* The header card */
.qma-head {
    flex: none; display: flex; flex-direction: column; gap: 16px;
    padding: 18px 22px; background: var(--bg-white);
    border: 1px solid var(--border); border-radius: 10px;
}
.qma-head-top { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }
.qma-head-title { min-width: 0; }
.qma-head-org { font-size: 13px; color: var(--text-muted); min-height: 1em; }
.qma-head-h1 { margin: 2px 0 0; font-size: 22px; font-weight: 600; color: var(--text); }
.qma-head-acts { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.qma-head-acts .qma-search-input { width: 240px; font-size: 14px; padding: 7px 10px; border-radius: 6px; }
.qms-access-wrap.is-tool .qma-list-only { display: none; }
/* The figures: label above, figure below, in a row (the shared inset, #1937). */
.qma-inset { padding: 16px 20px; }
.qma-inset .rc-mw-glance { display: flex; flex-wrap: wrap; gap: 12px 36px; }
.qma-inset .rc-mw-g-k { font-size: 13px; font-weight: 400; letter-spacing: 0; text-transform: none; margin-bottom: 0; }
.qma-inset .rc-mw-g-v { font-size: 20px; font-weight: 600; }
.qma-inset .rc-mw-next { font-size: 14px; justify-content: space-between; }
.qma-next.hidden { display: none; }

/* The card below: the rail beside the document pane */
.qma-body {
    flex: 1; min-height: 0; display: flex;
    background: var(--bg-white); border: 1px solid var(--border); border-radius: 10px;
    overflow: hidden;
}
.qms-access-sidebar {
    flex: 0 0 260px; max-width: 290px; box-sizing: border-box;
    background: var(--pq-soft); color: var(--text);
    border-right: 1px solid var(--border);
    /* #1969 — its top line level with the list's, below the card's heading
       (qms_access.js _alignRailToList measures it; 0 when a tool view shows) */
    padding: var(--qma-list-top, 0px) 6px 12px; overflow-y: auto;
    /* #2245:begin — a column (below); one rule for the rail, #2278 */
    display: flex; flex-direction: column;
    /* #2245:end */
}
/* #2245:begin — the rail is a column: only the folder and group LISTS may shrink, each
   scrolling in its own area, so the headings, "+ New folder", "+ New group", Work and
   Explore stay in view however many folders and groups there are (Craig 2026-10-18).
   The two lists shrink together, in proportion to their length, so neither pushes the
   other off; a short list takes only its own height. The rail's own scroll stays for a
   window too short even for the lists' floor. (#2278: the column declarations
   sit in the rail's one rule above, so the rail has one definition.) */
.qms-access-sidebar > * { flex: none; }
/* the section shrinks; its floor (heading + the list's floor + its add button) is set
   when drawn (_keepCurrentInView). CSS cannot say it: a section's own automatic minimum
   counts its list's FULL height (measured: the sections then never shrank), and 0 let
   a list spill over the section below (measured: '+ New folder' drawn under Groups). */
.qms-access-sidebar > .qms-rail-sec[data-rail-sec="folders"],
.qms-access-sidebar > .qms-rail-sec[data-rail-sec="groups"] {
    flex: 0 1 auto; min-height: 0; display: flex; flex-direction: column;
}
.qms-rail-sec > .qms-rail-h, .qms-rail-sec > .qms-rail-add { flex: none; }
.qms-rail-folders, .qms-rail-groups {
    /* takes what its section has left after the heading and the add button; the floor
       is the section's (set when drawn), so a short list reserves no empty space */
    flex: 0 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
    position: relative;   /* the keep-in-view arithmetic measures items against the list itself */
    scrollbar-width: thin; scrollbar-color: var(--border) transparent;   /* themed, light and dark */
}
/* #2245:end */
.qms-rail-h {
    font-size: 12px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--text-muted); padding: 14px 8px 6px;
}
.qms-rail-sec[data-rail-sec="groups"] .qms-rail-h { padding-top: 18px; }
.qms-rail-rule { border-top: 1px solid var(--border); margin: 14px 6px 0; }
.qms-rail-item, .qms-rail-add {
    width: 100%; box-sizing: border-box; text-align: left;
    display: flex; align-items: center; gap: 8px;
    border: 0; border-radius: 8px; background: transparent; color: var(--text);
    font: inherit; font-size: 14px; font-weight: 400; padding: 7px 12px; cursor: pointer;
}
.qms-rail-add { color: var(--text-muted); text-decoration: none; }
.qms-rail-sec[data-rail-sec="folders"] .qms-rail-add { padding-left: 28px; }
.qms-rail-item .ic { width: 15px; height: 15px; flex: none; }
.qms-rail-item:hover, .qms-rail-add:hover { background: var(--hover-bg); }
.qms-rail-item:focus-visible, .qms-rail-add:focus-visible { outline: 2px solid var(--primary-fg); outline-offset: -2px; }
/* Where you are: the ONE selection colour, and weight, so it is never colour alone. */
.qms-rail-item.is-current, .qms-rail-folder.is-current .qms-rail-item { background: var(--primary-pale); font-weight: 600; }
.qms-rail.is-tool .qms-rail-folder.is-current .qms-rail-item { background: none; font-weight: inherit; }
@media (prefers-contrast: more) {
    .qms-rail-item.is-current { outline: 2px solid var(--text); outline-offset: -2px; }
}
.qms-rail-t { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qms-rail-n { flex: none; margin-left: auto; font-size: 12px; font-weight: 400; color: var(--text-muted); font-variant-numeric: tabular-nums; }
/* The folders: a flat list, indented by depth (QMS, then 16px a level). The
   indent is the item's own padding, so the selection band runs the full width. */
.qms-rail-folder { --qma-rail-indent: calc(12px + var(--qma-depth, 0) * 16px); }
.qms-rail-folder .qms-rail-fname { padding-left: var(--qma-rail-indent); }
.qms-rail-folder.is-drop-target { outline: 2px dashed var(--primary-fg); outline-offset: -2px; border-radius: 8px; }
/* A group: its name, and beneath it its regulations and member count.
   #2126 — press-and-hold opens the group's menu on a touch screen; the phone's own
   callout and text selection would otherwise sit on top of it. */
.qms-rail-group { flex-direction: column; align-items: flex-start; gap: 1px;
    -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
.qms-rail-gsub { font-size: 12px; font-weight: 400; color: var(--text-muted); }
.qms-rail-note { font-size: 13px; color: var(--text-muted); padding: 2px 12px; }

/* The document pane */
.qms-access-main { flex: 999 1 640px; min-width: 0; display: flex; flex-direction: column; overflow: hidden; position: relative; }
.qma-sect-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 12px; padding: 16px 20px 10px; flex-wrap: wrap; flex: none; }
.qma-crumbs { font-size: 13px; color: var(--text-muted); min-height: 1em; }
.qma-view-title { margin: 0; font-size: 19px; font-weight: 600; color: var(--text); }
/* Structure | List: one segmented control; the chosen one is the selection colour. */
.qma-view-switch { display: inline-flex; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
.qma-view-switch .qma-btn-secondary {
    border: 0; border-radius: 0; background: var(--bg-white); color: var(--text);
    padding: 7px 12px; min-height: 34px; font-size: 13px; font-weight: 400; box-shadow: none;
}
.qma-view-switch .qma-btn-secondary + .qma-btn-secondary { border-left: 1px solid var(--border); }
.qma-view-switch .qma-btn-secondary.active { background: var(--primary-pale); font-weight: 600; }
.qms-access-toolbar {
    display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap;
    padding: 0 20px 14px; background: transparent; border-bottom: 1px solid var(--border); flex: none;
}
.qms-access-toolbar-left { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; flex: 1; }
.qms-access-toolbar-right { display: flex; gap: 8px; align-items: center; }
.qms-access-toolbar .qma-select { font-size: 13px; padding: 6px 10px; min-height: 34px; border-radius: 6px; }
.qma-search-input {
    padding: 6px 10px; border: 1px solid var(--border); border-radius: 6px;
    font: inherit; font-size: 13px; background: var(--bg-white); color: var(--text); width: 240px;
}
.qma-select {
    padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px;
    font-size: 13px; background: var(--bg-white);
    color: var(--text);   /* N4 — a <select> needs an explicit colour or it renders dark browser-default text on the dark bg */
}
.qma-select option { background: var(--card); color: var(--text); }
.qma-filter-hidden { font-size: 13px; color: var(--text-muted); }
.qma-filter-hidden:empty { display: none; }

/* Buttons: one filled primary per context; the rest neutral (the design's .btn) */
.qma-btn-primary {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 7px 12px; min-height: 34px; box-sizing: border-box;
    background: var(--btn-primary, var(--primary)); color: #fff; border: 1px solid var(--btn-primary, var(--primary));
    border-radius: 6px; font: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
}
.qma-btn-primary:hover { background: var(--btn-primary-h, var(--btn-primary)); }
.qma-btn-secondary {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 7px 12px; min-height: 34px; box-sizing: border-box;
    background: var(--bg-white); color: var(--text); border: 1px solid var(--border);
    border-radius: 6px; font: inherit; font-size: 13px; cursor: pointer;
}
.qma-btn-secondary:hover { background: var(--hover-bg); }

/* The table */
.qma-list-wrap { flex: 1; overflow-y: auto; overflow-x: auto; padding: 0;
    container-type: inline-size; }   /* the container query below */
.qma-table { width: 100%; border-collapse: collapse; font-size: 14px; background: var(--bg-white); box-shadow: none; border-radius: 0; }
.qma-table thead th {
    background: var(--pq-soft); color: var(--text-muted);
    font-size: 12px; font-weight: 600; letter-spacing: 0; text-transform: none;
    text-align: left; padding: 9px 8px; border-bottom: 1px solid var(--border);   /* #1969: 8px sides, ten columns */
    position: sticky; top: 0; z-index: 1;   /* B-055 — the header stays while rows scroll */
}
.qma-table td { padding: 9px 8px; vertical-align: middle; font-weight: 400; border-bottom: 1px solid var(--border); }
.qma-table :is(th, td):first-child { padding-left: 16px; }
/* #1969 — a heading may wrap ("Effective / date"), so the heading never sets its
   column's width: the content does. Two columns where one stood need the room. */
.qma-th-sortable { cursor: pointer; user-select: none; white-space: normal; }
.qma-th-sortable:hover { color: var(--text); }
.qma-sort-arrow { font-size: 0.7rem; opacity: 0.6; }
.qma-table tbody tr:hover td { background: var(--hover-bg); cursor: pointer; }
/* Craig's test 2026-10-06 ("the cols are not all visible"): no forced table
   width — the Document cell wraps so all eight columns fit the pane. */
/* #1969 — Number and Title, two columns: the number never wraps and takes its
   own width; the title wraps and takes the slack. */
.qma-cell-numcol { white-space: nowrap; }
.qma-cell-numcol .qma-cell-number { font-weight: 600; color: var(--text); white-space: nowrap; }
/* #1969 retest (1280 px window: the table 55 px wider than its 994 px pane, held by
   the one-line numbers) — WHEN THE PANE IS NARROW, and only then, a number may break
   after its hyphens ("GCD-SOP-" / "1401.00"). Measured on dev: with every number on
   one line the table needs 1,049 px, so a pane wider than 1,060 px keeps them whole
   (the 1,130 px pane at 1440 and up); a narrower one (994 px at 1280, or drawers
   open) lets them break and the table fits. A container query, so it follows the
   pane itself, drawers included, not the window (container-type is on
   .qma-list-wrap's own rule above). */
@container (max-width: 1060px) {
    .qma-cell-numcol, .qma-cell-numcol .qma-cell-number { white-space: normal; }
    /* two lines at most ("GCD-SOP-" / "307.00"), never one fragment per line.
       #2006 retest: 8em, not 8.5em - "GCD-SOP-" is about 70 px bold, so 112 px
       still holds it, and the half-em is part of what the full Status filter needs
       at 1,280 px (the table measured 998 px in a 994 px pane at 8.5em). */
    .qma-table td.qma-cell-numcol { min-width: 8em; }
}
.qma-table td[data-col="title"] { min-width: 140px; white-space: normal; }
.qma-table td[data-col="title"] .qma-title-btn { display: inline; white-space: normal; }
/* #1969 — one long label can never widen the table again: the cell stops at
   12em and the full text is on hover (title, qms_access.js). */
.qma-cell-ver { font-family: var(--mono); font-size: 13px; white-space: nowrap;
    max-width: 12em; overflow: hidden; text-overflow: ellipsis; }
/* Craig's look 2026-10-06 — a filter in its column's heading, under the label. */
.qma-th-filter { display: block; margin-top: 4px; max-width: 100%; min-height: 0; padding: 2px 6px; font-size: 12px; font-weight: 400; }
/* #2006 — the Status filter SETS its column's floor: sized to its longest option
   (the organisation's own state names, plus "All statuses"), never narrower than
   the column. Under the #1969 rule above (100% of a content-width column) the
   column shrank to the status badge and the filter read "All statu" at 1,280 and
   1,440 px. The other heading filters keep #1969's rule; the slack is Document's.
   Retest (4.3.1682, 1,280 px): every column was at its narrowest and the table
   998 px in a 994 px pane - the 26 px Status took was 4 px more than the others
   could give. Its side padding is 2 px, not 6: the filter is sized to its text, so
   the padding is the one part of its width that carries nothing (8 px back), and
   the Number floor gives about 7 more (above). */
.qma-table thead th[data-col="status"] .qma-th-filter { width: auto; min-width: 100%; max-width: none;
    padding-left: 2px; padding-right: 2px; }
/* #2005 — a note in the Domain config list (the session expired while it loaded). */
.domain-list-note { padding: 10px; font-size: 0.82rem; color: var(--text-muted); }
/* The Effective date column, and a date still ahead of today. */
.qma-cell-eff { white-space: nowrap; font-size: 13px; }
.qma-eff-future { margin-left: 4px; font-size: 11px; font-style: italic; color: var(--text-muted); }
/* #2105 — an Effective version without its uncontrolled copy: a warning, in the
   same amber as the audit trail's warning outcome (light and dark). */
.qma-copy-missing { margin-left: 4px; font-size: 11px; font-weight: 600;
    color: light-dark(#975a16, color-mix(in oklab, #975a16 45%, white)); }
/* Sort by, at the toolbar's right. */
.qma-sort-wrap { display: inline-flex; align-items: center; gap: 6px; }
/* #2173 — the QMS list toolbar's square icon buttons (sort direction, column
   settings). Their own name, so they no longer move with the Templates buttons; the
   declarations are the ones they rendered with under the shared name, so the look is
   unchanged (tests/test_2173 compares the two). */
.qma-tool-btn       { display: inline-flex; align-items: center; justify-content: center; gap: 5px;
                      width: 34px; height: 34px; box-sizing: border-box;
                      background: none; border: 1px solid var(--border); border-radius: 4px;
                      padding: 3px 7px; cursor: pointer; font-size: 0.85rem; color: var(--text-muted);
                      transition: background 0.15s; }
.qma-tool-btn:hover { background: var(--hover-bg); color: var(--primary-ink, #1a3a5c); }
.qma-sort-label { font-size: 13px; color: var(--text-muted); }
.qma-cell-owner { font-size: 14px; color: var(--text); }
.qma-cell-status { white-space: nowrap; }
.qma-cell-groups { white-space: normal; }
.qma-group-chip {
    display: inline-block; margin: 1px 4px 1px 0; padding: 1px 8px;
    font: inherit; font-size: 12px; line-height: 1.5;
    border: 1px solid var(--border); border-radius: 999px;
    background: transparent; color: var(--text-muted); cursor: pointer;
}
.qma-group-chip:hover, .qma-group-chip:focus-visible { background: var(--hover-bg); color: var(--text); }
/* Columns and display: a hidden column goes from the header and every row. */
.qma-table.qma-hide-type [data-col="type"], .qma-table.qma-hide-status [data-col="status"],
.qma-table.qma-hide-version [data-col="version"], .qma-table.qma-hide-analysis [data-col="analysis"],
.qma-table.qma-hide-owner [data-col="owner"], .qma-table.qma-hide-groups [data-col="groups"],
.qma-table.qma-hide-usedby [data-col="usedby"] { display: none; }

/* The applied vocabulary terms (the Filter you pick), each removable */
.qma-filter-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.qma-filter-chips:empty, .qma-filter-chips.hidden { display: none; }
.qma-filter-chip {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 12px; padding: 3px 6px 3px 10px; border-radius: 999px;
    background: var(--primary-pale); color: var(--primary-fg); border: 1px solid var(--primary-fg);
}
.qma-filter-chip b { font-weight: 400; }
.qma-filter-chip-x {
    border: 0; background: none; color: inherit; padding: 0 2px; cursor: pointer;
    display: inline-flex; border-radius: 50%;
}
.qma-filter-chip-x:hover, .qma-filter-chip-x:focus-visible { background: var(--hover-bg); }
.qma-filter-chip-x .ic { width: 11px; height: 11px; }

/* ── QMS Chat panel ─────────────────────────────────────────────────────── */
.qma-chat-messages {
    flex: 1;
    overflow-y: auto;
    padding: 16px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    min-height: 0;
}
.qma-chat-intro {
    font-size: 0.85rem;
    color: var(--muted, #718096);
    text-align: center;
    padding: 24px 16px;
    line-height: 1.6;
}
.qma-chat-msg {
    max-width: 85%;
    padding: 10px 14px;
    border-radius: 10px;
    font-size: 0.85rem;
    line-height: 1.55;
    white-space: pre-wrap;
    word-break: break-word;
}
.qma-chat-user {
    align-self: flex-end;
    background: var(--accent, #2b6cb0);
    color: #fff;
    border-bottom-right-radius: 3px;
}
.qma-chat-assistant {
    align-self: flex-start;
    background: var(--surface-2, #f0f4f8);
    color: var(--fg, #1a202c);
    border-bottom-left-radius: 3px;
}
.qma-chat-input-row {
    display: flex;
    gap: 8px;
    padding: 12px 16px;
    border-top: 1px solid var(--border, var(--border));
    background: var(--surface, #fff);
}
.qma-chat-input {
    flex: 1;
    padding: 8px 12px;
    border: 1px solid var(--border, #ccc);
    border-radius: 6px;
    font-size: 0.85rem;
    font-family: inherit;
}
#qma-chat-panel {
    display: flex;
    flex-direction: column;
    height: 100%;
}


/* ── Templates panel ──────────────────────────────────────────────────────── */
.qma-tmpl-group         { margin-bottom: 24px; }
.qma-tmpl-group-hdr     { font-size: 0.78rem; font-weight: 600; text-transform: uppercase;
                           letter-spacing: 0.05em; color: var(--text-muted); padding: 4px 0 8px;
                           border-bottom: 1px solid var(--border); margin-bottom: 10px; }
.qma-tmpl-row           { background: var(--card-bg, #fff); border: 1px solid var(--border);
                           border-radius: 6px; padding: 12px 14px; margin-bottom: 8px; }
.qma-tmpl-row-top       { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.qma-tmpl-name          { font-weight: 600; font-size: 0.92rem; flex: 1; }
.qma-tmpl-actions       { display: flex; gap: 4px; }
.qma-tmpl-desc          { font-size: 0.82rem; color: var(--text-muted); margin-bottom: 6px; }
.qma-tmpl-sections      { display: flex; flex-wrap: wrap; gap: 4px; margin: 6px 0; }
.qma-tmpl-sec           { background: var(--primary-pale); color: var(--primary-fg);
                           border-radius: 3px; padding: 2px 7px; font-size: 0.75rem; }
.qma-tmpl-no-secs       { font-size: 0.8rem; color: var(--text-muted); font-style: italic; }
.qma-tmpl-meta          { font-size: 0.75rem; color: var(--text-muted); margin-top: 6px; }
.qma-sections-input     { font-family: monospace; font-size: 0.82rem; }
.qma-current-file       { font-size: 0.8rem; color: var(--text-muted); margin-top: 4px; }
.qma-file-input         { font-size: 0.82rem; }
/* The Templates rows' icon buttons (download, edit, delete). #2173: the QMS list's
   sort and column buttons used to share this name (#1938 reused it), so a change to
   one restyled the other; they are .qma-tool-btn now. The look is unchanged. */
.qma-btn-icon           { display: inline-flex; align-items: center; justify-content: center;
                           width: 34px; height: 34px; box-sizing: border-box;
                           background: none; border: 1px solid var(--border); border-radius: 4px;
                           padding: 3px 7px; cursor: pointer; font-size: 0.85rem; color: var(--text-muted);
                           transition: background 0.15s; }
.qma-btn-icon:hover     { background: var(--hover-bg); color: var(--primary-ink, #1a3a5c); }
.qma-btn-icon-danger    { color: light-dark(#c53030, color-mix(in oklab, #c53030 45%, white)); }
.qma-btn-icon-danger:hover { background: light-dark(#fff5f5, var(--pq-elevated)); border-color: #fc8181; }
.qma-panel-intro        { font-size: 0.84rem; color: var(--text-muted); margin: -4px 0 18px; line-height: 1.5; }

/* ── QMA Modal ──────────────────────────────────────────────────────────────── */
.qma-modal-overlay      { position: fixed; inset: 0; background: rgba(0,0,0,0.45);
                           display: flex; align-items: center; justify-content: center; z-index: var(--z-dialog); }
.qma-modal              { background: Canvas; border-radius: 8px; width: 480px; max-width: 95vw;
                           max-height: 88vh; display: flex; flex-direction: column;
                           box-shadow: 0 8px 32px rgba(0,0,0,0.2); }
.qma-modal-hdr          { display: flex; align-items: center; justify-content: space-between;
                           padding: 14px 18px; border-bottom: 1px solid var(--border);
                           background: var(--primary, #1a3a5c); color: #fff; border-radius: 8px 8px 0 0; }
.qma-modal-title        { font-weight: 600; font-size: 0.95rem; }

.qma-modal-body         { padding: 18px; overflow-y: auto; flex: 1;
                           display: flex; flex-direction: column; gap: 10px;
    scrollbar-gutter: stable;
}
.qma-modal-footer       { display: flex; justify-content: flex-end; gap: 8px;
                           padding: 12px 18px; border-top: 1px solid var(--border); }
.qma-label              { font-size: 0.82rem; font-weight: 600; color: var(--text-muted); }
.qma-hint               { font-weight: 400; color: var(--text-muted); }
.req                    { color: light-dark(#c53030, color-mix(in oklab, #c53030 45%, white)); }

/* ── Auto-debate (batch debate) ─────────────────────────────────────────── */

.run-all-debates-btn {
    background: none;
    border: 1px solid var(--primary-light);
    border-radius: 4px;
    color: var(--primary-fg);
    font-size: 0.72rem;
    font-weight: 500;
    cursor: pointer;
    padding: 3px 7px;
    white-space: nowrap;
    margin-right: 4px;
    flex-shrink: 0;
}
.run-all-debates-btn:hover { background: var(--primary); color: #fff; border-color: var(--primary-fg); }
/* T-134 Slice B — active state for the "Hide resolved" toggle. */
.run-all-debates-btn.is-active { background: var(--primary); color: #fff; border-color: var(--primary-fg); }

/* Debate outcome badge on finding cards */
.debate-outcome-badge {
    font-size: 0.68rem;
    font-weight: 600;
    padding: 1px 5px;
    border-radius: 3px;
    white-space: nowrap;
    flex-shrink: 0;
}

.debate-badge-impasse   { background: light-dark(#fff3cd, color-mix(in oklab, #fff3cd 14%, var(--pq-surface))); color: light-dark(#856404, color-mix(in oklab, #856404 45%, white)); }
/* Axis-model disposition badge on the finding card (fallback tint; the data-driven
   path overrides with inline colours from _domainConfig.taxonomy.disposition_colours).
   dismiss=grey, monitor=blue, remediate=green. */
.debate-disp-dismiss   { background: light-dark(#f3f4f6, var(--pq-elevated)); color: light-dark(#6b7280, #cbd5e1); }
.debate-disp-monitor   { background: #eff6ff; color: #1e40af; }
@media (prefers-color-scheme: dark) { .debate-disp-monitor { background: var(--pq-surface); color: #93c5fd; } }
.debate-disp-remediate { background: light-dark(#f0fff4, #13301f); color: light-dark(#276749, #86efac); }

/* ── Stream-failure state (#265/#266): the ONE Retry/Defer error box every
      LLM stream consumer mounts (RCStream.errorBox). Light default + dark
      via prefers-color-scheme, per the app convention. ─────────────────── */
.rc-stream-error {
    display: flex; flex-direction: column; gap: 8px;
    padding: 10px 12px; margin: 4px 0;
    border: 1px solid #f0b4b4; border-radius: 8px;
    background: #fdf2f2; color: #9b2c2c;
    font-size: 0.86rem; line-height: 1.35;
}
.rc-stream-error-actions { display: flex; gap: 8px; }
.rc-stream-error-btn {
    padding: 4px 14px; border-radius: 6px; cursor: pointer;
    font-size: 0.82rem; font-weight: 600;
    border: 1px solid #cbd5e0; background: #ffffff; color: #2d3748;
}
.rc-stream-error-btn.rc-stream-retry { background: light-dark(#2b6cb0, #2b6cb0); border-color: light-dark(#2b6cb0, #2b6cb0); color: light-dark(#ffffff, #ffffff);
}
.rc-stream-error-btn:hover { filter: brightness(0.95); }
@media (prefers-color-scheme: dark) {
    .rc-stream-error { background: #3a1d1d; border-color: #7f2f2f; color: #fca5a5; }
    .rc-stream-error-btn { background: var(--pq-elevated); border-color: var(--pq-border); color: #e2e8f0; }
    
}

/* ── Progress UI (L-084): import modal + count-up corner timer ─────────────── */
.rc-prog-overlay {
    position: fixed; inset: 0; z-index: 11000;
    display: flex; align-items: center; justify-content: center;
    background: rgba(15, 23, 42, 0.45);
}
.rc-prog-overlay.hidden { display: none; }
.rc-prog-modal {
    display: flex; align-items: stretch;
    width: min(460px, 92vw); min-height: 150px; background: #ffffff; color: #1e293b;
    border-radius: 10px; overflow: hidden;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.28);
}
@media (prefers-color-scheme: dark) { .rc-prog-modal { background: var(--pq-elevated); color: #e2e8f0; } }
.rc-prog-body { flex: 1 1 auto; min-width: 0; padding: 20px 22px 18px; }
.rc-prog-title { font-size: 1.02rem; font-weight: 700; margin-bottom: 14px; }
.rc-prog-track { height: 10px; border-radius: 6px; background: light-dark(#e5e9f0, var(--pq-line)); overflow: hidden; }
.rc-prog-fill {
    height: 100%; width: 0%;
    background: var(--primary, #2f6df6);
    border-radius: 6px; transition: width 0.35s ease;
}
.rc-prog-count { margin-top: 8px; font-size: 0.9rem; font-weight: 600; color: light-dark(#475569, #cbd5e1); }
.rc-prog-caption {
    margin-top: 4px; font-size: 0.86rem; color: light-dark(#64748b, #94a3b8);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Elapsed + ETA (Craig 2026-07-13). tabular-nums so the counting-down clock
   doesn't jitter its own width as the digits change. Empty until timing starts,
   so it takes no space on an indeterminate op. */
.rc-prog-eta {
    margin-top: 10px; font-size: 0.8rem; color: light-dark(#64748b, #94a3b8);
    font-variant-numeric: tabular-nums;
}
.rc-prog-eta:empty { display: none; }
.rc-prog-actions { margin-top: 16px; text-align: right; }
.rc-prog-cancel {
    padding: 6px 14px; border-radius: 6px; border: 1px solid #cbd5e1;
    background: #f8fafc; color: light-dark(#334155, #e2e8f0); font-size: 0.86rem; cursor: pointer;
}
.rc-prog-cancel:hover:not(:disabled) { background: light-dark(#eef2f7, #3e4c63); }
.rc-prog-cancel:disabled { opacity: 0.6; cursor: default; }

/* Centred on screen (Craig 2026-07-12). It had been pinned to the bottom-right of
   the VIEWPORT; the "bottom right" in the original brief meant the bottom-right of
   a CENTRED modal (where the elapsed counter sits), not the corner of the screen.
   The component itself is unchanged — only its position. */
/* #610 — the "still waiting" notice inside a streaming debate bubble. Muted and
   italic: it is a status, not content, and must not read as something the voice
   said. It removes itself the moment data resumes. */
.rc-debate-waiting {
    font-style: italic;
    color: var(--text-muted);
    opacity: 0.85;
    white-space: nowrap;
}

.rc-prog-timer {
    /* v4.2.65 — was `top:50%` (dead centre), which parked the toast right on top of the
       empty-state text and the logo behind it (Craig 2026-07-13). Raised clear of them.
       Still horizontally centred, so it reads as a page-level status, not a corner nag. */
    position: fixed; top: 22%; left: 50%; transform: translateX(-50%);
    z-index: 11050;
    display: flex; align-items: center; gap: 9px;
    padding: 9px 14px; border-radius: 8px; background: #1e293b; color: #f1f5f9;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
    font-size: 0.86rem;
}
@media (prefers-color-scheme: dark) { .rc-prog-timer { background: var(--pq-ground); } }
.rc-prog-timer.hidden { display: none; }
.rc-prog-timer-label { font-weight: 600; }
.rc-prog-timer-secs {
    font-variant-numeric: tabular-nums; font-weight: 700;
    color: #7dd3fc; min-width: 30px; text-align: right;
}
.rc-prog-timer-spin {
    width: 13px; height: 13px; border-radius: 50%; flex: 0 0 auto;
    border: 2px solid rgba(255, 255, 255, 0.25); border-top-color: #7dd3fc;
    animation: rc-prog-spin 0.8s linear infinite;
}
@keyframes rc-prog-spin { to { transform: rotate(360deg); } }

@media (prefers-color-scheme: dark) {
    
    
    
    
    
    .rc-prog-cancel  { background: var(--pq-line); border-color: var(--pq-border); }
    
    
}

/* Processing-error badge on finding cards */
.proc-error-badge {
    font-size: 0.68rem;
    font-weight: 600;
    padding: 2px 6px;
    border-radius: 4px;
    background: light-dark(#fff3cd, color-mix(in oklab, #fff3cd 14%, var(--pq-surface)));
    color: light-dark(#856404, color-mix(in oklab, #856404 45%, white));
    border: 1px solid #ffc107;
    cursor: help;
    white-space: nowrap;
}

/* Processing errors section in expert review left pane */
.er-proc-errors {
    margin: 8px 12px;
    border: 1px solid #ffc107;
    border-radius: 6px;
    background: light-dark(#fffdf0, var(--pq-elevated));
    font-size: 0.78rem;
    overflow: hidden;
}
.er-proc-errors-hdr {
    font-weight: 700;
    padding: 5px 10px;
    background: light-dark(#fff3cd, color-mix(in oklab, #fff3cd 14%, var(--pq-surface)));
    color: light-dark(#856404, color-mix(in oklab, #856404 45%, white));
    border-bottom: 1px solid #ffc107;
}
.er-proc-error-row {
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: 6px;
    align-items: baseline;
    padding: 4px 10px;
    border-bottom: 1px solid light-dark(#ffeaa0, color-mix(in oklab, #ffeaa0 30%, var(--pq-surface)));
}
.er-proc-error-row:last-child { border-bottom: none; }
.er-proc-error-src {
    font-weight: 600;
    color: light-dark(#856404, color-mix(in oklab, #856404 45%, white));
    white-space: nowrap;
    font-family: monospace;
    font-size: 0.72rem;
}
.er-proc-error-msg { color: light-dark(#5a4000, color-mix(in oklab, #5a4000 45%, white)); }
.er-proc-error-ts  { color: var(--text-muted); font-size: 0.7rem; white-space: nowrap; }

/* ── Popout panel (per-section expand button) ──────────────────────────────── */
.er-popout-btn {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 0.95rem;
    color: var(--text-muted);
    padding: 0 2px;
    line-height: 1;
    opacity: 0.55;
    transition: opacity 0.15s, color 0.15s;
    margin-left: auto;
    flex-shrink: 0;
}
.er-popout-btn:hover { opacity: 1; color: var(--primary-fg); }
.er-popout-panel {
    position: fixed;
    top: 80px;
    right: 50px;
    width: 440px;
    max-height: 72vh;
    background: var(--surface);
    border: 2px solid var(--primary-fg);
    border-radius: 10px;
    box-shadow: 0 10px 40px rgba(0,0,0,0.28);
    display: flex;
    flex-direction: column;
    z-index: 9500;
    overflow: hidden;
}
.er-popout-hdr {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
    cursor: move;
    user-select: none;
    flex-shrink: 0;
}
.er-popout-title {
    font-size: 0.82rem;
    font-weight: 700;
    flex: 1;
}


.er-popout-body {
    flex: 1;
    overflow-y: auto;
    padding: 12px 16px;
    font-size: 0.87rem;
}

/* ── Batch debate floating progress panel ──────────────────────────────────── */
.bd-float-panel {
    position: fixed;
    bottom: 20px;
    right: 20px;
    width: 360px;
    max-width: calc(100vw - 40px);
    background: var(--surface, #fff);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 4px 20px rgba(0,0,0,0.18);
    z-index: 1200;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.bd-float-panel.hidden { display: none; }

.bd-float-header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    background: var(--primary);
    color: #fff;
    cursor: pointer;
    user-select: none;
    flex-shrink: 0;
}
.bd-float-title   { font-weight: 600; font-size: 0.85rem; flex-shrink: 0; }
.bd-float-summary { flex: 1; font-size: 0.78rem; opacity: 0.88; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bd-float-chevron { background: none; border: none; color: #fff; font-size: 0.9rem; cursor: pointer; padding: 0 2px; line-height: 1; flex-shrink: 0; }


.bd-float-bar-wrap {
    height: 4px;
    background: rgba(0,0,0,0.12);
    flex-shrink: 0;
}
.bd-float-bar {
    height: 100%;
    width: 0%;
    background: var(--bg-white);
    transition: width 0.3s;
    opacity: 0.7;
}

.bd-float-list-wrap {
    display: flex;
    flex-direction: column;
    max-height: 320px;
    overflow: hidden;
    transition: max-height 0.25s ease;
}
.bd-float-list-wrap.bd-collapsed { max-height: 0; }

.bd-float-list {
    flex: 1;
    overflow-y: auto;
    padding: 6px 10px;
}
.bd-float-footer {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
    padding: 8px 12px;
    border-top: 1px solid var(--border);
    flex-shrink: 0;
}

/* Batch debate confirm — findings table */
.bd-findings-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.82rem;
}
.bd-findings-table thead th {
    text-align: left;
    padding: 5px 8px;
    border-bottom: 2px solid var(--border);
    font-weight: 600;
    color: var(--text-muted);
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}
.bd-findings-table tbody tr {
    border-bottom: 1px solid var(--border-subtle, #eee);
}
.bd-findings-table tbody tr:last-child { border-bottom: none; }
.bd-findings-table td { padding: 5px 8px; vertical-align: middle; }
.bd-row-skip td { opacity: 0.6; }
.bd-cell-ref {
    font-size: 0.75rem;
    color: var(--text-muted);
    white-space: nowrap;
    max-width: 110px;
    overflow: hidden;
    text-overflow: ellipsis;
}
.bd-cell-desc {
    max-width: 380px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    line-height: 1.4;
}

/* Batch progress rows */
.bd-row {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 5px 4px;
    border-bottom: 1px solid var(--border);
    font-size: 0.80rem;
    line-height: 1.4;
}
.bd-row:last-child { border-bottom: none; }
.bd-row-icon    { flex-shrink: 0; width: 18px; text-align: center; font-size: 0.82rem; padding-top: 1px; }
.bd-row-quote   { flex: 1; color: var(--text); word-break: break-word; }
.bd-row-outcome { flex-shrink: 0; font-size: 0.70rem; font-weight: 600; padding: 1px 5px; border-radius: 3px; white-space: nowrap; }

.bd-status-pending   { color: var(--text-muted); }
.bd-status-debating  { color: var(--primary-fg); }
.bd-status-consensus { color: light-dark(#155724, color-mix(in oklab, #155724 45%, white)); }
.bd-status-impasse   { color: light-dark(#856404, color-mix(in oklab, #856404 45%, white)); }
.bd-status-error     { color: light-dark(#c0392b, color-mix(in oklab, #c0392b 45%, white)); }

.bd-outcome-consensus { background: light-dark(#d4edda, color-mix(in oklab, #d4edda 14%, var(--pq-surface))); color: light-dark(#155724, color-mix(in oklab, #155724 45%, white)); }
.bd-outcome-impasse   { background: light-dark(#fff3cd, color-mix(in oklab, #fff3cd 14%, var(--pq-surface))); color: light-dark(#856404, color-mix(in oklab, #856404 45%, white)); }

/* ── Popup / embed integration ── */

/* Return button — sits in the header, visible only in popup mode */
.popup-return-btn {
    margin-left: auto;
    margin-right: 8px;
    padding: 6px 14px;
    background: var(--primary);
    color: #fff;
    border: none;
    border-radius: 6px;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s, box-shadow 0.15s;
}
.popup-return-btn:hover { background: var(--primary-dark, #1a6ab5); }
.popup-return-btn:disabled { opacity: 0.6; cursor: default; }

/* Pulse when analysis completes to draw attention */
.popup-return-btn.popup-return-ready {
    animation: popup-btn-pulse 1.2s ease-in-out 3;
}
@keyframes popup-btn-pulse {
    0%, 100% { box-shadow: none; }
    50%       { box-shadow: 0 0 0 4px rgba(var(--primary-rgb, 30, 110, 190), 0.35); }
}


/* Popup mode: hide everything not needed for pure document analysis */
body.popup-mode #org-context-wrap       { display: none; }  /* admin org picker */

/* Hamburger menu in popup mode: keep only View ▸ and Brevity */
body.popup-mode .app-menu-item               { display: none !important; }
body.popup-mode .app-menu-sep                { display: none !important; }
/* Restore the two kept items */
body.popup-mode .app-menu-item.popup-menu-keep              { display: block !important; }
body.popup-mode .app-menu-item.app-menu-setting.popup-menu-keep { display: flex  !important; }
/* Restore submenu items inside the kept View ▸ parent */
body.popup-mode .popup-menu-keep .app-menu-item             { display: block !important; }

/* Hide report-download buttons in popup mode — results are sent via postMessage */

body.popup-mode #er-report-btn      { display: none !important; }

/* Hide upload sections in popup mode — file arrives via postMessage */




/* T-074(8) — Gate work area when not authenticated. Shoulder-surfing + residual-data
   defence. Regulatory anchors: NIST SP 800-53 AC-12 (session termination), NIST SP
   800-63B §4.4 (no residual session-bound data), 21 CFR §11.10(d) (limit access to
   authorized individuals), EU GMP Annex 11 §12 (security). Pairs with doLogout()
   active zeroing of in-memory + DOM state.
   T-024 W2.4 follow-up (Craig 2026-05-08): the original three-selector rule
   missed the floating containers (chat-float, sop-builder-wrap, qms-access-wrap)
   which sit at body level alongside #app-wrap. When the work area
   was hidden but a chat panel carried residual prior-session
   content, the defence was incomplete. Switched to an exclusion rule —
   hide every direct body child EXCEPT the header (brand) and the login-modal
   (the foreground UI). Defence in depth with the original 3-selector rule
   retained below. */
body.no-auth > *:not(header):not(#login-modal) { display: none !important; }
body.no-auth aside.sidebar,
body.no-auth main#main,
/* v4.2.64 — was `aside.findings-panel`, which no longer exists. The findings pane is
   now one of three .rc-drawer elements (findings / progress / metadata), and ALL of
   them can hold prior-session content — the Progress drawer holds the SSE log, and the
   Metadata drawer hosts the QMS detail panel. Renaming an element out from under a
   §11.10(d) purge rule is how a security control quietly stops covering anything, so
   this is widened to every drawer rather than dropped. */
body.no-auth aside.rc-drawer { display: none !important; }

/* ─────────────────────────────────────────────────────────────────────────
   T-118 C8.2 — TipTap rich-text editor (Draft editing surface).

   Scoped local variables so the editor honours the user's OS colour-scheme
   preference WITHOUT forcing the rest of the (currently light) app into dark
   mode. Light defaults inherit the app's existing palette vars; the
   prefers-color-scheme: dark block below remaps ONLY these editor-local vars.
   No hardcoded light-only or dark-only palette. NO finding-highlight
   decorations here — that is C8.3.
   ───────────────────────────────────────────────────────────────────────── */
.rc-tiptap-wrap {
    --rc-ed-bg:       var(--bg-white, #ffffff);
    --rc-ed-text:     var(--text, #2d3748);
    --rc-ed-muted:    var(--text-muted, #718096);
    --rc-ed-border:   var(--border, #e2e8f0);
    --rc-ed-toolbar:  var(--bg, #f0f2f5);
    --rc-ed-accent:   var(--primary, #1a3a5c);
    --rc-ed-accent-pale: var(--primary-pale, #eef3f9);

    display: flex;
    flex-direction: column;
    border: 1px solid var(--rc-ed-border);
    border-radius: 8px;
    overflow: hidden;
    background: var(--rc-ed-bg);
    color: var(--rc-ed-text);
    /* Fill the analysis pane width like the document render (was capped at
       900px + centred, which made the editor narrower than the doc). */
    max-width: none;
    margin: 0;
    /* Bound the editor to the viewport so the document scrolls INSIDE the
       .rc-tiptap-editor region (below) rather than scrolling the whole page —
       this structurally keeps the toolbar pinned at the top of the editor
       instead of sticking-then-scrolling-away with the page. */
    max-height: calc(100vh - 200px);
    min-height: 360px;
}

.rc-tiptap-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
    padding: 6px 8px;
    background: var(--rc-ed-toolbar);
    border-bottom: 1px solid var(--rc-ed-border);
    /* B-016 (Craig 2026-05-23): when the window narrows, the toolbar wraps to
       two rows. The redundant `position: sticky; top: 0` let the now-taller
       toolbar ride UP under the non-participating "Analysed" coverage bar that
       sits above the editor, and the wrap's `overflow: hidden` then clipped its
       top row (B/I/U, colour, font, H1-H3, lists). The bounded-flex wrap
       (.rc-tiptap-wrap max-height + the editor's own overflow:auto) ALREADY pins
       the toolbar at the top of the editor, so sticky was never needed. Make it
       a normal, non-shrinking flex child so BOTH rows always render fully below
       the coverage bar. Structural fix per no-opaque-sticky-workarounds — no
       z-index / opacity layering. */
    flex-shrink: 0;
}

.rc-tiptap-toolbar-sep {
    width: 1px;
    align-self: stretch;
    margin: 2px 4px;
    background: var(--rc-ed-border);
}

.rc-tiptap-btn {
    min-width: 30px;
    height: 28px;
    padding: 0 8px;
    font-size: 0.85rem;
    line-height: 1;
    color: var(--rc-ed-text);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 5px;
    cursor: pointer;
    transition: background 0.12s, border-color 0.12s;
}

.rc-tiptap-btn:hover {
    background: var(--rc-ed-accent-pale);
    border-color: var(--rc-ed-border);
}

.rc-tiptap-btn.is-active {
    background: var(--rc-ed-accent); color: light-dark(#ffffff, #0f1622);
    border-color: var(--rc-ed-accent);
}

/* T-131 — colour inputs, selects + the inline link input in the toolbar. */
/* Split-button colour control (Word-style): [ A▮ ][▾]. Click the swatch to
   apply the current colour; click ▾ for the palette. Theme-aware via --rc-ed-*. */
.rc-color-split {
    display: inline-flex;
    align-items: stretch;
    height: 28px;
    border: 1px solid var(--rc-ed-border);
    border-radius: 5px;
    overflow: hidden;
}
.rc-color-main {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1px;
    width: 26px;
    padding: 0;
    border: none;
    background: var(--rc-ed-bg);
    color: var(--rc-ed-text);
    cursor: pointer;
}
.rc-color-main:hover { background: var(--rc-ed-accent-pale); }
.rc-color-glyph { font-size: 0.82rem; font-weight: 700; line-height: 1; }
.rc-color-bar { width: 16px; height: 4px; border-radius: 1px; }
.rc-color-arrow {
    width: 16px;
    padding: 0;
    border: none;
    border-left: 1px solid var(--rc-ed-border);
    background: var(--rc-ed-bg);
    color: var(--rc-ed-muted);
    font-size: 0.7rem;
    cursor: pointer;
}
.rc-color-arrow:hover { background: var(--rc-ed-accent-pale); color: var(--rc-ed-text); }
.rc-color-palette {
    position: fixed;
    z-index: 10000;
    background: var(--rc-ed-bg, #fff);
    border: 1px solid var(--rc-ed-border, #ccc);
    border-radius: 6px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.22);
    padding: 8px;
}
.rc-color-grid {
    display: grid;
    grid-template-columns: repeat(5, 18px);
    gap: 4px;
}
.rc-color-swatch {
    width: 18px;
    height: 18px;
    padding: 0;
    border: 1px solid rgba(0, 0, 0, 0.25);
    border-radius: 3px;
    cursor: pointer;
}
.rc-color-swatch:hover { outline: 2px solid var(--rc-ed-accent); outline-offset: 1px; }
.rc-color-none,
.rc-color-custom {
    display: block;
    width: 100%;
    margin-top: 6px;
    font-size: 0.76rem;
    color: var(--rc-ed-text);
}
.rc-color-none {
    padding: 4px 6px;
    border: 1px solid var(--rc-ed-border);
    border-radius: 4px;
    background: var(--rc-ed-bg);
    cursor: pointer;
    text-align: left;
}
.rc-color-none:hover { background: var(--rc-ed-accent-pale); }
.rc-color-custom { cursor: pointer; }
.rc-color-custom input[type="color"] {
    vertical-align: middle;
    width: 28px;
    height: 20px;
    padding: 0;
    border: 1px solid var(--rc-ed-border);
    border-radius: 3px;
    background: transparent;
    cursor: pointer;
}
.rc-tiptap-select {
    height: 28px;
    font-size: 0.8rem;
    color: var(--rc-ed-text);
    background: var(--rc-ed-bg);
    border: 1px solid var(--rc-ed-border);
    border-radius: 5px;
    padding: 0 4px;
    cursor: pointer;
    max-width: 130px;
}
/* T-136 — the Styles dropdown holds named-style labels (can be longer); give it
   a little more room than the font/size selects. */
.rc-style-select { max-width: 160px; font-weight: 500; }
.rc-tiptap-link-input {
    height: 28px;
    min-width: 240px;
    font-size: 0.82rem;
    padding: 0 8px;
    color: var(--rc-ed-text);
    background: var(--rc-ed-bg);
    border: 1px solid var(--rc-ed-accent);
    border-radius: 5px;
}

.rc-tiptap-editor {
    flex: 1 1 auto;
    overflow: auto;
    padding: 24px 32px;
    background: var(--rc-ed-bg);
}

/* The ProseMirror editable region (TipTap renders into .rc-tiptap-content). */
.rc-tiptap-editor .ProseMirror,
.rc-tiptap-content {
    outline: none;
    /* Match the source SOP's typography + use the full editor width so line
       wrapping matches the document (the old 70ch cap made it narrow and wrap
       differently). */
    font-family: 'Times New Roman', Times, Georgia, serif;
    font-size: 1rem;
    line-height: 1.5;
    color: var(--rc-ed-text);
    max-width: none;
}

.rc-tiptap-content:focus { outline: none; }

.rc-tiptap-content p { margin: 0 0 0.85em; }

/* House heading styles (Craig 2026-06-27 — Option 1: accept ADA's semantic model,
   make recognised headings READ clearly as headings). ADA captures heading STRUCTURE
   (the `#` markers) but no font/size/colour, so the editor supplies its own clear,
   consistent hierarchy: brand-blue accent + explicit weight + sized steps. H1/H2 also
   carry a hairline accent rule so top-level sections demarcate at a glance (mirrors
   the source docs' ruled chapter headings). Dark-mode re-asserts the colour below. */
.rc-tiptap-content h1,
.rc-tiptap-content h2,
.rc-tiptap-content h3,
.rc-tiptap-content h4 {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: var(--rc-ed-accent);
    font-weight: 700;
    line-height: 1.25;
    margin: 1.3em 0 0.5em;
}
@media (prefers-color-scheme: dark) { .rc-tiptap-content h1,
.rc-tiptap-content h2,
.rc-tiptap-content h3,
.rc-tiptap-content h4 { color: var(--rc-ed-accent); } }
.rc-tiptap-content h1 { font-size: 1.95rem; letter-spacing: -0.01em; }
.rc-tiptap-content h2 { font-size: 1.55rem; }
.rc-tiptap-content h3 { font-size: 1.22rem; font-weight: 600; }
.rc-tiptap-content h4 { font-size: 1.06rem; font-weight: 600; }
.rc-tiptap-content h1,
.rc-tiptap-content h2 {
    border-bottom: 1px solid var(--rc-ed-border);
    padding-bottom: 0.2em;
}

.rc-tiptap-content ul,
.rc-tiptap-content ol { margin: 0 0 0.85em; padding-left: 1.6em; }
/* Restore list markers — a global reset elsewhere sets list-style:none, so the
   editor's bullets/numbers were invisible without re-asserting them here. */
.rc-tiptap-content ul { list-style: disc outside; }
.rc-tiptap-content ol { list-style: decimal outside; }
.rc-tiptap-content li { margin: 0.2em 0; display: list-item; }
.rc-tiptap-content li > p { margin: 0; }

/* Tables */
.rc-tiptap-content table {
    border-collapse: collapse;
    width: 100%;
    margin: 0.85em 0;
    table-layout: fixed;
}
.rc-tiptap-content td,
.rc-tiptap-content th {
    border: 1px solid var(--rc-ed-border);
    padding: 6px 9px;
    vertical-align: top;
    position: relative;
}
.rc-tiptap-content th {
    background: var(--rc-ed-accent-pale);
    font-weight: 600;
    text-align: left;
}

/* Table of Contents — proper-ToC rendering (Craig 2026-06-27, sandbox v4 "Option B").
   The converter (_rcConvertTocTable, tiptap_setup.js) tags the table [data-rc-toc],
   strips the source's literal dot-leader runs, and splits each chapter's inline page
   number into its own cell. Here we make it READ like a contents page: borderless,
   the leader redrawn as a CSS dotted rule, the page number flush-right, sub-entries
   indented, chapter rows bold.
   table-layout:auto (NOT the default :fixed) — the ToC's first row is the lone
   colspan "Table of Contents" title, so fixed-layout with no colgroup widths would
   collapse to equal 50/50 columns (the original wrap bug). Auto self-sizes the number
   column narrow (to its widest page number) and hands the rest to the entry column. */
.rc-tiptap-content table[data-rc-toc] {
    table-layout: auto;
    border: 0;
}
.rc-tiptap-content table[data-rc-toc] td {
    border: 0;
    padding: 3px 0;
    vertical-align: baseline;
}
.rc-tiptap-content table[data-rc-toc] td p { margin: 0; }
/* page-number cell (2nd cell of an entry/chapter row) — flush right, never wraps.
   :not(:first-child) excludes the single-cell title row from right-alignment. */
.rc-tiptap-content table[data-rc-toc] td:last-child:not(:first-child) {
    text-align: right;
    white-space: nowrap;
    padding-left: 6px;
}
/* entry cell — text + a flex dot-leader filling out to the page number */
.rc-tiptap-content table[data-rc-toc] td:first-child:not(:last-child) p {
    display: flex;
    align-items: baseline;
    white-space: nowrap;
}
.rc-tiptap-content table[data-rc-toc] td:first-child:not(:last-child) p::after {
    content: "";
    flex: 1 1 auto;
    border-bottom: 1.5px dotted var(--rc-ed-muted);
    margin: 0 5px;
    transform: translateY(-0.28em);
    min-width: 18px;
}
/* sub-entries indented; chapter rows stay flush-left + bold */
.rc-tiptap-content table[data-rc-toc] tr:not([data-rc-toc-chap]) td:first-child:not(:last-child) p {
    padding-left: 1.6em;
}
.rc-tiptap-content table[data-rc-toc] tr[data-rc-toc-chap] p { font-weight: 700; }
.rc-tiptap-content .selectedCell::after {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--rc-ed-accent);
    opacity: 0.12;
    pointer-events: none;
}
/* TipTap column-resize handle */
.rc-tiptap-content .column-resize-handle {
    position: absolute;
    right: -2px;
    top: 0;
    bottom: 0;
    width: 4px;
    background: var(--rc-ed-accent);
    opacity: 0.4;
    pointer-events: none;
}

/* ── Tier 3 — images + retain-don't-drop preserved blocks ────────────────── */
.rc-tiptap-content img {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 0.6em 0;
}
.rc-tiptap-content img.ProseMirror-selectednode {
    outline: 2px solid var(--rc-ed-accent);
}
/* Preserved block: header/footer + footnotes show their retained text; opaque
   `preserved` (sectPr/sdt) shows just the label chip. The source is kept in the
   node's `raw`/`text` attrs (round-trips on save) even though it's not richly
   rendered — the losslessness guarantee. */
.rc-preserved {
    border: 1px dashed var(--rc-ed-border);
    border-left: 3px solid var(--rc-ed-muted);
    border-radius: 4px;
    background: var(--rc-ed-toolbar);
    padding: 8px 12px;
    margin: 0.7em 0;
    font-size: 0.86rem;
}
.rc-preserved-label {
    display: inline-block;
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--rc-ed-muted);
    margin-bottom: 4px;
}
.rc-preserved-text {
    white-space: pre-wrap;
    color: var(--rc-ed-text);
}
/* #2317 — a figure the import could not read says so under the picture; the same
   words and look as the Document view's .g-notstated note. */
.rc-preserved-note { font-style: italic; color: var(--text-muted); font-size: 13px; margin-top: 4px; }
/* Build A (one-surface ADA render) — the real cropped image inside a display-only
   card (figure/logo/signature). Rendered before the description (which becomes a
   small caption). Display-only: the atom is non-editable + the <img> is inert.
   Colours ride the editor-local --rc-ed-* vars, which honour prefers-color-scheme
   via the editor dark-mode remap (no hardcoded palette). */
.rc-preserved-img {
    display: block;
    max-width: 100%;
    height: auto;
    border-radius: 5px;
    border: 1px solid var(--rc-ed-border);
    margin: 2px 0 8px;
    background: light-dark(#fff, var(--pq-surface));   /* a white mat so transparent PNG crops read on dark + light */
}
.rc-preserved-preserved .rc-preserved-text { display: none; }
/* B-026 — opaque "retain-don't-drop" markers (Word bookmarks, comment ranges,
   sectPr page setup, content-control sdt, proofErr, etc.) are captured for
   lossless round-trip but carry NO reviewer-facing content, so the placeholder
   block "Preserved (bookmarkEnd)" is just noise in the editor. Suppress it
   VISUALLY only — the preservedBlock node + its raw XML stay in the document
   (getJSON serialises them), so a load→save round-trip remains lossless and a
   future "show preserved markers" toggle can simply un-hide this. Header/Footer
   + Footnotes preserved blocks carry text and use their OWN classes
   (rc-preserved-header_footer / -footnotes) — they stay visible. (Craig
   2026-05-27: "suppress is the right answer — we can't drop stuff we don't
   understand; one day we may need to show these somehow in the editor.") */
.rc-preserved-preserved { display: none; }

/* T-128 (Craig 2026-06-19, sandbox-verified) — render the two retained elements
   that carry real reviewer-facing content as proper regions instead of dashed
   "chips". Real page breaks (run-level <w:br w:type="page"/> + pageBreakBefore)
   are NOW captured structurally (document_parser._para_break_info → `page_break`
   section → pageBreak node, see .rc-page-break below); the final-sectPr stays an
   opaque preserved block (it is the doc's section properties, not a break). */
/* Page break → a labelled horizontal rule, so the reviewer sees where the
   controlled document paginates (round-trips to a real Word page break). */
.rc-page-break {
    display: flex; align-items: center; gap: 10px;
    margin: 18px 0; user-select: none;
}
.rc-page-break::before, .rc-page-break::after {
    content: ""; flex: 1 1 auto;
    border-top: 1px dashed var(--rc-ed-muted);
}
.rc-page-break-label {
    flex: 0 0 auto; font-size: 0.62rem; font-weight: 600;
    letter-spacing: 0.1em; text-transform: uppercase;
    color: var(--rc-ed-muted);
}
/* Footnotes → a quiet footnote region (a rule + the retained footnote text). */
.rc-preserved-footnotes {
    border: 0; border-top: 1px solid var(--rc-ed-border); border-radius: 0;
    background: transparent; margin: 24px 0 0; padding: 8px 0 0;
}
.rc-preserved-footnotes .rc-preserved-label { color: var(--rc-ed-muted); }
.rc-preserved-footnotes .rc-preserved-text {
    display: block; white-space: pre-wrap; font-size: 0.82rem;
    color: var(--rc-ed-text); margin-top: 4px;
}
/* Header → a quiet document-header banner (rendered ONCE at the top; no pages in
   the editor, so the old docx-preview per-page header failure cannot recur). The
   STRUCTURED version — table layout + logo — is a tracked LATER (see L-049). */
.rc-preserved-header_footer {
    border: 1px solid var(--rc-ed-border); border-left: 1px solid var(--rc-ed-border);
    border-radius: 6px; background: var(--rc-ed-toolbar); padding: 7px 14px; margin: 0 0 20px;
}
.rc-preserved-header_footer .rc-preserved-label {
    display: block; font-size: 0.6rem; letter-spacing: 0.09em; color: var(--rc-ed-muted); margin-bottom: 2px;
}
.rc-preserved-header_footer .rc-preserved-text {
    display: block; white-space: pre-wrap; font-size: 0.8rem; color: var(--rc-ed-text);
}
/* Header/footer RECORD bands (Craig 2026-06-27: "if it looks like a header record
   it needs to behave like one"). The backend (classify_furniture) tags the ONE
   kept header + ONE kept footer; chunksToTiptap hoists them (header → top, footer
   → bottom) and stamps data-rc-zone. Styled as a quiet editable band — distinct
   from body so it reads as a header/footer record, but the TEXT stays editable;
   on export the block becomes the Word header/footer part. Vars are light/dark-
   aware. The band wraps whatever the record is — a paragraph, a table, or a logo. */
.rc-tiptap-content [data-rc-zone="header"],
.rc-tiptap-content [data-rc-zone="footer"] {
    position: relative;
    border: 1px solid var(--rc-ed-border);
    border-radius: 6px;
    background: var(--rc-ed-toolbar);
    padding: 11px 14px 9px;
    margin: 0 0 22px;
}
.rc-tiptap-content [data-rc-zone="footer"] { margin: 24px 0 0; }
.rc-tiptap-content [data-rc-zone="header"]::before,
.rc-tiptap-content [data-rc-zone="footer"]::before {
    content: "HEADER";
    position: absolute; top: -8px; left: 12px;
    font-size: 0.58rem; letter-spacing: 0.1em; font-weight: 600;
    color: var(--rc-ed-muted); background: var(--rc-ed-bg); padding: 0 5px;
    font-family: system-ui, -apple-system, sans-serif;
}
.rc-tiptap-content [data-rc-zone="footer"]::before { content: "FOOTER"; }

/* L-049 — STRUCTURED document-header banner: render the Word header's table
   grid (logo-left / title-centre / doc-info-right) + logo image, not just text.
   Rendered ONCE at the top (no pagination in the editor). Falls back to the
   text banner above when no structure was captured. */
.rc-docheader {
    border: 1px solid var(--rc-ed-border); border-radius: 6px;
    background: var(--rc-ed-toolbar); padding: 10px 16px; margin: 0 0 20px;
    user-select: none;
}
.rc-docheader-tag {
    display: block; font-size: 0.6rem; letter-spacing: 0.09em;
    text-transform: uppercase; color: var(--rc-ed-muted); margin-bottom: 6px;
}
.rc-docheader-table {
    width: 100%; border-collapse: collapse; table-layout: fixed;
}
.rc-docheader-table td {
    vertical-align: middle; padding: 2px 8px; border: 0;
    font-size: 0.8rem; color: var(--rc-ed-text);
}
/* logo column (first) left, doc-info column (last) right, title (middle) centred */
.rc-docheader-table td:first-child { text-align: left; }
.rc-docheader-table td:last-child  { text-align: right; }
.rc-docheader-table tr td:not(:first-child):not(:last-child) { text-align: center; }
.rc-docheader-img { max-height: 40px; max-width: 100%; object-fit: contain; }
/* B-090 — the header logo rendered inside a table cell is the ONLY img source in
   tiptap table cells; cap its height so a wide/colspan cell can't blow it up to
   full container width (the "huge spanning copy"). */
.rc-tiptap-content td img, .rc-tiptap-content th img { max-height: 40px; width: auto; object-fit: contain; }

/* B-099 / #299 — the source header/footer bands are NEVER rendered. Was
   "hidden by default with a debug escape hatch"; the hatch is burned (charter
   rule 7) because #299 replaced the facsimile with the DocumentHeader record,
   which every treatment renders from `#doc-header-host`. The band's bytes
   deliberately REMAIN in the stored content as provenance — docx export reads
   them back into the real Word header/footer parts (docx_writer._emit_zone) —
   they are simply never drawn on screen. That is the whole #299 posture: the
   source header is provenance, not content. */
.rc-tiptap-content [data-rc-zone="header"],
.rc-tiptap-content [data-rc-zone="footer"],
.rc-tiptap-content .rc-docheader,
.rc-tiptap-content [data-doc-header] { display: none !important; }
.rc-tiptap-content td:first-child { width: 60px; }
.rc-tiptap-content td:last-child  { width: 48%; }

/* B-100 — the Edit-step ACTIVITY editor: ~6-line min height + wrap long single lines
   (was content-height, so single lines showed one line and long lines didn't wrap). */
.pg-editpop .ProseMirror { min-height: 8.5em; overflow-wrap: anywhere; word-break: break-word; }
.rc-docheader-celltext { white-space: pre-wrap; line-height: 1.4; }
.rc-docheader-paras { display: flex; flex-direction: column; gap: 2px; align-items: flex-start; }

/* ── Tier-1 full-fidelity inline styling ──────────────────────────────────
   Links, super/sub-script, highlight + paragraph alignment captured from the
   source docx. Colour / font-size / font-family ride on inline style attrs the
   FontSize/Color/FontFamily extensions emit, so they need no rule here. */
.rc-tiptap-content a {
    color: var(--rc-ed-accent);
    text-decoration: underline;
    cursor: pointer;
}
.rc-tiptap-content a:hover { text-decoration: underline; opacity: 0.85; }
.rc-tiptap-content sup { vertical-align: super; font-size: 0.75em; line-height: 0; }
.rc-tiptap-content sub { vertical-align: sub;   font-size: 0.75em; line-height: 0; }
/* Highlight mark — colour comes from the inline background-color the Highlight
   extension renders (multicolor). This rule just guarantees readable padding
   and keeps the default chrome if a swatch ever renders without inline colour. */
.rc-tiptap-content mark {
    background-color: #fff3a3;
    color: inherit;
    padding: 0 0.05em;
    border-radius: 2px;
}


@media (prefers-color-scheme: dark) {
    .rc-tiptap-wrap {
        --rc-ed-bg: var(--pq-night-900);
        --rc-ed-text: var(--pq-mist-200);
        --rc-ed-muted:    #a0aec0;
        --rc-ed-border:   var(--pq-border);
        --rc-ed-toolbar:  var(--pq-ground);
        --rc-ed-accent:   #7aa7d6;
        --rc-ed-accent-pale: var(--pq-elevated);
    }
    /* Headings use the accent colour; in dark mode keep them legible. */
    
    
    /* Highlighted text fallback sits on a bright swatch — force dark glyphs so
       it stays readable even when the page is in dark mode. (When the source
       docx carried an explicit highlight colour it renders inline; this only
       covers the fallback swatch above.) */
    .rc-tiptap-content mark { color: #1a202c; }
    /* Links: keep the accent legible against the dark editor background. */
    .rc-tiptap-content a { color: var(--rc-ed-accent); }
}



/* ─────────────────────────────────────────────────────────────────────────
   T-118 C8.3 — Finding-highlight DECORATIONS (TipTap / ProseMirror surface).

   Rendered by finding_decoration.js as inline ProseMirror decorations, NOT by
   DOM-wrapping. Class shape: `rc-finding rc-finding-<category>` (+ `is-active`
   for the SELECTED finding). This is the ONLY finding paint — the legacy
   `.hl` / `.hl-block` pipeline and its CSS burned with B-115 WP1-WP3.

   Visual contract (Craig UI-OBS #1/#2/#3/#5 + L-023):
   - ONE unified bordered-BOX per finding, every category — no left-bar /
     underline-wash variants, no `:has()` inference. A multi-paragraph finding
     is a single decoration range, so the browser paints ONE continuous box
     across the wrapped text rather than double-painting per paragraph.
   - Per-category colour shared with the finding cards (the --rc-find-* vars
     reuse the same per-category *-bar colours). CRITICAL GAP nudged toward red.
   - SELECTED state is obvious + UNIFORM across every category: the active
     finding's box uses the DARKER per-category `-sel` fill + a heavier border.
   - Honours prefers-color-scheme: dark via the editor-local var remap below.
   ───────────────────────────────────────────────────────────────────────── */
/* ── THE CLEAN COPY (Craig 2026-07-30) ───────────────────────────────────────
   "a button next to the Pills on the document view which shows the document
   clean, or toggle the finding anchors on or off ... with the words Controlled
   document, Uncontrolled when printed"

   Suppression is presentational ONLY. The anchor elements stay in the DOM with
   their handlers, their active state and their scroll targets intact — this
   removes the paint, not the machinery, so toggling back needs to restore
   nothing. `all: unset` would take the cursor and the transitions with it and
   leave the element unable to come back cleanly, so each painted property is
   named. */
.rc-clean-copy .rc-finding {
    background-color: transparent !important;
    border-color: transparent !important;
    /* padding is KEPT: removing it would reflow every anchored block and the
       document would visibly jump on each toggle, which is the opposite of
       "shows the document clean". */
}
/* The active-highlight ring is the one anchor mark that is about the READER's
   place rather than about a finding, but in a clean copy there is nothing to be
   placed against.
   #1575 — this named `box-shadow` on `.rc-finding-active` only, and the mark is
   drawn mostly with `outline`, which is not a shadow and which `all: unset` was
   rejected for reaching. So a selected anchor's OUTLINE already survived onto a
   clean copy, on the editor surface, before this change — strengthening the mark
   would have made that plainly visible. Name every property the mark paints
   with, on the shared class both surfaces now use. */
.rc-clean-copy .rc-finding.is-active {
    outline: none !important;
    outline-offset: 0 !important;
    box-shadow: none !important;
    animation: none !important;
}
/* #1665 — the eleven per-category `.rc-finding-<category>.is-active`
   background-color rules were deleted here. They were the same
   selection-fill signal as the rule above, repeated once per category,
   and Craig ruled the fill change out: the box is sufficient and the
   fill said "different category" in a vocabulary already spoken for.
   The `--rc-find-*-sel` tokens remain declared; retiring those is the
   colour system's own pass, not this one. */
/* Structural anchors, the dimmed state, and any category edge follow the same
   rule — they are all anchor paint. */
.rc-clean-copy .rc-finding-dimmed { opacity: 1 !important; }

/* The control statement. Hidden in the working view, which shows findings and
   so is not the document as released. */
.doc-control-stmt { display: none; }
.rc-clean-copy .doc-control-stmt {
    display: block;
    text-align: center;
    font-size: 0.72rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted);
    border-bottom: 1px solid var(--border);
    padding: 8px 12px 10px;
    margin-bottom: 14px;
}

/* The toggle sits in the pill row and must not read as an eighth CATEGORY —
   it counts nothing. Outline rather than fill, per the button standard.
   #1653 — it used to borrow `.stat-pill` for its padding, radius and size, so
   when the pill became the chip this control would have silently lost its
   frame. It now states its own geometry, matched to the chip's so it sits as a
   peer in the row rather than inheriting from a component it is not. */
.rc-clean-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: 10px;
    padding: 6px 9px 5px;
    border-radius: 3px;
    font-size: 12px;
    font-weight: 500;
    line-height: 1;
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text-muted);
    cursor: pointer;
}
.rc-clean-btn:hover { border-color: var(--primary-fg); color: var(--primary-fg); }
.rc-clean-btn.is-active {
    border-color: var(--primary-fg);
    color: var(--primary-fg);
    font-weight: 600;
}
.rc-clean-btn .ic { width: 13px; height: 13px; }

/* Printing a clean copy is the case the statement exists for, so the anchors go
   whatever the toggle says — a print must never carry working marks. */
@media print {
    .rc-finding { background-color: transparent !important; border-color: transparent !important; }
    .doc-control-stmt { display: block !important; }
    .rc-clean-btn { display: none !important; }
}

/* BIG-BOX model (Craig issue 1): `.rc-finding` now lands on BLOCK nodes via
   Decoration.node (paragraph / heading / list item / table-cell paragraph), not
   on inline spans. Each finding paints a bordered BOX around its block(s). We
   use a real per-side `border` (default unknown edge) + tinted fill, plus the
   `--rc-find-*-edge` colour variable so per-category rules only swap the colour
   and fill. The continuous-box role classes (single/first/mid/last) suppress
   the borders BETWEEN a multi-block finding's blocks so the boxes read as ONE
   continuous box (border-top only on the first, border-bottom only on the last,
   left/right on all). */
.rc-finding {
    cursor: pointer;
    transition: background-color 0.12s, border-color 0.12s;
    /* default (unknown) fill/edge; per-category rules below override */
    --rc-box-edge: var(--rc-find-unknown-edge);
    --rc-box-width: 1px;
    background-color: var(--rc-find-unknown-fill);
    border: var(--rc-box-width) solid var(--rc-box-edge);
    /* small inner breathing room so the box hugs the text, not the glyphs */
    padding: 2px 4px;
}

/* Continuous-box roles — collapse the shared edges between stacked blocks of one
   finding so they paint as a single box. The horizontal radius is kept on the
   outer corners only. */
.rc-finding-box-single { border-radius: 4px; }
/* L-025 (Craig 2026-06-19, sandbox-verified) — a multi-block finding was
   FRAGMENTING into separate boxes because the editor's inter-paragraph MARGIN
   left white gaps between the stacked block decorations. Collapse those between
   margins (the dropped inter-borders then join into ONE continuous box); inner
   padding keeps the text from cramping. !important beats the more-specific
   `.rc-tiptap-content p` margin rule. */
.rc-finding-box-first  { border-bottom: 0; border-radius: 4px 4px 0 0; margin-bottom: 0 !important; padding-bottom: 7px; }
.rc-finding-box-mid    { border-top: 0; border-bottom: 0; border-radius: 0; margin-top: 0 !important; margin-bottom: 0 !important; padding-top: 7px; padding-bottom: 7px; }
.rc-finding-box-last   { border-top: 0; border-radius: 0 0 4px 4px; margin-top: 0 !important; padding-top: 7px; }

/* Per-category colour (matches the finding cards via the shared --rc-find-* vars). */
.rc-finding-conflict          { --rc-box-edge: var(--rc-find-conflict-edge);    background-color: var(--rc-find-conflict-fill); }
.rc-finding-incomplete      { --rc-box-edge: var(--rc-find-gap-edge);         background-color: var(--rc-find-gap-fill); }
.rc-finding-missing           { --rc-box-edge: var(--rc-find-missing-edge);     background-color: var(--rc-find-missing-fill); }
.rc-finding-vague             { --rc-box-edge: var(--rc-find-vague-edge);       background-color: var(--rc-find-vague-fill); }
.rc-finding-unnecessary_detail{ --rc-box-edge: var(--rc-find-unnecessary-edge); background-color: var(--rc-find-unnecessary-fill); }
.rc-finding-ai_suggestion     { --rc-box-edge: var(--rc-find-ai-edge);          background-color: var(--rc-find-ai-fill); }
.rc-finding-inserted          { --rc-box-edge: var(--rc-find-inserted-edge);    background-color: var(--rc-find-inserted-fill); }
/* B-205 — was falling through to the unknown-grey box (see _findingBoxCat). */
.rc-finding-structural        { --rc-box-edge: var(--rc-find-structural-edge);  background-color: var(--rc-find-structural-fill); }

/* #325 / #708 — the DISPOSITION states. The box round the text now says what
   happened to the finding, which is what Craig reported missing: "the finding
   card is green while its document anchor is not".

   AGREED IS DASHED, APPLIED IS SOLID, and that distinction is the whole of #325.
   `inserted` means the agreed text is physically in the document; `resolved`
   means it was agreed in Expert Review and has NOT been applied. Both are green
   because both are resolution-side, so the difference has to be carried by the
   EDGE — a broken outline reads as "settled but not yet done", which is exactly
   what #325's own comment proposed ("the finding's own colour with the outline
   broken or hollowed"). The card already uses a dashed border for the same two
   states, so the two surfaces now say it the same way.

   THE EXACT TREATMENT IS CRAIG'S TO JUDGE BY LOOKING, not by reasoning — his
   words on #325. This is a defensible first cut built on the card's own
   existing grammar; a sandbox comparison is the way to settle it. */
.rc-finding-resolved          { --rc-box-edge: var(--rc-find-resolved-edge);    background-color: var(--rc-find-resolved-fill); border-style: dashed; }
.rc-finding-acted             { --rc-box-edge: var(--rc-find-acted-edge);       background-color: var(--rc-find-acted-fill);    border-style: dashed; }
.rc-finding-rejected          { --rc-box-edge: var(--rc-find-rejected-edge);    background-color: var(--rc-find-rejected-fill); border-style: dashed; }

.rc-finding:hover { filter: brightness(0.95); }

/* WP3 (v4.1.279, M3) — filter-pill dimming, decoration-side. A finding whose
   category is filtered out fades but stays visible/clickable. 0.55 matches the
   calibrated legacy .hl.dimmed value. */
.rc-finding-dimmed { opacity: 0.55; }

/* SELECTED — THE BOX, AND ONLY THE BOX.
   #1665, Craig 2026-09-20: "when the item is selected in the text it then
   becomes a different color from the card, which is confusing, the bold box
   around it would be sufficient".

   WHAT WAS WRONG WITH TWO SIGNALS, because the previous note here defended
   them as deliberate ("darker fill + heavier border, applied uniformly so the
   active finding is unmistakable"). The reasoning was sound about EMPHASIS and
   wrong about MEANING: on this surface COLOUR ENCODES CATEGORY, everywhere
   else and consistently. Changing the fill on selection therefore said
   "different category" in the one vocabulary the surface has already committed
   to that job, so a selected conflict read as some other kind of finding. The
   border carries emphasis without borrowing a meaning that is already taken.

   The per-category `-sel` fills below go with it — they were the same signal,
   once per category. The `--rc-find-*-sel` TOKENS are left declared: they are
   the colour system's business and #1655 territory, and deleting tokens is a
   different pass from deleting the rules that used them. */
.rc-finding.is-active                       {
    --rc-box-width: 2px;
    outline: 3px solid var(--rc-anchor-ring, #2563eb);
    outline-offset: 2px;
    box-shadow: inset 4px 0 0 0 var(--rc-anchor-ring, #2563eb),
                0 0 0 5px rgba(37, 99, 235, .16),
                0 4px 14px rgba(15, 23, 42, .18);
    position: relative;
    z-index: 2;
    animation: rc-anchor-pulse 900ms ease-out 1;
}

/* ── B-133 — T1 "paint the CELL" (Craig's sandbox pick, decoration-sandbox.html,
   2026-07-03). A decorated paragraph that is a table cell's direct child moves
   its visual box onto the CELL: an inset ring + fill span the full cell height,
   so a row-distribute accept paints four uniform boxes regardless of per-cell
   text length (no more short boxes floating in tall cells). The finding anchor,
   click target, and data-fid stay on the PARAGRAPH node — this is CSS-only.
   Category colour rides --rc-cell-* vars set per category via :has() (the cell
   is the parent, so the paragraph's own --rc-box-* vars can't reach it). */
.rc-tiptap-content td:has(> p.rc-finding),
.rc-tiptap-content th:has(> p.rc-finding) {
    box-shadow: inset 0 0 0 var(--rc-cell-box-width, 1px) var(--rc-cell-box-edge, var(--rc-find-unknown-edge));
    background-color: var(--rc-cell-box-fill, var(--rc-find-unknown-fill));
}
.rc-tiptap-content td > p.rc-finding,
.rc-tiptap-content th > p.rc-finding {
    background: transparent;
    border: 0;
    padding: 0;
    border-radius: 0;
}
.rc-tiptap-content td:has(> p.rc-finding-conflict),           .rc-tiptap-content th:has(> p.rc-finding-conflict)           { --rc-cell-box-edge: var(--rc-find-conflict-edge);    --rc-cell-box-fill: var(--rc-find-conflict-fill); }
.rc-tiptap-content td:has(> p.rc-finding-incomplete),       .rc-tiptap-content th:has(> p.rc-finding-incomplete)       { --rc-cell-box-edge: var(--rc-find-gap-edge);         --rc-cell-box-fill: var(--rc-find-gap-fill); }
.rc-tiptap-content td:has(> p.rc-finding-missing),            .rc-tiptap-content th:has(> p.rc-finding-missing)            { --rc-cell-box-edge: var(--rc-find-missing-edge);     --rc-cell-box-fill: var(--rc-find-missing-fill); }
.rc-tiptap-content td:has(> p.rc-finding-vague),              .rc-tiptap-content th:has(> p.rc-finding-vague)              { --rc-cell-box-edge: var(--rc-find-vague-edge);       --rc-cell-box-fill: var(--rc-find-vague-fill); }
.rc-tiptap-content td:has(> p.rc-finding-unnecessary_detail), .rc-tiptap-content th:has(> p.rc-finding-unnecessary_detail) { --rc-cell-box-edge: var(--rc-find-unnecessary-edge); --rc-cell-box-fill: var(--rc-find-unnecessary-fill); }
.rc-tiptap-content td:has(> p.rc-finding-ai_suggestion),      .rc-tiptap-content th:has(> p.rc-finding-ai_suggestion)      { --rc-cell-box-edge: var(--rc-find-ai-edge);          --rc-cell-box-fill: var(--rc-find-ai-fill); }
/* B-159 — the `inserted` (accepted-change) rules live BELOW the .is-active block
   (see end of this cascade) so an inserted/changed cell keeps its OWN colour even
   when another finding in the same mixed-category cell is the active one. */
/* selected / hover / dimmed states follow the cell paint */
.rc-tiptap-content td:has(> p.rc-finding.is-active),
.rc-tiptap-content th:has(> p.rc-finding.is-active)                       { --rc-cell-box-width: 2px; --rc-cell-box-fill: var(--rc-find-unknown-sel); }
.rc-tiptap-content td:has(> p.rc-finding-conflict.is-active),             .rc-tiptap-content th:has(> p.rc-finding-conflict.is-active)             { --rc-cell-box-fill: var(--rc-find-conflict-sel); }
.rc-tiptap-content td:has(> p.rc-finding-incomplete.is-active),         .rc-tiptap-content th:has(> p.rc-finding-incomplete.is-active)         { --rc-cell-box-fill: var(--rc-find-gap-sel); }
.rc-tiptap-content td:has(> p.rc-finding-missing.is-active),              .rc-tiptap-content th:has(> p.rc-finding-missing.is-active)              { --rc-cell-box-fill: var(--rc-find-missing-sel); }
.rc-tiptap-content td:has(> p.rc-finding-vague.is-active),                .rc-tiptap-content th:has(> p.rc-finding-vague.is-active)                { --rc-cell-box-fill: var(--rc-find-vague-sel); }
.rc-tiptap-content td:has(> p.rc-finding-unnecessary_detail.is-active),   .rc-tiptap-content th:has(> p.rc-finding-unnecessary_detail.is-active)   { --rc-cell-box-fill: var(--rc-find-unnecessary-sel); }
.rc-tiptap-content td:has(> p.rc-finding-ai_suggestion.is-active),        .rc-tiptap-content th:has(> p.rc-finding-ai_suggestion.is-active)        { --rc-cell-box-fill: var(--rc-find-ai-sel); }
/* B-159 — `inserted` paint sits LAST so it beats every other category's base and
   .is-active fill: a cell that holds an accepted change keeps its inserted colour
   regardless of which finding is currently active. When the inserted finding is
   itself active it still gets its own selected variant (second rule below). */
.rc-tiptap-content td:has(> p.rc-finding-inserted),                       .rc-tiptap-content th:has(> p.rc-finding-inserted)                       { --rc-cell-box-edge: var(--rc-find-inserted-edge);    --rc-cell-box-fill: var(--rc-find-inserted-fill); }
.rc-tiptap-content td:has(> p.rc-finding-inserted.is-active),             .rc-tiptap-content th:has(> p.rc-finding-inserted.is-active)             { --rc-cell-box-fill: var(--rc-find-inserted-sel); }
.rc-tiptap-content td:has(> p.rc-finding:hover),
.rc-tiptap-content th:has(> p.rc-finding:hover) { filter: brightness(0.97); }
.rc-tiptap-content td:has(> p.rc-finding-dimmed),
.rc-tiptap-content th:has(> p.rc-finding-dimmed) { opacity: 0.55; }

/* ── #34 / B-159 — MIXED-category cell paint. The T1 flatten gives a cell ONE
   fill, and the .is-active rules above made that fill FOLLOW whichever child is
   active — so a cell hosting findings of two different categories flipped colour
   as you clicked different findings (post-accept it read gap-red until clicked).
   A genuinely mixed cell cannot honestly show any single category's colour, so it
   gets ONE STABLE "mixed" treatment that never follows the active finding: a
   neutral hatch (reads as "multiple findings here"), and the active finding is
   emphasised by an OUTLINE on its OWN paragraph — NOT by repainting the shared
   cell. The inserted-wins case (an accepted-change cell beside one analyser
   category) is deliberately NOT mixed (`:not(.rc-finding-inserted)` in the "other"
   arm), so a changed cell keeps its stable inserted paint.

   Detector (#1903, code review FE-F57): the editor's decoration plugin marks the
   cell `data-rc-mixed` when it holds two or more DISTINCT categories, not counting
   inserted (finding_decoration.js). It replaced 32 double-`:has()` selectors that
   the browser re-evaluated on every table cell at every keystroke and that kept the
   category list in CSS. `:has(> p.rc-finding)` keeps these rules at the same
   specificity as the per-category .is-active rules above, and source order lets
   them win the cell fill - the fill can no longer flip. */
.rc-tiptap-content td[data-rc-mixed]:has(> p.rc-finding),
.rc-tiptap-content th[data-rc-mixed]:has(> p.rc-finding) {
    --rc-cell-box-width: 1px;
    --rc-cell-box-edge: var(--rc-find-unknown-edge);
    --rc-cell-box-fill: var(--rc-find-unknown-fill);
    /* Faint diagonal hatch signalling "multiple findings"; stable across theme
       and across which finding is active. Neutral grey reads on light + dark. */
    background-image: repeating-linear-gradient(135deg,
        rgba(128, 128, 128, 0.12) 0, rgba(128, 128, 128, 0.12) 5px,
        transparent 5px, transparent 10px);
}
/* The ACTIVE finding inside a mixed cell is emphasised by an outline on its own
   paragraph — layout-neutral, and it does NOT touch the shared cell fill. The
   outline colour rides the finding's own --rc-box-edge (set per category above). */
.rc-tiptap-content td[data-rc-mixed] > p.rc-finding.is-active,
.rc-tiptap-content th[data-rc-mixed] > p.rc-finding.is-active {
    outline: 2px solid var(--rc-box-edge, var(--rc-find-unknown-edge));
    outline-offset: -3px;
    border-radius: 3px;
}

/* ── B-114 / B-125 — L1 "tight run" (Craig's sandbox pick, 2026-07-03). List-item
   finding boxes close ranks: the li's vertical margins collapse inside a
   decorated run (the first/mid/last role classes then join into ONE continuous
   box, same as stacked paragraphs), and each item's box stretches back across
   the 1.6em marker gutter so its left edge aligns with the intro paragraph's
   box. List geometry (markers, indent) is untouched — only the paint reaches
   further left. */
.rc-tiptap-content li:has(> p.rc-finding) { margin: 0; }
.rc-tiptap-content ul:has(> li > p.rc-finding),
.rc-tiptap-content ol:has(> li > p.rc-finding) { margin-top: 0; }
.rc-tiptap-content li > p.rc-finding {
    margin-left: -1.6em !important;
    padding-left: calc(1.6em + 4px);
}

@media (prefers-color-scheme: dark) {
    /* Dark ground needs more alpha in the fills before a decoration box reads.
       That alpha is all these restate: the hue keeps coming from the same
       `--{stem}-bar` base as light mode, so the Settings picker reaches this
       surface too and card↔decoration colour identity holds by construction.
       (Three exceptions, called out at the foot of the block.)

       `unknown` is the one hue that has to move: slate #718096 vanishes against
       a dark page. Its BASE is lifted here, so edge/fill/sel all follow from
       the one value rather than being restated three times. */
    .rc-tiptap-wrap {
        --unknown-bar: #a0aec0;
        --rc-find-unknown-fill:     color-mix(in srgb, var(--unknown-bar) 24%, transparent);
        --rc-find-unknown-sel:      color-mix(in srgb, var(--unknown-bar) 44%, transparent);
        --rc-find-unnecessary-fill: color-mix(in srgb, var(--unnecessary-bar) 26%, transparent);
        --rc-find-unnecessary-sel:  color-mix(in srgb, var(--unnecessary-bar) 46%, transparent);
        --rc-find-ai-fill:          color-mix(in srgb, var(--ai-bar) 26%, transparent);
        --rc-find-ai-sel:           color-mix(in srgb, var(--ai-bar) 46%, transparent);
        --rc-find-inserted-fill:    color-mix(in srgb, var(--inserted-bar) 26%, transparent);
        --rc-find-inserted-sel:     color-mix(in srgb, var(--inserted-bar) 46%, transparent);

        /* #492/#444 — DERIVED NOW. These three were the last literals in this
           file that authored a finding colour: the pre-2026-06-19 hues
           (#e53e3e / #d9663b / #d69e2e), left in place because deriving them
           CHANGES the dark appearance and that was Craig's call rather than
           #492's to make.

           #444 makes it, and in the direction that removes the moving part: he
           asked for dark mode to stop being hand-picked and start satisfying a
           contract, and three hardcoded hues the Settings picker cannot reach
           are the opposite of that. Until now an admin who recoloured `conflict`
           saw it change in light mode and not in dark — one category, two
           answers, and no way to tell from the picker which one they had.

           Deriving them from the same `--{stem}-bar` bases their neighbours use
           makes dark mode a function of the light palette rather than a second
           palette maintained alongside it. The alpha stays higher than light
           mode's 18/32 because a dark ground genuinely needs more fill before a
           box reads — that is the contract, not a preference. */
        --rc-find-conflict-fill: color-mix(in srgb, var(--conflict-bar) 26%, transparent);
        --rc-find-conflict-sel:  color-mix(in srgb, var(--conflict-bar) 46%, transparent);
        --rc-find-gap-fill:      color-mix(in srgb, var(--gap-bar) 28%, transparent);
        --rc-find-gap-sel:       color-mix(in srgb, var(--gap-bar) 48%, transparent);
        --rc-find-vague-fill:    color-mix(in srgb, var(--vague-bar) 26%, transparent);
        --rc-find-vague-sel:     color-mix(in srgb, var(--vague-bar) 46%, transparent);
        --rc-find-missing-fill:  color-mix(in srgb, var(--missing-bar) 26%, transparent);
        --rc-find-missing-sel:   color-mix(in srgb, var(--missing-bar) 46%, transparent);
    }
    .rc-finding:hover { filter: brightness(1.12); }
    /* #444 — the TABLE-CELL hover had no dark override, so hovering a finding
       inside a table darkened the cell (brightness 0.97) while hovering one in a
       paragraph lightened it. Two answers to the same gesture, and SOPs are full
       of tables — the step tables are where most findings land. Same direction as
       its paragraph sibling above, gentler because a whole cell is a bigger area
       than a run of text. */
    .rc-tiptap-content td:has(> p.rc-finding:hover),
    .rc-tiptap-content th:has(> p.rc-finding:hover) { filter: brightness(1.08); }
}



/* ──────────────────────────────────────────────────────────────────────────
 * v5.0.0 M4-V1 / M4-3 — Graph view (within-document process graph)
 * ────────────────────────────────────────────────────────────────────────── */

.graph-view-header {
    padding: 10px 16px;
    border-bottom: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    gap: 8px;
    /* B-054 (Craig 2026-06-20) — the header is a structural SIBLING of the
       scrolling canvas (not inside it), but with no background + no stacking
       context the canvas floats (z-index ≤10050 per L-048) painted THROUGH it
       while scrolling, making the toolbar illegible. Give it an opaque surface
       + a z-index ABOVE the canvas floats but BELOW the top nav (10200) so the
       control bar stays readable. NOT an opaque-overlay band-aid — the
       structure was already correct. */
    background: var(--background, #fff);
    position: relative;
    z-index: 10100;
}
.graph-view-toolbar {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
}


/* v4.0.41 — drag-to-resize splitter on the LEFT edge of the drawer. */


.rc-drawer-head::after {
    /* hairline accent under the header */
    content: '';
    position: absolute;
    left: 24px; right: 24px; bottom: -1px;
    height: 1px;
    background: linear-gradient(90deg,
        var(--rc-graph-section-border, #4f6ddb) 0%,
        transparent 100%);
    opacity: 0.4;
}

@media (prefers-color-scheme: dark) {
    .rc-drawer-head {
        background: linear-gradient(180deg, #1f2330 0%, #181b25 100%);
    }
    .rc-drawer-body { background: var(--pq-ground); }
    
}


/* × Close at the top-right of the document frame.
 *
 * v4.2.64 — TWO defects fixed (Craig 2026-07-13).
 *
 *   1. IT SCROLLED. It was `position:absolute` inside `.doc-scroll` — and
 *      `.doc-scroll` IS the `overflow-y:auto` container. An absolutely-positioned
 *      child of a scrolling box is pinned to the CONTENT, so it rode away with the
 *      text. It now lives in `.doc-frame`, which does not scroll.
 *
 *   2. IT WAS PINNED TO THE PANE (`right:14px`), not the document. With the drawers
 *      shut the pane is ~600px wider, so the ✕ flew far right of the card it closes.
 *      It now anchors to the CARD's top-right — the position Craig liked when the
 *      findings pane happened to be open — via `--doc-card-w`, the live card width.
 *      The card is centred (`margin:0 auto`), so its right edge is at
 *      `50% + card/2`. Two surfaces, two widths: .doc-body is 960px, the structure
 *      view's .g-grid is 1180px — hence the :has() below rather than a constant.
 *      The clamp keeps it inside the frame on a narrow window.
 */
.doc-frame {
    position: relative;      /* the non-scrolling host — this is the whole point */
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    --doc-card-w: 960px;     /* .doc-body */
}
.doc-frame:has(#doc-structure:not(.hidden)) { --doc-card-w: 1180px; }


   /* establish positioning context */


@keyframes graph-spin {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}
.graph-loading-text {
    margin-top: 10px;
    font-size: 0.9rem;
    color: var(--text-secondary);
}
.graph-progress-bar {
    width: 220px;
    height: 6px;
    background: var(--border, #ddd);
    border-radius: 3px;
    overflow: hidden;
    margin: 12px auto 0;
}
.graph-progress-fill {
    height: 100%;
    width: 0%;
    background: var(--primary, #4f6ddb);
    transition: width 0.35s ease;
}


/* ── M4 P2 — Process Graph dark-mode token overrides (v4.0.38) ─────────────
   Backgrounds darken; borders + text brighten to keep WCAG-AA contrast.
   Honours the user's prefers-color-scheme. */
@media (prefers-color-scheme: dark) {
    :root {
        --rc-graph-action-bg:        #3b50a8;
        --rc-graph-action-border:    #6f8aff;
        --rc-graph-action-text:      #f1f5f9;

        --rc-graph-decision-bg:      #b8801e;
        --rc-graph-decision-border:  #ffc869;
        --rc-graph-decision-text:    #f8f2e3;

        --rc-graph-section-bg:       var(--pq-elevated);
        --rc-graph-section-border:   #6f8aff;
        --rc-graph-section-text:     #cfd9ff;

        --rc-graph-procedure-bg:     #1c3424;
        --rc-graph-procedure-border: #5dc77c;
        --rc-graph-procedure-text:   #cdeed4;

        --rc-graph-procedures-bg:    #16291d;
        --rc-graph-procedures-border:#5dc77c;
        --rc-graph-procedures-text:  #cdeed4;

        --rc-graph-context-bg:       #2b2417;
        --rc-graph-context-border:   #d4a23c;
        --rc-graph-context-text:     #f4e3bf;

        --rc-graph-edge-next:        #9ca3af;
        --rc-graph-edge-branch:      #ffc869;
        --rc-graph-edge-branch-text: #f4e3bf;
        --rc-graph-edge-meta: var(--pq-grey-500);

        --rc-graph-selected-border:  #f1f5f9;

        --rc-graph-chip-resp-fg:     #c7b8f0;
        --rc-graph-chip-inv-fg:      #b69ef0;
    }
}

/* #1900 (FE-F46) — the Level 2 card colours in dark mode. flow_grouped_v2.js reads
   these (cardBg, cardBorder, slot*, header*); the light values sit with the
   other graph tokens near the top of this file. The cytoscape "SOP card" overlay
   these once also styled is gone, and its rules with it. */
@media (prefers-color-scheme: dark) {
    :root {
        --rc-sop-card-bg:           var(--pq-surface);
        --rc-sop-card-border:       var(--pq-border);
        --rc-sop-card-header-bg:    var(--pq-elevated);
        --rc-sop-card-header-fg: var(--pq-mist-200);
        --rc-sop-card-header-meta:  #94a3b8;
        --rc-sop-card-slot-bg:      var(--pq-surface);
        --rc-sop-card-slot-hover:   var(--pq-line);
        --rc-sop-card-slot-fg:      #cbd5e1;
    }
}


/* ──────────────────────────────────────────────────────────────────────────
 * v4.1.8 T-143 / graph editing plan P0 — JointJS+ Process Graph inside the
 * SPA (#process-graph-wrap + process_graph.js). One canvas host per
 * document; drawer + palette CSS ported from frontend/spike-jointjs/
 * index.html (which retires at P0 close).
 * ────────────────────────────────────────────────────────────────────────── */
.process-graph-wrap {
    /* Mirror .graph-view-wrap: in-flow below the 52px app header. */
    display: flex;
    flex-direction: column;
    height: calc(100vh - 52px);
    background: var(--background, #fff);
}
.pg-main {
    flex: 1;
    display: flex;
    overflow: hidden;
    min-height: 0;
}
.pg-canvases {
    flex: 1;
    min-width: 0;
    position: relative;
    overflow: hidden;
    background: var(--bg);
    border-right: 1px solid var(--border);
}
.pg-empty {
    padding: 40px;
    color: var(--text-muted);
    font-size: 0.9rem;
    text-align: center;
}
/* One canvas host PER DOCUMENT — created on first open, shown/hidden on
   switch. Only the active doc's host is displayed; the others keep their
   rendered graph + layout alive (document-as-object, plan Q11). */
.pg-canvas-host {
    display: none;
    position: absolute;
    inset: 0;
    overflow: hidden;
}
.pg-canvas-host.pg-active { display: block; }
.pg-canvas-host .pg-paper { width: 100%; height: 100%; position: relative; }
/* v4.1.21 (U3 chunk 2, B1) — the PaperScroller fills .pg-paper and owns the
   scrollbars (horizontal + vertical). It is the scrollable viewport; the
   JointJS paper lives inside it sized to the content. */
.pg-canvas-host .pg-paper .joint-paper-scroller,
.pg-canvas-host .pg-paper .paper-scroller {
    width: 100%;
    height: 100%;
    overflow: scroll;
}
/* Craig 2026-06-18 — give the scroller a RESERVED, always-visible scrollbar
   gutter instead of macOS's thin floating overlay bar. The overlay bar sat in
   the same band as the step cards / status strip and was hard to grab on open;
   a dedicated 13px gutter (chrome, above content) is a reliable target. */
.pg-canvas-host .pg-paper .joint-paper-scroller::-webkit-scrollbar,
.pg-canvas-host .pg-paper .paper-scroller::-webkit-scrollbar { width: 13px; height: 13px; }
.pg-canvas-host .pg-paper .joint-paper-scroller::-webkit-scrollbar-track,
.pg-canvas-host .pg-paper .paper-scroller::-webkit-scrollbar-track { background: rgba(0,0,0,0.04); }
.pg-canvas-host .pg-paper .joint-paper-scroller::-webkit-scrollbar-thumb,
.pg-canvas-host .pg-paper .paper-scroller::-webkit-scrollbar-thumb {
    background: rgba(100,116,139,0.55);
    border-radius: 7px;
    border: 3px solid transparent;
    background-clip: padding-box;
}
.pg-canvas-host .pg-paper .joint-paper-scroller::-webkit-scrollbar-thumb:hover,
.pg-canvas-host .pg-paper .paper-scroller::-webkit-scrollbar-thumb:hover {
    background: rgba(71,85,105,0.85);
    background-clip: padding-box;
}


/* Status strip (mirrors .graph-view-status). */
.pg-status {
    padding: 6px 16px;
    border-top: 1px solid var(--border);
    color: var(--text-muted);
    font-size: 0.8rem;
    font-family: monospace;
    /* Craig 2026-06-18 — the status strip sits flush at the bottom, right over
       the canvas's horizontal scrollbar; capturing pointer events meant a drag
       hit the status text instead of the scrollbar ("hard to scroll on open").
       It is purely informational (no controls), so let drags fall THROUGH to the
       scrollbar beneath. */
    pointer-events: none;
    user-select: none;
}

/* v4.1.8 — lifecycle/permission gating pill in the toolbar. */
.pg-mode-pill {
    font-size: 0.75rem;
    font-weight: 600;
    padding: 3px 10px;
    border-radius: 12px;
    background: var(--primary-pale, #eef3f9);
    color: var(--text-muted, #718096);
    border: 1px solid var(--border, var(--border));
    white-space: nowrap;
}
.pg-mode-pill.pg-mode-editable { background: light-dark(#ecfdf5, #052e21); color: light-dark(#047857, #6ee7b7); border-color: light-dark(#6ee7b7, #047857);
}
/* #562 — the EFFECTIVE (released, controlled) version reads differently at a
   glance from a draft. Craig changed a Level 2 grouping believing he was in the
   released record; he was not, and nothing on screen could have told him. This
   is the one state where being wrong about which version you are in costs the
   most, so it is the one state that gets its own treatment rather than sharing
   the neutral pill with every other read-only lifecycle state.
   Wins over .pg-mode-editable by source order — an Effective version is not
   editable, so the two never both apply in practice. */
.pg-mode-pill.pg-mode-effective { background: light-dark(#eef2ff, #1e1b4b); color: light-dark(#3730a3, #c7d2fe); border-color: light-dark(#a5b4fc, #4338ca);
    font-weight: 700;
}


/* ── Edit popover (v4.1.9 — graph editing P1) ─────────────────────────
   Singleton editor at the click point. Settled-Q9 scroll layout: pinned
   (non-scrolling) TipTap toolbar, scrollable content region, fixed
   footer — only the prose scrolls, controls never leave view. Resizable
   via CSS resize; draggable by the title bar; screen-anchored. */
.pg-editpop {
    position: fixed;
    z-index: 10000;
    display: flex;
    flex-direction: column;
    background: var(--bg-white, #ffffff);
    border: 1px solid var(--primary, var(--primary-fg));
    border-radius: 8px;
    box-shadow: 0 12px 32px rgba(0,0,0,0.25);
    font: 13px -apple-system, BlinkMacSystemFont, sans-serif;
    color: var(--text, #2d3748);
    min-width: 340px;
    min-height: 160px;
    max-width: 94vw;
    max-height: 92vh;
    resize: both;
    overflow: hidden;
}
.pg-editpop-titlebar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    background: var(--primary, #1a3a5c);
    color: #fff;
    font-weight: 600;
    border-bottom: 3px solid var(--accent, #d4a23c);
    cursor: move;
    user-select: none;
    flex-shrink: 0;
}
.pg-editpop-title {
    flex: 1;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.pg-editpop-titlebar button {
    background: transparent;
    border: none;
    color: rgba(255,255,255,0.85);
    font-size: 14px;
    cursor: pointer;
    padding: 0 4px;
    line-height: 1;
}
.pg-editpop-titlebar button:hover { color: #fff; }
.pg-editpop-pin.pg-editpop-pin-on {
    color: var(--accent, #d4a23c);
    text-shadow: 0 0 6px rgba(212,162,60,0.6);
}

.pg-editpop-body {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow-y: auto;          /* form-style bodies scroll as a whole … */
}
.pg-editpop-toolbar {
    flex-shrink: 0;            /* … but a TipTap toolbar never scrolls */
    border-bottom: 1px solid var(--border, var(--border));
    background: var(--bg-light, #f8fafc);
    padding: 4px 6px;
    position: sticky;
    top: 0;
    z-index: 2;
}
.pg-editpop-content {
    flex: 1;
    min-height: 80px;
    overflow-y: auto;
    padding: 10px 14px;
}
.pg-editpop-content .rc-tiptap-content { outline: none; min-height: 60px; }
.pg-editpop-cellcontent {
    border: 1px solid var(--border, var(--border));
    border-radius: 4px;
    min-height: 64px;
    max-height: 180px;
}
.pg-editpop-rowform { padding: 10px 14px; gap: 10px; }
/* #1967 — the row-shaped step editor: the dialog IS the row (Step, Action by,
   Involved, Activity, then any further column), the process step title above it.
   Colours are theme tokens only, so it follows prefers-color-scheme. */
.pg-editpop-hint { font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--text-muted); margin-left: 6px; }
.pg-stepform-title { margin-bottom: 12px; }
.pg-stepform-row {
    display: grid;
    border: 1px solid var(--border);
    border-radius: 4px;
    overflow: hidden;
}
.pg-stepform-head {
    padding: 6px 8px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
    background: var(--surface-2, var(--bg-subtle, transparent));
    border-bottom: 1px solid var(--border);
}
.pg-stepform-head + .pg-stepform-head,
.pg-stepform-cell + .pg-stepform-cell { border-left: 1px solid var(--border); }
.pg-stepform-cell { padding: 8px; min-width: 0; }
.pg-stepform-num { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--text-muted); cursor: help; }
.pg-stepform-cell .pg-editpop-cellcontent { max-height: 260px; }
.pg-rolepick { display: flex; flex-direction: column; gap: 6px; }
.pg-rolepick-list { max-height: 220px; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }
.pg-rolepick-item { display: grid; grid-template-columns: auto 1fr; column-gap: 6px; align-items: baseline; font-size: 13px; cursor: pointer; line-height: 1.35; }
.pg-rolepick-tag { grid-column: 2; font-size: 11px; color: var(--text-muted); }
.pg-rolepick-warn { font-size: 12px; color: var(--btn-caution); cursor: help; display: flex; align-items: center; gap: 4px; }
.pg-rolepick-warn[hidden] { display: none; }
.pg-rolepick-warn .ic { width: 13px; height: 13px; }
.pg-rolepick-add {
    align-self: flex-start;
    margin-top: 4px;
    background: none;
    border: 0;
    padding: 2px 0;
    color: var(--accent);
    font-size: 13px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.pg-rolepick-add .ic { width: 13px; height: 13px; }
.pg-rolepick-new { display: flex; gap: 6px; margin-top: 4px; }
.pg-rolepick-new .pg-editpop-input { flex: 1 1 auto; min-width: 0; }
.pg-editpop-field { margin-bottom: 10px; }
.pg-editpop-field label {
    display: block;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted, #64748b);
    margin-bottom: 4px;
}
.pg-help-dot {
    display: inline-block;
    margin-left: 5px;
    color: var(--text-muted, #94a3b8);
    cursor: help;
    font-weight: 400;
    font-size: 12px;
    text-transform: none;
    letter-spacing: 0;
    vertical-align: baseline;
}
.pg-help-dot:hover,
.pg-help-dot:focus { color: var(--accent, #5b7ab5); outline: none; }
.pg-editpop-field-empty {
    color: var(--text-muted, #94a3b8);
    font-style: italic;
    font-size: 12px;
    padding: 6px 0;
}
/* #753 — the ORPHANED-FINDINGS warning inside the delete popover. The
   delete confirmation and its Part 11 reason are ONE dialog now, so this
   carries what the separate branded confirm used to say. Yellow, not red:
   the app is about to do exactly what was asked and nothing is broken —
   this is the consequence, stated before the act. */
.pg-editpop-field-warn {
    color: var(--gap-text, #92400e);
    background: var(--gap-bg, #fffbeb);
    border: 1px solid var(--gap-bar, #f59e0b);
    border-radius: 5px;
    font-size: 12px;
    line-height: 1.45;
    padding: 8px 10px;
    margin-top: 8px;
    max-height: 9em;
    overflow-y: auto;
}
.pg-editpop-input {
    width: 100%;
    box-sizing: border-box;
    padding: 7px 10px;
    border: 1px solid var(--border, var(--border));
    border-radius: 5px;
    font: inherit;
}
.pg-editpop-input:focus {
    outline: none;
    border-color: var(--primary, var(--primary-fg));
}
.pg-editpop-status {
    flex-shrink: 0;
    padding: 6px 12px;
    font-size: 12px;
    background: var(--bg-light, #f8fafc);
    border-top: 1px solid var(--border, var(--border));
    color: var(--text-muted, #475569);
}
.pg-editpop-status-err { color: light-dark(#b91c1c, color-mix(in oklab, #b91c1c 45%, white)); font-weight: 600; }
.pg-editpop-footer {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    border-top: 1px solid var(--border, var(--border));
    background: var(--bg-light, #f8fafc);
}
.pg-editpop-reason {
    flex: 1;
    min-width: 0;
    padding: 7px 10px;
    border: 1px solid var(--border, var(--border));
    border-radius: 5px;
    font: inherit;
}
.pg-editpop-reason:focus { outline: none; border-color: var(--primary, var(--primary-fg)); }
.pg-editpop-reason-invalid {
    border-color: #dc2626 !important;
    background: light-dark(#fef2f2, var(--pq-elevated));
}
.pg-editpop-footer button {
    flex-shrink: 0;
    padding: 7px 16px;
    border-radius: 5px;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    border: 1px solid var(--border, var(--border));
    background: var(--bg-white, #fff);
    color: var(--text, #2d3748);
}
.pg-editpop-save {
    background: var(--primary, #1a3a5c) !important;
    border-color: var(--primary, var(--primary-fg)) !important;
    color: #fff !important;
}
.pg-editpop-save:hover { background: var(--primary-light, #2a5a8c) !important; }
.pg-editpop-save:disabled { opacity: 0.6; cursor: default; }

/* ── Command palette (ported from the spike's #cmd-palette CSS) ────── */
/* #1963 — the knowledge-graph zoom bar (.kg-zoom) shares the palette's look: the
   same frame and the same 36px plus/minus buttons, one statement for both. */
.pg-palette,
.kg-zoom {
    position: absolute;
    background: var(--bg-white, #ffffff);
    border: 1px solid var(--border, var(--border));
    border-radius: 6px;
    box-shadow: 0 2px 10px rgba(0,0,0,0.12);
    display: flex;
    overflow: hidden;
    z-index: 200;
    user-select: none;
}
/* Dark-mode: --border on the palette blended into the dark canvas — lift the
   border + deepen the shadow so it separates (Craig D5: "slightly brighter border"). */
@media (prefers-color-scheme: dark) {
    .pg-palette, .kg-zoom { border-color: var(--pq-border); box-shadow: 0 2px 14px rgba(0,0,0,0.45); }
}
.pg-palette.orient-vertical   { flex-direction: column; }
.pg-palette.orient-horizontal { flex-direction: row; }
.pg-palette.dock-left   { left: 12px;   top: 50%; transform: translateY(-50%); }
.pg-palette.dock-right  { right: 12px;  top: 50%; transform: translateY(-50%); }
.pg-palette.dock-top    { top: 12px;    left: 50%; transform: translateX(-50%); }
.pg-palette.dock-bottom { bottom: 12px; left: 50%; transform: translateX(-50%); }
.pg-palette.dragging {
    cursor: grabbing;
    opacity: 0.85;
    box-shadow: 0 6px 18px rgba(0,0,0,0.20);
}
.pg-palette-grip {
    background: var(--primary, #1a3a5c);
    cursor: grab;
    flex-shrink: 0;
    position: relative;
    display: block;          /* never let a stray inline context collapse it */
}
.pg-palette-grip::before {
    content: '';
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    background-image: radial-gradient(rgba(255,255,255,0.85) 1.3px, transparent 1.3px);
    background-size: 5px 5px;
}
/* v4.2.106 — grip is a BIG, OBVIOUS handle in BOTH orientations, and can never
   collapse: the recurring "loses the grab handle when docked top/bottom" (Craig
   2026-07-07, again 2026-07-15) traces to the horizontal grip being too easy to
   miss. It is now a solid 22px navy bar with brighter dots, guarded by min-width/
   min-height so it is present at every orientation, docked or floating. */
.pg-palette.orient-vertical   .pg-palette-grip { height: 22px; min-height: 22px; width: 100%; }
.pg-palette.orient-vertical   .pg-palette-grip::before { width: 30px; height: 5px; }
.pg-palette.orient-horizontal .pg-palette-grip { width: 22px; min-width: 22px; height: 100%; min-height: 36px; }
.pg-palette.orient-horizontal .pg-palette-grip::before { width: 5px; height: 30px; }
/* A palette with NO orientation class yet (freshly built, before first drag) still
   reads as vertical — carry the handle there too so it is never handle-less. */
.pg-palette:not(.orient-horizontal) .pg-palette-grip { height: 22px; min-height: 22px; width: 100%; }
.pg-palette-grip:active { cursor: grabbing; }
/* v4.2.67 — CENTRE the tools (Craig 2026-07-13: "the icons should be in the middle of the
   palette"). The default `align-items: stretch` did nothing here because every icon button
   sets an explicit 36px width — so as soon as ONE button was wider (the old text-face
   numbering toggle, and the graph's BPMN toggle), the palette grew to fit it and every
   fixed-width icon sat pinned to the LEFT of the wider column. Centring makes the alignment
   independent of the widest child, so a future wide button can never knock the icons askew
   again. */
.pg-palette-tools { display: flex; }
.pg-palette.orient-vertical   .pg-palette-tools {
    align-items: center;
    flex-direction: column;
}
.pg-palette.orient-horizontal .pg-palette-tools {
    justify-content: center;
    flex-direction: row;
}
.pg-palette-tools button,
.kg-zoom button {
    background: var(--bg-white, #ffffff);
    color: var(--text, #2d3748);
    border: none;
    width: 36px; height: 36px;
    font-size: 18px;
    font-weight: 700;
    cursor: pointer;
    transition: background 100ms;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: inherit;
}
.pg-palette-tools button:hover { background: var(--hover-bg); }
.pg-palette.orient-vertical   .pg-palette-tools button:not(:last-child) { border-bottom: 1px solid var(--border, var(--border)); }
.pg-palette.orient-horizontal .pg-palette-tools button:not(:last-child),
.kg-zoom button:not(:last-child) { border-right:  1px solid var(--border, var(--border)); }

/* ── v4.1.10 P2 — structural palette tools + assessment button ── */
.pg-palette-tools button:disabled {
    opacity: 0.35;
    cursor: default;
}
.pg-palette-tools button:disabled:hover { background: var(--bg-white, #ffffff); }

/* #776 §1.6 — a write-capable control on a frozen version. It reads exactly like
   `:disabled` because it means the same thing to the user, but it is a SEPARATE
   attribute: `disabled` already has other owners on some of these buttons (the
   numbering toggle greys itself on a document with no alternative numbering),
   and two owners of one property means the last writer wins at random. The
   button keeps its icon and its place — QMS-DS-001 §14.4.2 requires the surface
   to stay fully viewable where editing is not permitted — and its tooltip
   carries the reason. Both palettes use `.pg-palette`, so this one rule covers
   the document surface and all three canvases. Analysis controls are excluded:
   #731 removes those actions with the native hidden state on frozen versions. */
.pg-palette-tools button[data-frozen="1"] {
    opacity: 0.35;
    cursor: default;
}
.pg-palette-tools button[data-frozen="1"]:hover { background: var(--bg-white, #ffffff); }
/* #731 — author CSS gives palette buttons display:flex, so state the native
   hidden contract at the same author level. Effective/Signed-off versions do
   not offer an analysis compass on either palette. */
.pg-palette-tools button[data-analyses][hidden] { display: none; }
.pg-palette-sep {
    background: var(--border, #cbd5e0);
    flex-shrink: 0;
}
.pg-palette.orient-vertical   .pg-palette-sep { height: 2px; width: 100%; }
.pg-palette.orient-horizontal .pg-palette-sep { width: 2px; height: 100%; }

/* ── #310 — palette taller than the viewport wraps into columns ──────────
   Craig 2026-07-21: the BPMN palette ran off both ends of the screen, so the
   grab bar AND the analysis action were unreachable and it could not even be
   dragged back. When palette_fit.js measures that the tools no longer fit, it
   adds .is-wrapped and sets ONE number (the strip's max size on the wrapping
   axis); flex-wrap then flows the buttons into as many columns as it takes.
   The column COUNT lives nowhere in code — the browser derives it, and re-derives
   it on resize and on browser zoom with no JS in the loop. "2 initially, but who
   knows in the future" (Craig) therefore needs no change here to become 3 or 4. */
.pg-palette.is-wrapped .pg-palette-tools { flex-wrap: wrap; align-content: flex-start; }
/* Separators are full-bleed in the single-column form; once wrapped that would
   span every column, so they are pinned to one column's measured width. */
.pg-palette.is-wrapped.orient-vertical   .pg-palette-sep { width:  var(--rc-pal-col, 36px); }
.pg-palette.is-wrapped.orient-horizontal .pg-palette-sep { height: var(--rc-pal-col, 36px); }
/* A visible seam between columns, matching the existing inter-button divider. */
.pg-palette.is-wrapped.orient-vertical   .pg-palette-tools { border-left: 0; }
.pg-palette.is-wrapped.orient-vertical   .pg-palette-tools > *:not(:last-child) { border-right-color: var(--border, #cbd5e0); }
/* Degenerate case only: too big even using every column the width allows.
   The strip scrolls; the grip is outside it and stays draggable. */
.pg-palette.is-overflow.orient-vertical   .pg-palette-tools { overflow-y: auto; }
.pg-palette.is-overflow.orient-horizontal .pg-palette-tools { overflow-x: auto; }
.pg-palette-tools button.pg-tool-assess img {
    width: 20px; height: 20px;
    display: block;
    object-fit: contain;
    pointer-events: none;
}
/* M5 — Source/Simplified numbering toggle: a LABELLED face in the icon strip.
   Auto-width so the full word fits (the palette grows to the widest label);
   small font keeps it compact. Disabled (greyed) when the doc has no
   alternative numbering — the shared button:disabled rule dims it. */
/* v4.2.67 — the numbering toggle is an ICON now, not the word "Source"/"Simplified"
   (Craig: "not keen on the simplified and source words. An icon would be better?"). It
   therefore drops back to the standard 36px square, which is ALSO what un-widened the
   palette — the text face was the reason the icons were off-centre in the first place.
   The `is-simplified` colour split stays: it is a second, redundant channel for which
   style is active, on top of the icon shape and the tooltip. */
.pg-palette-tools button.pg-tool-numbering {
    width: 36px;
    color: var(--primary-fg, #1a3a5c);
}
.pg-palette-tools button.pg-tool-numbering.is-simplified {
    color: var(--text, #2d3748);
}

/* v4.2.67 — the graph's Flow ⇄ Business Process toggle. It had NO rule of its own, so it
   inherited the 36px square and rendered the word "Flow" / "BPMN" overflowing it at 18px.
   (Unlike the numbering toggle it never widened the palette — it just looked wrong.) Now an
   icon, with the same two-tone active/inactive treatment. */
.pg-palette-tools button.pg-tool-viewmode { width: 36px; color: var(--primary-fg, #1a3a5c); }
.pg-palette-tools button.pg-tool-viewmode.is-bpmn { color: var(--text, #2d3748); }

/* #63 — the node text-size pair (Word's Grow/Shrink Font icons). Deliberately the
   NEUTRAL --text colour, not the viewmode toggle's --primary-fg: these are view
   ADJUSTMENTS in the same family as zoom in/out directly above, not a mode switch,
   and colouring them like the mode toggle they sit next to would misgroup them.
   Visibility is not styled here — the shared `.hidden` (display:none !important)
   already beats the palette button's display:flex, and RCBpmnView is the one writer
   that applies it. Both tokens are theme variables, so the pair follows light/dark
   with the rest of the palette. */
.pg-palette-tools button.pg-tool-textsize { width: 36px; color: var(--text, #2d3748); }
/* #1493 — the step-number toggle sits with the text-size pair and matches it.
   `is-on` is the ORDINARY state (numbers shown), so it carries no emphasis; the
   OFF state is the one worth showing, and it dims the same way a disabled tool
   does without being disabled. */
.pg-palette-tools button.pg-tool-bpmnnums { width: 36px; color: var(--text, #2d3748); }
/* #1724 — the Objects button reads as pressed while its window is open. */
.pg-palette-tools button.pg-tool-bpmnobjects { width: 36px; color: var(--text); }
.pg-palette-tools button.pg-tool-bpmnobjects.is-on { background: var(--bg-hover); box-shadow: inset 0 0 0 1.5px var(--accent); }
.pg-palette-tools button.pg-tool-bpmnnums:not(.is-on) { opacity: .55; }

/* Wiring criteria popover — delete-output button */
.pg-wire-delete {
    margin-top: 4px;
    padding: 6px 12px;
    background: light-dark(#fff5f5, var(--pq-elevated));
    color: light-dark(#b91c1c, color-mix(in oklab, #b91c1c 45%, white));
    border: 1px solid light-dark(#fca5a5, color-mix(in oklab, #fca5a5 30%, var(--pq-surface)));
    border-radius: 4px;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
}
.pg-wire-delete:hover { background: light-dark(#fee2e2, color-mix(in oklab, #fee2e2 14%, var(--pq-surface))); }


/* ── Findings disposition chips (v4.1.11 — graph editing P3 §4.3) ──
   Action chips on finding rows in the slot popover + drawer findings
   section. Rendered only on Draft + can_edit. */
.pg-frow-actions {
    flex-basis: 100%;
    display: flex; gap: 6px; padding-top: 4px;
}
.pg-fbtn {
    font-size: 11px; font-weight: 600; line-height: 1.4;
    padding: 2px 9px; border-radius: 11px; cursor: pointer;
    background: var(--bg-white); border: 1px solid currentColor;
    transition: background 100ms, color 100ms;
}
.pg-fbtn-accept { color: light-dark(#16a34a, color-mix(in oklab, #16a34a 45%, white)); }
.pg-fbtn-accept:hover { background: #16a34a; color: #fff; }


/* T-160 — Edit (fix it yourself) replaces the old standalone Acted. */
.pg-fbtn-edit { color: light-dark(#2563eb, var(--pq-accent)); }
.pg-fbtn-edit:hover { background: #2563eb; color: #fff; }
.pg-fbtn-reject { color: light-dark(#dc2626, color-mix(in oklab, #dc2626 45%, white)); }
.pg-fbtn-reject:hover { background: #dc2626; color: #fff; }
.pg-fbtn-revert { color: light-dark(#92400e, color-mix(in oklab, #92400e 45%, white)); }
.pg-fbtn-revert:hover { background: #92400e; color: #fff; }
/* v4.1.18 (U2 chunk 3) — debate action (opens the Expert Review window). */
.pg-fbtn-debate { color: #7c3aed; }
.pg-fbtn-debate:hover { background: #7c3aed; color: #fff; }


.pg-run-chip {
    /* #431 — BIGGER, AND IN THE MIDDLE. Craig 2026-08-24, watching a run:
       "The timer needs to be bigger - you cant find it unless you know it is
       there - so bigger and right in the middle."

       THIS SUPERSEDES HIS OWN PLACEMENT OF 2026-08-09, which read: "the chip
       sits at the CENTRE OF THE TOP THIRD of its surface — not the top edge,
       where it collided with the pill row. 16.7% = the vertical midpoint of the
       top third." Kept here rather than deleted, because the earlier ruling
       carried a REASON that still binds: the top EDGE collides with the pill
       row, so 50% is the answer and 0 is not.

       WHY PROMINENCE IS NOT COSMETIC: the standing rule is that any operation
       which can exceed a second or two shows a steady, informative indicator,
       persistent until done — because silent long work reads as broken and
       causes re-submits. An analysis runs for MINUTES. An indicator a
       first-time user cannot locate is not doing the job an indicator exists to
       do, which is his "you cant find it unless you know it is there".

       IT COVERS CONTENT AT 50%, and that is the trade he asked for: nobody is
       reading the document while waiting for its analysis, and the chip is
       transient — it exists only while a run is in flight. */
    position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    z-index: 60; display: flex; align-items: center; gap: 12px;
    background: var(--bg-white); color: var(--text); font-size: 18px; font-weight: 600;
    padding: 14px 26px; border-radius: 26px;
    border: 1px solid light-dark(#c7d2fe, color-mix(in oklab, #c7d2fe 30%, var(--pq-surface)));
    /* A deeper shadow, because it now sits OVER content rather than above it —
       the lift is what separates it from whatever it is covering. */
    box-shadow: 0 8px 28px rgba(0,0,0,0.22);
}
/* #575/#431 — the document surface mounts the SAME chip, body-level. Fixed
   rather than absolute so it holds its place while the document scrolls under
   it; everything else — size, colour, position — is the shared definition, so
   the two surfaces cannot drift apart on any of them again. */
.pg-run-chip--doc { position: fixed; }
/* the Section-N-of-M line — same look inside both chips (Craig 2026-08-10:
   "everything looks the same") */
.pg-run-chip .rc-analysis-progress { font-weight: 500; color: var(--text-muted, #718096); }
.pg-run-chip[data-phase="done"]  { border-color: #16a34a; color: light-dark(#166534, color-mix(in oklab, #166534 45%, white)); }
.pg-run-chip[data-phase="warn"]  { border-color: #d97706; color: light-dark(#92400e, color-mix(in oklab, #92400e 45%, white)); }
.pg-run-chip[data-phase="error"] { border-color: #dc2626; color: light-dark(#b91c1c, color-mix(in oklab, #b91c1c 45%, white)); }
/* Craig 2026-06-18 — small ■ Stop on the run pill (matches the doc Stop). */
.pg-run-chip .pg-run-stop {
    /* #431 — grown with the chip. Left at 18px it became a speck on a pill
       three times its old height, and the Stop is the one thing on this
       control a user may actually need to hit. */
    display: inline-flex; align-items: center; justify-content: center;
    width: 26px; height: 26px; margin-left: 4px; padding: 0;
    font-size: 12px; line-height: 1; color: light-dark(#b91c1c, color-mix(in oklab, #b91c1c 45%, white));
    background: light-dark(#fef2f2, var(--pq-elevated)); border: 1px solid light-dark(#fca5a5, color-mix(in oklab, #fca5a5 30%, var(--pq-surface))); border-radius: 50%;
    cursor: pointer;
}
.pg-run-chip .pg-run-stop:hover { background: light-dark(#fee2e2, color-mix(in oklab, #fee2e2 14%, var(--pq-surface))); border-color: #dc2626; }
.pg-run-chip .pg-run-dot {
    width: 8px; height: 8px; border-radius: 50%; background: #4f6ddb;
    animation: pg-run-pulse 1s ease-in-out infinite;
}
@keyframes pg-run-pulse { 0%,100% { opacity: 0.35; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  /* the still state shown in place of the motion (#1903: the motion itself
     is off for everything, by the one global rule near the top) */
  .pg-run-chip .pg-run-dot { opacity: 0.8; }
}

/* ── v4.1.12 P4 (D-008 B) — findings source filter chips ──────────────
   One visual grammar in three homes: the graph findings popover, the
   graph drawer findings section, and the editor Findings pane header.
   All / Regulatory / Structural; structural accent = violet (its own
   severity class, distinct from the regulatory reds). */
.pg-src-filter {
    display: flex; gap: 4px; padding: 6px 12px 4px;
    /* #1655 — was rgba(0,0,0,0.06): a hairline of alpha BLACK, which is a
       faint rule on a light ground and nothing at all on a dark one. */
    border-bottom: 1px solid var(--border-subtle, var(--border));
}

.pg-src-filter button {
    font-size: 10px; font-weight: 600; line-height: 1.5;
    padding: 1px 9px; border-radius: 10px; cursor: pointer;
    background: var(--bg-white); color: var(--text-muted); border: 1px solid var(--border);
    transition: background 100ms, color 100ms, border-color 100ms;
}
.pg-src-filter button:hover { border-color: #64748b; color: light-dark(#334155, var(--pq-ink)); }
.pg-src-filter button.is-active {
    background: var(--primary); border-color: var(--primary-fg); color: #fff;
}
.pg-src-filter button.is-active[data-src="structural"],
.pg-src-filter button.is-active[data-pgf-filter="structural"] {
    background: var(--structural-bar); border-color: var(--structural-bar);
}


/* T-181 "Together B" (2026-07-15) — the .pg-fwpanel floating framework panel is
   BURNED. The picker now lives as a column of the unified launch card
   (.sa-col-fw / .sa-fw-mount, above); #rc-fw-host is re-parented into it. */

/* ── B-041 — instant palette tooltip (body-anchored; never clipped) ─────── */

/* ── T-164 — QMS buttons now carry the shared svg.ic icons; align icon + label
   cleanly. Appended at EOF so it wins the cascade over the per-class display
   rules above. Full-width buttons (dark sidebar nav, context-menu items) use
   flex to keep their block width; inline buttons use inline-flex. (Craig 2026-06-21) */
/* .qma-row-edit and .qma-row-meta were the first two selectors here and went with
   the actions column (#726/#648, 2026-08-17). Removed from the list rather than
   left in place: a selector matching nothing looks like a live rule to the next
   reader and is the reason dead CSS accumulates. */
.qma-btn-secondary, .qma-btn-primary,
.qma-btn-icon, .qma-rel-delete { display: inline-flex; align-items: center; gap: 5px; }
.qma-btn-icon, .qma-rel-delete, .qma-tax-chip-x { justify-content: center; }
/* icons inherit the button's text colour (white on the dark sidebar) via currentColor */

/* ── Registry add/edit POPOVER + toast (Craig 2026-06-22 — the inline form opened
   off-screen at the top of a long list; a centred popover is always in view). ── */
.rc-popover-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.35);
    display: flex; align-items: center; justify-content: center; z-index: calc(var(--z-dialog) + 30); }
.rc-popover-card { background: var(--bg-white, #fff); color: var(--text, #1c2430);
    border: 1px solid var(--border, var(--border)); border-radius: 10px;
    box-shadow: 0 12px 40px rgba(0,0,0,0.25); width: min(560px, 92vw);
    max-height: 90vh; overflow: auto; }
.rc-popover-head { display: flex; align-items: center; justify-content: space-between;
    padding: 12px 16px; border-bottom: 1px solid var(--border, var(--border)); }
.rc-popover-body { padding: 16px; }
.rc-popover-foot { display: flex; justify-content: flex-end; gap: 8px;
    padding: 12px 16px; border-top: 1px solid var(--border, var(--border)); }
.rc-popover-card label { font-size: 0.8rem; color: var(--text-muted, #6b7685); }
.rc-popover-card input, .rc-popover-card select { width: 100%; padding: 6px 8px; margin-top: 2px;
    border: 1px solid var(--border, var(--border)); border-radius: 6px;
    background: var(--bg-white, #fff); color: var(--text, #1c2430); }
/* ── RCTell — the ONE channel for anything the user must be told ──────────────
 *
 * Three levels, and the colours are Craig's own console convention applied to the
 * UI (2026-08-05): white/neutral = information, YELLOW = warning, RED = an actual
 * error. A refusal is a warning — the system is healthy and is declining to do
 * something — so it must not wear the same red as a failure. When everything is
 * red, red carries no information.
 *
 * Both colour pairs are existing theme-aware tokens (--gap-* amber, --conflict-*
 * red), each defined for light AND dark, so this inherits the palette rather than
 * starting a third one. */
.rc-tell {
    /* #2023 — lifted clear of the open-document tabs when they are on screen
       (rc_tell.js measures them as each notice is shown). */
    position: fixed; bottom: calc(24px + var(--rc-tell-lift, 0px)); left: 50%; transform: translateX(-50%);
    max-width: min(560px, 86vw); padding: 12px 38px 12px 16px;
    border-radius: 8px; border: 1px solid transparent;
    box-shadow: 0 6px 20px rgba(0,0,0,0.28);
    /* #303 — THE ONE CHANNEL MUST NEVER BE COVERED. At 9500 this sat UNDER every
       modal in the app (10200 the nav, 10300 admin, 10400 audit detail, 11000 /
       11050 / 11060 the full-screen overlays), so a refusal raised from inside a
       modal — the admin panel is the one the issue names — painted behind the
       thing the user was looking at. The drawer burn moved the channel and
       carried the defect with it: hidden inside #app-wrap before, hidden behind
       a modal now. Above EVERYTHING, deliberately, and a test asserts nothing in
       this file outranks it. */
    z-index: 12000; opacity: 0; pointer-events: none;
    transition: opacity 0.2s; font-size: 0.85rem; text-align: left;
}
/* Interactive ONLY while shown — a hidden toast must never swallow a click on the
   canvas beneath it, which a permanently pointer-events:auto box would. */
.rc-tell.show { opacity: 1; pointer-events: auto; }
.rc-tell-msg { font-weight: 600; line-height: 1.45; }

.rc-tell-note { background: var(--primary, #1f4e79); color: #fff; text-align: center;
                padding-right: 16px; }
.rc-tell-warn { background: var(--gap-bg); color: var(--gap-text);
                border-color: var(--gap-text); }
.rc-tell-err  { background: var(--conflict-bg); color: var(--conflict-text);
                border-color: var(--conflict-text); }

/* "Great - now what" — the suggested actions. A plain string renders as a bullet;
   an action with a handler renders as a button, in the same list, so a message can
   gain a button later without changing shape at its call site. */
.rc-tell-actions {
    margin: 8px 0 0; padding: 0 0 0 18px; font-weight: 400; line-height: 1.5;
}
.rc-tell-actions li { margin: 2px 0; }
.rc-tell-actions li:has(> .rc-tell-do) { list-style: none; margin-left: -18px; }
.rc-tell-do {
    font: inherit; font-weight: 600; cursor: pointer;
    background: transparent; color: inherit;
    border: 1px solid currentColor; border-radius: 5px; padding: 3px 10px;
    margin-top: 2px;
}
.rc-tell-do:hover { background: color-mix(in srgb, currentColor 12%, transparent); }

/* #1446 — the dismiss control draws the sprite (`#i-x`), not a literal '×'.
   `font-size: 1.15rem` sized that glyph and is GONE: the button has no text,
   so it now sizes nothing, and an inert declaration here is what a later
   reader mistakes for load-bearing. The size moves to the `.ic` rule below,
   which is how every other icon in this file is sized — `svg.ic` has a global
   18px default and each context narrows it. */
.rc-tell-x {
    position: absolute; top: 6px; right: 8px;
    background: transparent; border: 0; color: inherit; cursor: pointer;
    line-height: 1; padding: 2px 5px; border-radius: 4px;
    opacity: 0.65;
    display: inline-flex; align-items: center; justify-content: center;
}
/* 15px, DERIVED not picked: a glyph's visible mark runs about 0.8 of its
   font-size, and 1.15rem -> 15 is the same arithmetic the thirteen earlier
   conversions used (v4.2.875 records this exact pair). */
.rc-tell-x .ic { width: 15px; height: 15px; }
.rc-tell-x:hover { opacity: 1; background: color-mix(in srgb, currentColor 12%, transparent); }



@media (prefers-color-scheme: dark) {
    .rc-popover-card { background: var(--pq-surface); color: #e6eaf0; border-color: var(--pq-border); }
    .rc-popover-card input, .rc-popover-card select { background: var(--pq-ground); color: #e6eaf0; border-color: var(--pq-border); }
}

/* ── L-080 M3 — process-graph DANGLING connectors (loose ends) ──────────────
   Mirrors frontend/lane-sandbox.html `.conn.dangling` / `.dangle-end`: a red
   dashed stub from a live step ending in a hollow "unconnected" node, so a
   post-delete loose end stays VISIBLE (★ INVARIANT REVERSAL — dangling allowed
   while drafting). The JointJS cells are also styled by inline attrs (the
   codebase's reliable path); these rules are the CSS hook via the cell root
   class and match those attrs. Read-only in M3 (reconnect/delete is M4). */
.joint-cell.rc-dangle-conn .connection,
.joint-cell.rc-dangle-conn line { stroke: #dc2626; stroke-width: 2.5;
    stroke-dasharray: 5 4; }
.joint-cell.rc-dangle-end circle,
.joint-cell.rc-dangle-end .body { fill: #fff; stroke: #dc2626; stroke-width: 2.5; }

/* ── Structure (gutter) view — object-model projection on the doc surface ──────
   Ported from numbering-gutter-sandbox.html. Each projection object = one gutter
   cell (identity rail: section name / step number) + one content cell; the CSS
   grid keeps the gutter label top-aligned against multi-line content for free. */
.doc-structure{ padding:8px 0 40px; }
/* #324 — ONE DOCUMENT, NOT TWO.
   The letterhead (#doc-header-host) and the body (.g-grid / .doc-body) used to be
   independent siblings, each drawing its own card. That read as two stacked
   documents — Craig: "the header on the live version appears to be separate from
   the main body, but on the sandbox they are clearly part of the same document" —
   and could not be kept edge-aligned ("the width should align"), because every
   width and padding had to agree by hand across two rule-sets while the
   letterhead's own max-width fought the body's.

   They are now wrapped in ONE element (index.html #doc-sheet) which owns the page
   chrome, so the tables simply share the sheet's padding and line up by
   construction rather than by coincidence. The sheet wraps EVERY surface (gutter,
   freeform, routing) and is deliberately NOT gated on which one is up: Craig —
   "All the documents should look the same so other document types should look the
   same as this", and empty white space where a surface has little to show is
   intended, not a defect.

   Width follows --doc-card-w, the SAME variable the title bar takes (#1968), so
   the sheet, the card and the title bar cannot disagree. No `overflow` here on
   purpose: app.js walks up from #doc-structure looking for the scrollable ancestor
   to restore scroll against, and making this element scrollable would capture
   that silently. */
/* #2113 (Craig 2026-10-12: "The quality report should follow the same model as the
   document and align with the header row") — the Quality report panel takes THIS
   rule: the same width token, centring, padding and card, so its edges are the
   header row's at every window width. It stays a SIBLING of the sheet (#1553: a
   statement ABOUT the document, never part of the document's own sheet) and keeps its
   own "Quality report" heading. One rule, not a copy of the numbers. */
.doc-sheet, .doc-qr{
    width:min(var(--doc-card-w, 960px), 100%);
    margin:0 auto; padding:20px;
    background:var(--bg-white);
    border:1px solid var(--border); border-radius:10px;
    box-shadow:0 3px 14px rgba(0,0,0,.28);
}
/* Nothing open (the "All SOPs" summary) — every surface inside is hidden, so drop
   the chrome rather than float an empty sheet behind the summary list. */
.doc-sheet:not(:has(> #doc-body:not(.hidden), > #doc-structure:not(.hidden), > #doc-routing:not(.hidden))){
    background:none; border:0; box-shadow:none; padding:0;
}
/* The letterhead fills the sheet's content box. Its own max-width:1180px (set for
   the standalone case) would fight the sheet's padding and leave the letterhead
   and the step table on different edges — the exact misalignment Craig flagged. */
.doc-sheet #doc-header-host .rc-cdoc{ max-width:none; width:100%; margin:0; }
/* Inside the sheet the grid is just the table: the sheet owns the page chrome, so
   the grid must not draw a second card (its own border/radius/lift) inside it. */
.doc-sheet .g-grid{
    max-width:none; width:100%; margin:0;
    border-radius:0; box-shadow:none;
}
.doc-sheet .doc-structure{ padding:0; }
.g-grid{
    position:relative;
    display:grid; grid-template-columns:var(--gc-num,104px) minmax(0,1fr);
    max-width:1180px; margin:0 auto;
    background:var(--bg-white); border:1px solid var(--border); border-radius:10px;
    font-size:14px; color:var(--primary-fg);
    /* #324 — the raised-paper lift the approved target has and this surface did
       not (Craig: "Notice the 3d effect in the sandbox, absent from the version
       you pushed"). The document should sit ON the desk, not be flush with it. */
    box-shadow:0 3px 14px rgba(0,0,0,.28);
}
/* #324 — a row that spans the whole table (the phase band), number column
   included. Without this the band began at the content column and left the
   number column visibly uncovered. */
.g-fullrow{ grid-column:1 / -1; }
/* full-height draggable column dividers (positioned by the width vars via calc) */
.g-vdiv{ position:absolute; top:0; bottom:0; width:9px; margin-left:-4px; cursor:col-resize; z-index:8; }
.g-vdiv:hover{ background:rgba(79,109,219,.30); }
.g-gutter{
    padding:10px 12px; border-right:1px solid var(--border);
    border-bottom:1px solid var(--border-subtle); background:light-dark(#f7f9fc, var(--pq-elevated));
    color:var(--text-muted); font-size:12px; display:flex; align-items:flex-start;
    min-width:0; overflow-wrap:break-word; word-break:break-word;
}
.g-content{
    padding: 10px 14px;
    border-bottom: 1px solid var(--border-subtle);
    min-width: 0;
    overflow-wrap: anywhere;
    position: relative;
}
.g-sec{ font-weight:700; color:light-dark(#3a4a63, var(--pq-accent)); text-transform:none; font-size:12.5px; }
/* Context-section left cell is intentionally blank + white (label shows once in
   the content column). Overrides the grey gutter fill so it matches the sheet. */
.g-sec-blank{ background:var(--bg-white,#fff); }

.g-secname{ font-size:15px; font-weight:700; margin:0 0 4px; color:var(--primary-fg); }
.g-prose{ color:light-dark(#33435c, var(--pq-accent)); }
.g-prose .g-pline{ line-height:1.5; }
.g-prose .g-pline.blank{ height:.5em; }
/* a table in a section's prose (a revision history), drawn as a table */
.g-prose .g-ptable{ border-collapse:collapse; margin:6px 0 8px; width:100%; }
.g-prose .g-ptable td, .g-prose .g-ptable th{ border:1px solid var(--pq-line); padding:4px 8px; vertical-align:top; text-align:left; }
.g-prose .g-ptable th{ font-weight:600; }
/* procedure heading — full-width banner (its gutter cell is blank) */
/* #324 — a procedure heading on a Visio-sourced document IS the phase band
   (CCDS / Local Labels / Artwork / Supply). Craig approved it as a solid banded
   header in the ETE v2 target render, so it reads as a section divider rather
   than another pale row in the table. */
.g-prochead{
    background: #1f4e79;
    font-weight: 700;
    color: #fff;
    border-bottom: 1px solid #1f4e79;
    text-transform: uppercase;
    letter-spacing: .06em;
    font-size: 12.5px;
    display: flex;
    align-items: center;
    justify-content: space-between;
}
/* step "table" — content cell is a 3-col mini-grid so it reads like the SOP table,
   but the number lives in the gutter, not a content column */
.g-colhead{ display:grid; grid-template-columns:var(--gc-who,130px) var(--gc-inv,110px) minmax(0,1fr); gap:0; padding:0;
    background:light-dark(#f2f5fa, var(--pq-elevated)); font-size:11px; font-weight:700; color:var(--text-muted);
    text-transform:uppercase; letter-spacing:.03em; }
.g-colhead>div{ padding:7px 12px; border-right:1px solid var(--border); }
.g-colhead>div:last-child{ border-right:0; }
/* #324 — "Step" labels the gutter column (it carries the number), matching the
   approved ETE v2 target render. */
/* #324 — centred to sit over the numbers it labels (Craig: "the word step should
   be centered on the col as well to match the numbers"). */
.g-colhead-num{ background:light-dark(#f2f5fa, var(--pq-elevated)); font-size:11px; font-weight:700;
    color:var(--text-muted); text-transform:uppercase; letter-spacing:.03em;
    padding:7px 12px; align-items:center; justify-content:center;
    text-align:center; }
/* #324 — Involved is dropped when the document never populates it, so the
   two-column variant needs its own track definition. */
.g-colhead.g-no-inv, .g-step.g-no-inv{
    grid-template-columns:var(--gc-who,130px) minmax(0,1fr); }
/* #324 — a floating milestone (Visio diamond): a landmark, not a numbered step.
   Set apart so the eye reads it as a phase marker rather than a missing number. */
/* the landmark band runs across the WHOLE row — number cell included — so a
   milestone reads as one object rather than a step with a missing number. */
.g-milenum{ background:light-dark(#fbf5e6, color-mix(in oklab, #fbf5e6 14%, var(--pq-surface))); align-items:flex-start;
    justify-content:center; text-align:center; padding-top:9px; }
.g-step.g-mile{ background:light-dark(#fbf5e6, color-mix(in oklab, #fbf5e6 14%, var(--pq-surface))); }
.g-mile-mark{ color:light-dark(#b8860b, color-mix(in oklab, #b8860b 45%, white)); font-size:14px; line-height:1; }
.g-mile .g-act{ font-style:italic; font-weight:600; color:light-dark(#7a5c00, color-mix(in oklab, #7a5c00 45%, white)); }
/* #324 — a decision node carries the same "?" the graph shows. */
.g-decq{ display:inline-block; margin-left:6px; color:light-dark(#a33, color-mix(in oklab, #a33 45%, white)); font-weight:700; }
.g-step{ display:grid; grid-template-columns:var(--gc-who,130px) var(--gc-inv,110px) minmax(0,1fr); gap:0; padding:0; }
.g-step>div{ padding:10px 12px; border-right:1px solid var(--border-subtle); }
.g-step>div:last-child{ border-right:0; }
.g-step .g-who{ font-weight:600; color:light-dark(#3a4a63, var(--pq-accent)); }
.g-step .g-inv{ color:var(--text-muted); }
/* #324 — the step number is TYPE, not a widget. Craig: "In the new version step
   numbers are shown in white boxes on a grey background, in the sandbox they are
   shown as black on white, no boxing, no grey. They are also centered in the col
   not left justified." So: white cell (not the grey furniture rail), no pill —
   no border, no radius, no fill — and centred in its column. */
.g-stepnum{ background:var(--bg-white); align-items:flex-start;
    justify-content:center; text-align:center; padding-top:10px; }
.g-stepnum .g-num{ background:none; border:0; border-radius:0; padding:0;
    color:var(--primary-fg); font-weight:700;
    font-variant-numeric:tabular-nums; font-size:13px; }
/* Craig 2026-08-07 — a GOTO surfaces a connector the reader cannot follow by
   simply reading on. It sits under the activity text as its continuation, not
   beside it as a control: this is part of what the step SAYS. Muted and smaller
   so the activity stays the thing you read first, themed via --text-muted so it
   composes in dark mode rather than needing a second rule. (This replaces the
   #390 provisional-number style — P28 deleted the mint, so no number can carry
   a hash any more.) */
/* ONE ARM PER LINE (Craig 2026-08-07: "List the arms separately, yes it is not
   perfect but it is at least correct"). A wrapping row ran the arms together, so a
   four-way gateway read as one sentence and two arms carrying the same condition —
   which the Labeling drawing does have — looked like a rendering fault rather than
   the data it is. Stacked, each arm is separately readable and separately wrong
   where the source is wrong. */
.g-gotos{ margin-top:6px; display:flex; flex-direction:column; gap:2px; }
.g-goto{ font-size:12px; color:var(--text-muted); font-style:italic; }
.g-goto .g-goto-num{ font-style:normal; font-weight:600;
    font-variant-numeric:tabular-nums; }
/* A terminal reads like every other arm (Craig 2026-08-08: "the text formatting of
   that differs from the other notes on steps, it should match look and feel of the
   other gotos statements"). It WAS upright and bold, on the reasoning that ending
   the process is the outcome a reader must not miss — but a row that shouts among
   quiet siblings reads as a different KIND of thing, and it is not: it is one more
   arm out of the step. The only distinction kept is the word STOP itself, which is
   the author's. */
.g-goto-stop .g-goto-word{ font-weight:600; }
/* #1848 (P28) — "Step N" in an activity that points at a step: a link to that
   step's row. Shared tokens, so light and dark follow the computer's setting. */
.g-act a.g-stepref{ color:var(--accent); text-decoration:underline;
    text-underline-offset:2px; font-variant-numeric:tabular-nums; cursor:pointer; }
.g-act a.g-stepref:hover, .g-act a.g-stepref:focus-visible{ text-decoration-thickness:2px; }
.g-act .g-stepref-off{ font-variant-numeric:tabular-nums; }
.g-content.g-stepref-target{ outline:2px solid var(--accent); outline-offset:2px;
    background:var(--accent-soft); }
/* An ASSUMED section: present because every process is taken to have one, empty
   because this document does not state it. Muted heading + an explicit line, so
   the emptiness reads as a fact rather than as a rendering fault. */
.g-assumed .g-secname, .g-prochead.g-assumed{ opacity:.72; }
.g-notstated{ font-style:italic; color:var(--text-muted); font-size:13px; }
/* #677 — an AUTHORED metadata item. Clickable at all times, including when the
   document states nothing, so the affordance has to read as available on an empty
   row too — hence the hover treatment rather than relying on text being there. */
/* #677 — the derived sections' tables. Compact, quiet, and readable in both
   themes: every colour is a theme token so nothing is hardcoded light or dark. */
.g-dtable{ width:100%; border-collapse:collapse; font-size:12.5px; margin-top:4px; }
.g-dtable th{ text-align:left; font-weight:600; font-size:11px; text-transform:uppercase;
    letter-spacing:0.04em; color:var(--text-muted); padding:2px 8px 4px 0; }
.g-dtable td{ padding:3px 8px 3px 0; vertical-align:top;
    border-top:1px solid var(--border-subtle); }
.g-dtable .g-dcode{ font-variant-numeric:tabular-nums; font-weight:600; white-space:nowrap; }
.g-dtable .g-dnote{ color:var(--text-muted); font-size:11.5px; }
.g-approval{ font-size:13px; padding:2px 0; }
/* #677 — an authored metadata row on the data block: prose, so it wraps, and it
   is clickable even when empty. */
.rc-db-meta{ cursor:pointer; align-items:flex-start; }
.rc-db-meta:hover{ background:var(--hover-bg, rgba(127,127,127,0.08)); }
.rc-db-meta .rc-db-v{ white-space:normal; line-height:1.4; }
.rc-db-meta .rc-db-empty{ font-style:italic; opacity:.65; }
.g-meta{ cursor:pointer; }
.g-meta:hover{ background:var(--hover-bg, rgba(127,127,127,0.06)); }
.g-meta .g-meta-src{ margin-left:8px; font-size:10px; font-weight:600;
    text-transform:uppercase; letter-spacing:0.04em; color:var(--text-muted);
    border:1px solid var(--border-subtle); border-radius:3px; padding:1px 5px; }
.g-click{ cursor:pointer; }
.g-click:hover{ background:light-dark(#f5f8ff, var(--pq-elevated)); }
.g-empty{ max-width:1040px; margin:24px auto; padding:24px; text-align:center;
    color:var(--text-muted); background:var(--bg-white); border:1px dashed var(--border); border-radius:10px; }

/* read-only detail panel (click a step/section) */

.g-modal{ background:var(--bg-white); border-radius:12px; width:min(600px,100%);
    box-shadow:0 20px 50px rgba(10,20,40,.35); overflow:hidden; }
.g-modal h3{ margin:0; padding:14px 18px; font-size:15px; border-bottom:1px solid var(--border);
    background:light-dark(#f7f9fc, var(--pq-elevated)); display:flex; align-items:center; gap:10px; }


/* active state for the palette toggle */
.g-act.rc-finding{ display:block; }
/* B-183 — activity text may carry paragraph breaks (multi-paragraph cell
   content, e.g. an accepted replacement); render them instead of running
   the paragraphs together. */
.g-act{ white-space:pre-line; }
/* B-183 — the card→document activation marks the gutter box active (the
   reverse of box-click→card).
   (BURNED — #1575) A gutter-local `.rc-finding-active` rule lived here, drawing
   its own 2px outline. It existed only because the gutter marked a class name no
   shared rule matched, so it was compensating paint for a class mismatch — and
   it compensated in a background token, which is why it showed nothing. Both are
   gone: the gutter now marks `is-active`, the same class the editor marks, and
   inherits the shared selected treatment (darker per-category `-sel` fill + the
   #322 ring) exactly as the comment above this block always said it did. The
   correct fix deletes the patch machinery rather than re-tuning it. */
.g-pline.rc-finding{ margin:3px 0; }
/* #1964 — a list paragraph: its number or bullet hangs in the indent, nested
   lists one step further in. */
.g-pline.g-li{ padding-left:calc((var(--li-depth, 0) + 1) * 1.6em); text-indent:-1.6em; }
.g-li-mark{ display:inline-block; width:1.6em; text-indent:0; color:var(--text-muted); font-variant-numeric:tabular-nums; }
/* a finding whose quoted_text didn't anchor to a specific prose line — shown as a
   boxed snippet so it is never dropped (coverage parity with the count pill). */
.g-pline.g-unanchored{ font-style:italic; opacity:.92; }
.g-pline.g-unanchored::before{ content:'\2691 '; font-style:normal; opacity:.6; }

/* ── B-232 — document surface routing placeholder ───────────────────────────
   Which surface a doc gets (gutter vs freeform editor) is a SERVER answer
   (is_process). This element owns the window between "render started" and that
   answer, so neither surface paints a guess — the freeform TipTap editor is the
   superseded model for SOPs and it is #doc-body's default-visible state, so any
   paint before the answer lands is a paint of the WRONG model.
   Colours come from the existing theme vars, which are already redefined under
   @media (prefers-color-scheme: dark) at the top of this file — so this honours
   the user's light/dark setting with no palette of its own. */
.doc-routing{
    display:flex; align-items:center; justify-content:center;
    min-height:240px; padding:2rem;
}
.doc-routing.hidden{ display:none; }
.doc-routing-msg{
    display:flex; flex-direction:column; align-items:center; gap:.6rem;
    color:var(--text-muted); font-size:.9rem; text-align:center;
}
.doc-routing-spin{
    width:22px; height:22px; border-radius:50%;
    border:2px solid var(--border); border-top-color:var(--text-muted);
    animation:doc-routing-spin .8s linear infinite;
}
@keyframes doc-routing-spin{ to{ transform:rotate(360deg); } }
/* Craig's global rule: respect prefers-reduced-motion — no spin, just a mark. */
@media (prefers-reduced-motion: reduce) {
  /* the still state shown in place of the motion (#1903: the motion itself
     is off for everything, by the one global rule near the top) */
  .doc-routing-spin { border-top-color:var(--border); }
}
.doc-routing-err{ color:var(--text); max-width:32rem; }
.doc-routing-err span{ color:var(--text-muted); }
.doc-routing-retry{
    margin-top:.4rem; padding:.35rem .9rem; cursor:pointer;
    border:1px solid var(--border); border-radius:6px;
    background:var(--surface); color:var(--text); font-size:.85rem;
}
.doc-routing-retry:hover{ border-color:var(--text-muted); }

/* ══════════════════════════════════════════════════════════════════════════
   #207 — THE GROUPED (ACTIVITY-ALTITUDE) PROCESS MAP (.pg-ga)
   The Flow surface's identity view: one card per entities.activities
   activity, in the agreed flow-altitude-sandbox visual language (a7868483).
   Light is the default; dark rides prefers-color-scheme (house rule) — the
   card surfaces reuse the --rc-sop-card-* vars, which already remap on dark.
   ══════════════════════════════════════════════════════════════════════════ */
/* #271 — the V2 (JointJS-paper) grouped renderer (.pg-gav2) SHARES this one
   token block (rule 6: one theme definition, two consumers during the
   parallel run; at the switch-and-burn the .pg-ga selector is dropped). */
.pg-ga,
.pg-gav2 {
    position: absolute; inset: 0; overflow: hidden;
    background: var(--bg, #f8fafc);
    --pg-ga-hdr-top: var(--pq-indigo-500); --pg-ga-hdr-bot: #3e5cc9;
    --pg-ga-phase-tint: rgba(79, 109, 219, 0.045);
    --pg-ga-phase-bg: #eef3f9; --pg-ga-phase-fg: var(--pq-navy-800);
    --pg-ga-edge: #8593a8; --pg-ga-edge-label-halo: #f8fafc;
    --pg-ga-badge-ring: var(--pq-white);
}
.pg-ga.hidden, .pg-gav2.hidden { display: none; }


/* #249 — NO SWIM LANES. The banding is by PROCESS = the PHASE.
   #253 (Craig 2026-07-20) — the bands run HORIZONTAL: one ROW per phase,
   tinted alternately, cards flowing left→right along it, bands stacked down
   the page. The phase name is a STICKY label in column 1 so it stays readable
   while a long band scrolls sideways (the horizontal analogue of the column
   header rev 2 pinned to the top). */


.pg-ga-phase {
    position: sticky; left: 0; z-index: 6; text-align: left;
    align-self: center; justify-self: stretch;
    font-size: 0.76rem; font-weight: 700;
    color: var(--pg-ga-phase-fg); background: var(--pg-ga-phase-bg);
    border: 1px solid var(--border, #cbd5e0); border-radius: 8px;
    padding: 6px 8px; margin: 8px 10px 8px 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}


/* #249 — UNIFORM HEADERS. Content and treatment are unchanged (Craig: the
   headers work, they must simply be the SAME SIZE); every band of the header
   gets a fixed box so a one-line title and a three-line title produce an
   identical header height. Sized in rem so it still tracks the user's font
   scaling (house rule) rather than being pinned to px. */
.pg-ga-hdr {
    height: calc(4.2rem + 23px); box-sizing: border-box; overflow: hidden;
    padding: 9px 12px 8px; color: #fff;
    background: linear-gradient(180deg, var(--pg-ga-hdr-top) 0%, var(--pg-ga-hdr-bot) 100%);
    display: flex; flex-direction: column; gap: 3px; justify-content: flex-start;
    border-bottom: 1px solid var(--rc-sop-card-border, #cbd5e0);
}


/* #261 — the "N steps" chip is a real button: THE expand/collapse affordance
   (with the +/− control) now that a card-body click selects instead */


.pg-ga-badge {
    position: absolute;
    top: -6px;
    right: -6px;
    z-index: 4;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: 10px;
    color: #fff;
    font-size: 0.72rem;
    font-weight: 700;
    line-height: 20px;
    text-align: center;
    letter-spacing: 0.02em;
    pointer-events: none;
    box-shadow: 0 0 0 2px var(--pg-ga-badge-ring), 0 2px 4px rgba(15,23,42,0.18);
    cursor: pointer;
}


/* #261/#263 — popover bodies: rolled-up item lists + decision points */
.pg-ga-itemlist { max-height: 320px; overflow-y: auto; }
.pg-ga-item {
    padding: 5px 12px; font-size: 0.78rem; color: var(--text, #2d3748);
    border-top: 1px solid var(--border, #e2e8f0);
}
.pg-ga-item:first-child { border-top: 0; }
.pg-ga-item--empty { color: var(--text-muted, #94a3b8); font-style: italic; }
.pg-ga-declist { max-height: 380px; overflow-y: auto; }
.pg-ga-decrow { padding: 7px 12px; border-top: 1px solid var(--border, #e2e8f0); }
.pg-ga-decrow:first-child { border-top: 0; }
.pg-ga-dechead { font-size: 0.78rem; font-weight: 700; color: var(--text, #2d3748); }
.pg-ga-decbranch {
    font-size: 0.74rem; color: var(--text-muted, #64748b);
    padding: 2px 0 0 18px;
}


/* ── #466 fault 2 — THE MINIMAP GETS OUT OF THE DRAWER'S WAY ───────────────────
   Craig, 2026-07-29: "I was showing the Adverse event reporting SOP … notice that
   the minimap is over the findings."

   Both are right-anchored. The drawer host is pinned to the window's right edge and
   the minimaps are pinned to the canvas's right edge, which is the same edge — so an
   open drawer sits on top of the minimap, or the minimap on top of the drawer,
   depending on which z-index wins. Neither is what anyone wants.

   The obvious fix is barred by Craig's own ruling: the canvas surfaces are
   deliberately excluded from the drawer RESERVE — "we cant change the size or shape
   of the process" — so the canvas will not shrink and the minimap cannot be pushed
   in by its container. What CAN move is the minimap, which is an overlay ON the
   canvas rather than part of the process: shifting it left changes nothing about the
   diagram's size or shape.

   `--rc-drawer-occupied` is the measured width of the open drawers plus the tab rail —
   what the host actually covers, written by RCDrawers.applyReserve from the SAME
   measurement that produces `--rc-drawer-reserve`. Two consumers, one measurement:
   the document surface pads by the reserve (which is 0 on a canvas, deliberately),
   and this moves by what is occupied (which is the real width on every surface).
   Reading it here means the minimap tracks a drag of the drawer's grip for free, and
   there is no second measurement to drift from the first (charter 6).

   Placed AFTER the base rules on purpose: same specificity, so order decides, and
   this must win. 16px is the resting inset the minimap already uses, kept as the
   base so nothing moves when every drawer is shut.

   #1874 — the offset now sits in the minimap's OWN rule, which is where order
   cannot defeat it: .joint-navigator.pg-gav2-navigator (Level 2). The override that
   stood here named the burned V1 grid's .pg-ga-nav, so Level 2 never got the offset
   at all. (#1846: the BPMN surface's minimap is the adopted editor's own, styled in
   bpmn_editor/rc_theme.css; the `.bpmn-navigator` rules were burned with
   bpmn_view.js.) */


/* context menu + rename popover (ruling 5 corrections) */


@media (prefers-color-scheme: dark) {
    /* #271 — the V2 renderer shares the dark remap too (one definition). */
    .pg-ga,
    .pg-gav2 {
        --pg-ga-hdr-top: #3d55b8; --pg-ga-hdr-bot: #32479c;
        --pg-ga-phase-tint: rgba(111, 133, 219, 0.07);
        --pg-ga-phase-bg: #232838; --pg-ga-phase-fg: #cfe0f2;
        --pg-ga-edge: #7c8aa0; --pg-ga-edge-label-halo: #0f141a;
        --pg-ga-badge-ring: var(--pq-night-850);
    }
    
    
}

/* ══════════════════════════════════════════════════════════════════════════
   #271 — GROUPED VIEW V2 (.pg-gav2): the JointJS-paper renderer. The V1⇄V2
   parallel run is OVER — the level selector renders Level 2 through this
   renderer only; the rc.flow.v2 flag machinery is dead code awaiting the burn
   (see flow_grouped_v2.js's STATUS note). The theme tokens live in the SHARED
   .pg-ga/.pg-gav2 block above (rule 6) — only V2-specific chrome is here.
   ══════════════════════════════════════════════════════════════════════════ */
.pg-gav2 .joint-paper-scroller { width: 100%; height: 100%; }
/* the band-name gutter — the V1 sticky label done the BPMN frozen-gutter way:
   pinned left, tracks vertical scroll/zoom via JS, never scrolls sideways */
/* #413 — the process-name gutter. Craig 2026-07-28: "there is a ton of
   realestate for the process names, but they are truncating ... Dont truncate,
   also the font size should be significantly bigger."

   It was a 132px column with a 0.76rem label on `white-space: nowrap` plus
   `text-overflow: ellipsis`, so "DEVELOP HQ STUDY START-UP" rendered as
   "DEVELOP H…" while the band it names ran hundreds of pixels down the canvas.
   Two changes, and they depend on each other: the column widens, AND the label
   WRAPS instead of clipping. Widening alone still truncates the long ones;
   wrapping alone crams four words into 122px. The vertical room is free — a
   band is as tall as the activities it contains. */
/* #1782 — the widths are set by flow_grouped_v2.js (GUTTER_W: the palette's
   58px home column plus the 232px label column), so the layout, this rail and
   the labels cannot disagree. The labels start right of the tool palette. */
.pg-gav2-gutter {
    position: absolute; left: 0; top: 0; bottom: 0; width: var(--pg-gav2-gutter-w);
    z-index: 5; overflow: hidden; pointer-events: none;
    /* #529 — THE RAIL IS AN EDGE, not a floating label.
       Craig 2026-08-02 (Image #9): scrolled right, half-cards showed through
       beside and behind the band labels, so the labels read as chips floating
       over card wreckage. Only the label BOX was opaque; the strip was not.
       An opaque background would hide it, but the guardrails are explicit that
       sticky-header/scroll artefacts get a STRUCTURAL fix — so the rail now
       paints the surface colour across its full width and the canvas genuinely
       ends at its edge. This also makes the 232px legible as RESERVED SPACE
       rather than the waste #428 reported: it is the process header's home. */
    background: var(--surface, #ffffff);
    border-right: 1px solid var(--border, #e2e8f0);
}
.pg-gav2-gutter-inner { position: absolute; left: 0; top: 0; width: var(--pg-gav2-gutter-w); }
/* #511 — a COLUMN now, not a row: the band hosts the process header block
   (name, summary, findings badge) stacked, not just the name. The block
   stays vertically centred in the band via justify-content, which is what
   the label's `margin: auto` was doing on its own before. */
.pg-gav2-gutter-band { position: absolute; left: var(--pg-gav2-label-left); width: calc(var(--pg-gav2-label-w) - 10px);
    display: flex; flex-direction: column; justify-content: center; }
.pg-gav2-gutter-label {
    /* #511 — was `margin: auto 10px auto 8px`, which centred this element
       itself when it was the band's only child. The band now centres the
       whole block, so auto here would fight it and push the summary and
       badge apart. */
    margin: 0 10px 0 8px; width: 100%; box-sizing: border-box;
    font-size: 1.05rem; font-weight: 700; line-height: 1.25;
    color: var(--pg-ga-phase-fg); background: var(--pg-ga-phase-bg);
    border: 1px solid var(--border, #cbd5e0); border-radius: 8px;
    padding: 8px 10px;
    /* No ellipsis, no nowrap — the whole name, wrapped. `overflow-wrap` keeps a
       single very long token from spilling out of the pill rather than clipping
       it, so nothing is ever hidden. */
    white-space: normal; overflow-wrap: anywhere;
}
/* #511 — the process header block, restored into the gutter.
   The name pill above is the block's first line; these are the other two. They
   live inside .pg-gav2-gutter-band, which already tracks the canvas's scroll and
   zoom, so the whole block moves with the process it describes. */
.pg-gav2-gutter-summary {
    margin: 4px 10px 0 8px;
    font-size: 0.82rem; line-height: 1.3;
    color: var(--muted, #64748b);
    /* Two lines, then clamp — the full text is on the title attribute. A summary
       that grows without bound would push the badge out of a short band. */
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
}
.pg-gav2-gutter-badge {
    align-self: flex-start;
    margin: 6px 0 0 8px;
    min-width: 22px; height: 22px; padding: 0 6px;
    border: 1px solid var(--pg-ga-badge-ring, #ffffff); border-radius: 11px;
    font-size: 0.78rem; font-weight: 700; line-height: 1;
    color: #ffffff;                    /* background is set per-band, from the
                                          shared P0/P4 precedence rule */
    cursor: pointer;
    box-shadow: 0 1px 3px rgba(0,0,0,0.18);
}
.pg-gav2-gutter-badge:hover { filter: brightness(1.08); }
.pg-gav2-gutter-badge:focus-visible { outline: 2px solid var(--primary, #4f6ddb); outline-offset: 2px; }


/* WT-29 two-class rule — carry the widget's own .joint-navigator class so these
   rules beat the runtime-injected joint-plus.css's ONE-class
   `.joint-navigator { position: relative }`. That is what keeps the LAYOUT
   right. It is not enough for the PAINT — see the #1292 note inside the rule. */
.joint-navigator.pg-gav2-navigator {
    /* #466 / #1874 — out of the open drawers' way. */
    position: absolute; right: calc(16px + var(--rc-drawer-occupied, 0px)); bottom: 16px; z-index: 30;
    /* #1292 — tokens, so this themes with the computer's setting. */
    border-radius: 8px;
    box-shadow: var(--rc-minimap-shadow);
    overflow: hidden; cursor: pointer;
    /* ── #1292 — WHY THESE TWO CARRY `!important` AND THE REST DO NOT ─────────
     *
     * MEASURED IN joint-plus.css, not deduced from the symptom: the selector
     * `.joint-navigator.joint-theme-default` sets background-color to white and
     * a light-grey 1px border. (Written without braces on purpose — this rule's
     * body is parsed by a stop-at-first-brace pattern in the #1292 test, and a
     * quoted brace in a comment truncates the capture.)
     *
     * TWO CLASSES — the SAME specificity as this rule, not less. The widget is
     * constructed with no `theme` option, so JointJS applies its default theme
     * and the element carries `joint-theme-default`. joint-plus.css is injected
     * at runtime and therefore loads AFTER this sheet, so at equal specificity
     * ORDER decides and the vendor wins. The LAYOUT never had this problem: the
     * vendor's bare `.joint-navigator` position rule is ONE class.
     *
     * A third specificity class would only beat `joint-theme-default`, so it
     * would break the day the widget's theme changed; `!important` beats every
     * theme variant the vendor ships (default / material / modern / dark).
     *
     * It also explains why light mode looked fine: the vendor's white won in
     * BOTH schemes all along, and the light token is near-white, so only dark
     * mode made the loss visible.
     *
     * (Moved here from the BPMN twin, `.bpmn-navigator`, burned in #1846 with
     * the hand-built view that wore it.) */
    background: var(--rc-minimap-bg) !important;
    border: 1px solid var(--rc-minimap-border) !important;
}
/* #466 / #1874 — the minimap slides out of the drawers' way, unless the person
   has asked for reduced motion. */
@media (prefers-reduced-motion: no-preference) {
    .joint-navigator.pg-gav2-navigator { transition: right .18s ease; }
}
.joint-navigator.pg-gav2-navigator .joint-paper { background: transparent !important; }
/* density-map mini (the WT-29 read): cards dark-neutral, bands keep their
   tints, text/links are noise at this scale */
.joint-navigator.pg-gav2-navigator .joint-paper rect:not(.pg-gav2-band-body) { fill: light-dark(#64748b, #7d8ea8) !important; stroke: none !important;
}
.joint-navigator.pg-gav2-navigator .joint-paper text,
.joint-navigator.pg-gav2-navigator .joint-paper circle,
.joint-navigator.pg-gav2-navigator .joint-paper path { display: none; }
/* #1292 — the chrome moved to the `--rc-minimap-*` tokens; the shape FILL stays
   behind a media query because it is a shape colour, not the panel, and it keeps
   the light/dark asymmetry (bands excluded) that a single token would flatten. */
/* the palette V2 flag button's ON face (painted by RCFlowGroupedV2) */
.pg-palette [data-tool="flow-v2"].is-active { background: light-dark(#eaf1fb, color-mix(in oklab, #eaf1fb 14%, var(--pq-surface))); color: light-dark(#2b3f8c, var(--pq-accent)); }

/* ═══════════════════════════════════════════════════════════════════════════
   #299 / #307 — THE DOCUMENT HEADER: one component, three stylesheets
   ═══════════════════════════════════════════════════════════════════════════

   Craig 2026-07-21 (#307): default "real controlled document"; Word and Modern
   web selectable; the choice is saved on the user record.

   ONE DOM. `_rcRenderDocHeader` emits a single skeleton — band / mark / title /
   facts / foot — and EVERY difference between the three treatments lives here,
   selected by `#doc-frame[data-doc-render="…"]`. Adding a fourth treatment must
   be possible without touching app.js (charter 6). The skeleton is a CSS grid
   with named areas, so a treatment can REORDER the same DOM rather than needing
   its own markup — which is what forced the sandbox to carry six header
   implementations.

   FINDINGS ARE NOT STYLED HERE. The ledger drawer, the gutter anchors and the
   locked finding palette (--rc-find-*) are untouched by every rule below: the
   header renders in its own host ABOVE the body surfaces and never selects into
   #doc-structure or #doc-body. That is #307's acceptance bar.
   ═════════════════════════════════════════════════════════════════════════ */

.rc-dochdr {
    display: grid;
    grid-template-areas: "band band" "mark facts" "title facts" "gap gap" "foot foot";
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 10px 20px;
    max-width: 1180px;
    margin: 0 auto 18px;
    padding: 0;
    color: var(--text);
    /* Locale-aware dates are formatted in JS; nothing here assumes en-US. */
}
.rc-dochdr-band  { grid-area: band; }
.rc-dochdr-mark  { grid-area: mark; display: flex; align-items: center; }
.rc-dochdr-title { grid-area: title; margin: 0; font-size: 1.35rem; line-height: 1.3; }
.rc-dochdr-facts { grid-area: facts; margin: 0; display: grid; gap: 2px 14px; }
.rc-dochdr-foot  { grid-area: foot; display: flex; justify-content: space-between;
                   gap: 12px; font-size: .72rem; color: var(--text-muted); }
.rc-dochdr-fact  { display: contents; }
.rc-dochdr-k     { font-size: .7rem; text-transform: uppercase; letter-spacing: .04em;
                   color: var(--text-muted); margin: 0; }
.rc-dochdr-v     { margin: 0; font-size: .85rem; }

/* #318 — an EMPTY header slot renders; it does not vanish (Craig 2026-07-22:
   "if the fields are empty then we render that and it is a remnder to complete
   tem"). Two things have to be true at once, and neither is automatic:

   1. The slot must be visibly THERE. An empty <dd> collapses to zero height, so
      without a min-height the cell reads as a rendering fault rather than a
      field awaiting a value — the opposite of the intent.
   2. The slot must be visibly EMPTY, WITHOUT faking content. The faint baseline
      rule is a form blank: it says "to be completed". It is deliberately NOT an
      em-dash — 2026-07-21 still stands, and a dash asserts that nothing belongs
      there, which is the claim we must not make about a field we simply never
      captured. */
.rc-dochdr-fact.is-empty .rc-dochdr-v {
    min-height: 1.05em;
    border-bottom: 1px dashed var(--border, #cbd5e1);
    opacity: .55;
}
.rc-dochdr-fact.is-empty .rc-dochdr-k { opacity: .75; }
.rc-dochdr-logo     { max-height: 46px; max-width: 190px; width: auto; object-fit: contain; }
.rc-dochdr-wordmark { font-weight: 700; letter-spacing: .02em; font-size: .95rem; }

/* SPARSE HEADERS SUPPRESS, THEY DO NOT DASH — and the suppression happens in
   the BUILDER, not here: absent cells are never emitted, so there is no
   `[data-source="absent"]` rule to write. Measured on a real ETE v2 Visio
   import, where only 6 of 15 fields populate: a grid of em-dashes and an empty
   classification banner reads as a FAILED controlled document. The gap is
   stated once instead, by `.rc-dochdr-gap`. Hiding with CSS would have been
   wrong here — a hidden element still occupies its grid area and would leave
   the ragged trailing gap the suppression exists to remove. */
.rc-dochdr-gap {
    grid-area: gap; margin: 0; font-size: .72rem; font-style: italic;
    color: var(--text-muted);
}

/* The header's own error state must NOT borrow the locked finding palette
   (--rc-find-*): those hues carry FINDING MEANING, and a header that failed to
   load is not a finding. Its own neutral alert colour, theme-aware. */
.rc-dochdr-error { border: 1px solid #b3261e; padding: 12px 14px; border-radius: 6px; }
@media (prefers-color-scheme: dark) { .rc-dochdr-error { border-color: #f2b8b5; } }

/* ── CONTROLLED HEADER (.rc-cdoc) — ported verbatim from the #298 sandbox's
   t-head-3 (Craig 2026-07-22: "do exactly what the sandbox does"). A bordered
   table, so it sizes to its content and sits in flow — it cannot overflow and
   draw over the document body the way the old CSS-grid header did once all the
   field slots rendered (#318). Arial/print posture, paper-white in dark like the
   rest of the controlled treatment. Values match the sandbox's .r3 .hd rules. */
.rc-cdoc {
    /* NEUTRALISE THE GLOBAL `header {}` RULE (the bare-element rule under
       "── Header ──" near the top of this file). That rule styles
       the app's navy top bar, but it is a BARE ELEMENT selector, so it also
       catches this <header class="rc-cdoc">: it imposed height:52px (clamping
       the header while its tables overflowed onto the body), display:flex
       (tables side-by-side), and background:var(--primary) (the navy bar seen
       cutting across the document). The sandbox renders correctly only because
       it is a standalone page with no such global rule. A class beats an element
       on specificity, so declaring these here wins. THIS is the actual bug — not
       the header markup, which was rebuilt several times against the wrong
       cause. Found 2026-07-22 by dumping the live computed styles. */
    display: block;
    position: static;
    height: auto;
    min-height: 0;
    background: none;
    color: light-dark(#1a1a1a, var(--pq-ink));
    padding: 0;
    box-shadow: none;
    font-family: Arial, Helvetica, sans-serif;
    max-width: 1180px;
    margin: 0 auto 18px;
}
.rc-cdoc-band { text-align: center; font-weight: 700; letter-spacing: .2em;
                text-transform: uppercase; font-size: .68rem; color: light-dark(#b91c1c, color-mix(in oklab, #b91c1c 45%, white));
                border: 1.5px solid #b91c1c; padding: 3px 6px; margin-bottom: 12px; }
.rc-cdoc-frame { width: 100%; border: 2px solid light-dark(#111, var(--pq-ink-muted)); border-collapse: collapse; }
.rc-cdoc-frame td { border: 1px solid light-dark(#111, var(--pq-ink-muted)); padding: 6px 10px; vertical-align: top; }
.rc-cdoc-org  { width: 26%; font-weight: 700; font-size: .82rem; letter-spacing: .04em; }
.rc-cdoc-type { text-align: center; font-weight: 700; letter-spacing: .1em;
                font-size: .78rem; text-transform: uppercase; }
.rc-cdoc-id   { width: 22%; font-size: .78rem; text-align: center; }
.rc-cdoc-id b { font-size: .95rem; }
.rc-cdoc-title { text-align: center; font-size: 1.18rem; font-weight: 700; padding: 12px 10px; }
.rc-cdoc-facts td { font-size: .74rem; }
.rc-cdoc-facts .k { font-weight: 700; text-transform: uppercase; font-size: .62rem; margin-right: 5px; }
.rc-cdoc-facts .rc-hd-eff-select { font: inherit; text-transform: uppercase; color: inherit; background: transparent; border: 1px solid currentColor; border-radius: 3px; padding: 0 2px; }
.rc-cdoc-revcap { font-size: .66rem; font-weight: 700; text-transform: uppercase;
                  letter-spacing: .08em; margin: 10px 0 3px; color: light-dark(#333, var(--pq-ink)); }
.rc-cdoc-rev { width: 100%; border-collapse: collapse; font-size: .72rem; margin-bottom: 8px; }
.rc-cdoc-rev th { background: light-dark(#e8e8e8, var(--pq-line)); border: 1px solid light-dark(#111, var(--pq-ink-muted)); padding: 3px 8px; text-align: left; }
.rc-cdoc-rev td { border: 1px solid light-dark(#111, var(--pq-ink-muted)); padding: 3px 8px; }
.rc-cdoc-logo { max-height: 46px; max-width: 190px; object-fit: contain; }
/* An empty slot renders as a dashed blank — a reminder to complete it (#318),
   deliberately NOT an em-dash (a dash asserts nothing belongs there; a blank
   invites the value). */
.rc-cdoc-f.is-empty { display: inline-block; min-width: 64px; min-height: 1em;
                      border-bottom: 1px dashed #999; }

/* #621 — OUR version, shown in parentheses beside the client's own. Muted and a
   touch smaller so the client's number stays the one their eye lands on: it is
   their document and their numbering. Ours is there for tracing, and for telling
   an Effective 1.0 from its draft 1.1 when the source prints "01" on both.
   `currentColor` at reduced opacity rather than a fixed grey, so it follows the
   theme in both light and dark without a second rule. */
.rc-cdoc-vours { opacity: 0.65; font-size: 0.92em; font-weight: 400;
                 white-space: nowrap; }

/* ── 1 · REAL CONTROLLED DOCUMENT (the #307 default) ────────────────────────
   Print-fidelity regulatory block: classification banner, framed grid, the
   "uncontrolled when printed" footer as a RENDER-TIME artefact (it is never in
   the record — it is a fact about this printout, not about the document).

   PRINT POSTURE, DELIBERATE (carried from the #298 sandbox): this treatment
   stays PAPER-WHITE in dark mode. Only the desk around it darkens. Its whole
   claim is "this is what your controlled document looks like", and re-tinting
   the page would break that claim — the same stance the BPMN printed-page
   surface already takes. This is the one place in the app that intentionally
   does not follow prefers-color-scheme, and it is a decision, not an oversight.
   The other two treatments follow the user's theme normally.               */
#doc-frame[data-doc-render="controlled"] .rc-dochdr {
    grid-template-areas: "band band" "mark ident" "title title" "facts facts" "gap gap" "foot foot";
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 0;
    background: #fff;
    color: #1a1a1a;
    border: 1.5px solid #333;
    padding: 0;
}
#doc-frame[data-doc-render="controlled"] .rc-dochdr-band {
    background: #1f2d3d; color: #fff; text-align: center;
    font-size: .72rem; font-weight: 700; letter-spacing: .12em;
    text-transform: uppercase; padding: 5px 10px;
}
#doc-frame[data-doc-render="controlled"] .rc-dochdr-mark  { padding: 12px 16px; border-bottom: 1px solid #333; }
#doc-frame[data-doc-render="controlled"] .rc-dochdr-title {
    grid-area: title; padding: 12px 16px; border-bottom: 1px solid #333;
    font-size: 1.15rem; font-weight: 700; text-align: center; text-transform: uppercase;
    letter-spacing: .03em;
}
#doc-frame[data-doc-render="controlled"] .rc-dochdr-facts {
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: 0;
}
#doc-frame[data-doc-render="controlled"] .rc-dochdr-fact {
    display: block; padding: 7px 12px;
    border-right: 1px solid #ccc; border-bottom: 1px solid #ccc;
}
#doc-frame[data-doc-render="controlled"] .rc-dochdr-k { color: #555; }
#doc-frame[data-doc-render="controlled"] .rc-dochdr-v { color: #1a1a1a; font-weight: 600; }
#doc-frame[data-doc-render="controlled"] .rc-dochdr-gap { padding: 6px 14px; }
#doc-frame[data-doc-render="controlled"] .rc-dochdr-foot {
    padding: 6px 14px; color: #555; border-top: 1px solid #333; font-size: .68rem;
}
#doc-frame[data-doc-render="controlled"] .rc-dochdr-uncontrolled {
    text-transform: uppercase; letter-spacing: .08em; font-weight: 700;
}
/* The desk darkens; the page does not. See the note above. */
@media (prefers-color-scheme: dark) {
    #doc-frame[data-doc-render="controlled"] .rc-dochdr {
        box-shadow: 0 2px 18px rgba(0, 0, 0, .55);
    }
}

/* ── 2 · WORD ───────────────────────────────────────────────────────────────
   The bordered header table clients recognise from their own templates: mark
   left, title centre, a small facts grid right. Follows the user's theme.   */
#doc-frame[data-doc-render="word"] .rc-dochdr {
    grid-template-areas: "mark title facts" "gap gap gap" "foot foot foot";
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: 0;
    border: 1px solid var(--border);
    background: var(--bg-white);
}
#doc-frame[data-doc-render="word"] .rc-dochdr-mark {
    padding: 10px 14px; border-right: 1px solid var(--border); justify-content: center;
}
#doc-frame[data-doc-render="word"] .rc-dochdr-title {
    padding: 10px 16px; text-align: center; font-size: 1.05rem; font-weight: 700;
    border-right: 1px solid var(--border);
}
#doc-frame[data-doc-render="word"] .rc-dochdr-facts {
    grid-template-columns: auto auto; padding: 8px 14px; gap: 3px 12px;
}
#doc-frame[data-doc-render="word"] .rc-dochdr-foot { display: none; }

/* ── 3 · MODERN WEB ─────────────────────────────────────────────────────────
   No table at all: org line, page heading, facts as a metadata chip row.
   Follows the user's theme.                                                 */
#doc-frame[data-doc-render="modern"] .rc-dochdr {
    grid-template-areas: "mark" "title" "facts" "band" "gap" "foot";
    grid-template-columns: minmax(0, 1fr);
    gap: 8px;
    border: 0;
    border-bottom: 1px solid var(--border-subtle, var(--border));
    padding-bottom: 14px;
}
#doc-frame[data-doc-render="modern"] .rc-dochdr-mark  { gap: 8px; }
#doc-frame[data-doc-render="modern"] .rc-dochdr-title { font-size: 1.6rem; font-weight: 700; letter-spacing: -.01em; }
#doc-frame[data-doc-render="modern"] .rc-dochdr-facts {
    display: flex; flex-wrap: wrap; gap: 6px;
}
#doc-frame[data-doc-render="modern"] .rc-dochdr-fact {
    display: inline-flex; align-items: baseline; gap: 5px;
    background: var(--surface, rgba(127, 127, 127, .1));
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 999px; padding: 3px 11px;
}
#doc-frame[data-doc-render="modern"] .rc-dochdr-k { font-size: .65rem; }
#doc-frame[data-doc-render="modern"] .rc-dochdr-v { font-size: .8rem; font-weight: 600; }
#doc-frame[data-doc-render="modern"] .rc-dochdr-band {
    justify-self: start; font-size: .68rem; text-transform: uppercase;
    letter-spacing: .08em; color: var(--text-muted);
}
#doc-frame[data-doc-render="modern"] .rc-dochdr-foot { display: none; }

/* ── The appearance chooser ─────────────────────────────────────────────── */


/* Narrow viewports: every treatment collapses to a single column rather than
   letting the facts grid force the page to scroll sideways. */
@media (max-width: 720px) {
    #doc-frame[data-doc-render] .rc-dochdr {
        grid-template-areas: "band" "mark" "title" "facts" "gap" "foot";
        grid-template-columns: minmax(0, 1fr);
    }
    #doc-frame[data-doc-render="word"] .rc-dochdr-mark,
    #doc-frame[data-doc-render="word"] .rc-dochdr-title { border-right: 0; }
}

/* ═══ #299 / #307 — END OF THE DOCUMENT HEADER BLOCK ═══════════════════════
   Everything ABOVE this line, back to the "THE DOCUMENT HEADER" banner, is the
   header component and its three treatments. Everything BELOW is other work.

   No header treatment reaches into the findings machinery (the #307 acceptance
   bar). Keep this marker immediately before the first non-header block, so the
   header block has an end a reader can find (the test that sliced between the
   two went with the suite, v4.3.1955). */

/* ── #305 — accept-change popup: theme-aware, not hardcoded light ─────────────
   The "Replacing in the document" caption and its quoted block were built with
   INLINE light colours in process_graph.js, so in dark mode the panel stayed
   near-white and the quoted text was effectively invisible. Tokens now, so the
   panel follows the user's scheme like every other surface (standing rule: an
   interface respects user settings — never hardcode a palette). */
.pg-accept-caption {
    font-size: 11px; font-weight: 700; margin: 0 0 4px;
    color: var(--rc-muted, #64748b);
}
.pg-accept-caption--edit { color: var(--rc-accent, #4f6ddb); }
.pg-accept-quoted {
    font-size: 12px; margin-bottom: 10px; padding: 6px 8px;
    max-height: 90px; overflow: auto; white-space: pre-wrap;
    border-radius: 4px;
    color: var(--rc-fg, #334155);
    background: var(--rc-subtle-bg, #f1f5f9);
    border: 1px solid var(--rc-subtle-border, #e2e8f0);
}
@media (prefers-color-scheme: dark) {
    .pg-accept-caption  { color: #94a3b8; }
    .pg-accept-caption--edit { color: #93b4ff; }
    .pg-accept-quoted   { color: #e2e8f0; background: var(--pq-elevated); border-color: var(--pq-border); }
}
/* #1575 — every frame carries the inset accent bar. The animation overrides
   `box-shadow` wholesale while it runs, so a keyframe that omitted the bar would
   drop it for the first 900ms of every selection — the exact moment the mark is
   doing its job. The 100% frame matches the resting rule above. */
@keyframes rc-anchor-pulse {
    0%   { box-shadow: inset 4px 0 0 0 var(--rc-anchor-ring, #2563eb),
                       0 0 0 0 rgba(37, 99, 235, .55), 0 2px 10px rgba(15, 23, 42, .18); }
    70%  { box-shadow: inset 4px 0 0 0 var(--rc-anchor-ring, #2563eb),
                       0 0 0 10px rgba(37, 99, 235, 0), 0 2px 10px rgba(15, 23, 42, .18); }
    100% { box-shadow: inset 4px 0 0 0 var(--rc-anchor-ring, #2563eb),
                       0 0 0 5px rgba(37, 99, 235, .16), 0 4px 14px rgba(15, 23, 42, .18); }
}

/* Dark: same shape, the drawer's dark ring colour (#60a5fa) so the page mark and
   the drawer card stay the same "selected" in both schemes. */
@media (prefers-color-scheme: dark) {
    .rc-finding.is-active {
        outline-color: #60a5fa;
        box-shadow: inset 4px 0 0 0 #60a5fa,
                    0 0 0 5px rgba(96, 165, 250, .22),
                    0 4px 14px rgba(0, 0, 0, .5);
    }
}
@media (prefers-color-scheme: dark) {
    .pg-bpmn-host {
        /* Anchored to the app's own dark tokens (--bg #0f141a / --bg-white
           #1a212b / --text #e6eaf0) so the canvas sits IN the app rather than
           looking like a separate product. Strokes are lifted rather than
           inverted: a hairline that reads at 1.5px on white disappears on dark,
           so every outline moves up in luminance, not just down in darkness. */
        --rc-bpmn-paper:        #0f141a;
        --rc-bpmn-gutter: var(--pq-night-850);
        /* #1310 — was #46566b, 2.16:1 on the dark gutter. The gutter line is
           the lane boundary and carries which lane a step is in. Lightened 9%
           to 3.02:1. Light mode already passed at 8.40:1. */
        --rc-bpmn-gutter-line:  #586c87;
        --rc-bpmn-band-fg: var(--pq-mist-100);
        --rc-bpmn-task-fill: var(--pq-night-850);
        --rc-bpmn-task-stroke:  #6f8fc4;
        --rc-bpmn-call-stroke: var(--pq-blue-250);
        --rc-bpmn-task-num:     #cbd5e1;
        --rc-bpmn-task-title: var(--pq-mist-100);
        --rc-bpmn-gw-fill:      #2a2114;
        --rc-bpmn-gw-stroke: var(--pq-amber-500);
        --rc-bpmn-gw-fg:        #f6c88a;
        --rc-bpmn-ev-fill: var(--pq-night-850);
        --rc-bpmn-ev-start:     #4ade80;
        --rc-bpmn-ev-end:       #f87171;
        /* Dark: a deep tint rather than the light one, so the ring still reads
           and the glyph on it keeps its contrast. */
        --rc-bpmn-ev-start-fill: #14331f;
        --rc-bpmn-ev-end-fill:   #3a1a1d;
        --rc-bpmn-ev-stroke: var(--pq-mist-100);
        --rc-bpmn-ev-link: var(--pq-blue-250);
        --rc-bpmn-ev-glyph: var(--pq-rose-300);
        --rc-bpmn-ev-text: var(--pq-mist-100);
        --rc-bpmn-ev-caption: var(--pq-mist-100);
        --rc-bpmn-note-fill:    #171d26;
        --rc-bpmn-note-stroke:  #8fa1b5;
        --rc-bpmn-note-fg: var(--pq-mist-100);
        --rc-bpmn-art-fill:     #1c2530;
        --rc-bpmn-art-top:      #243040;
        --rc-bpmn-art-stroke:   #8fa1b5;
        --rc-bpmn-art-fg:       #d7dee7;
        --rc-bpmn-link: var(--pq-blue-250);
        --rc-bpmn-link-lbl-bg: var(--pq-night-850);
        /* #1310 — was #46566b, 2.16:1. Same reasoning as the light value. */
        --rc-bpmn-link-lbl-bd:  #586c87;
        --rc-bpmn-link-lbl-fg: var(--pq-mist-100);
        --rc-bpmn-port-fill:    #1e3a5f;
        --rc-bpmn-port-stroke: var(--pq-blue-350);
        --rc-bpmn-select: var(--pq-blue-350);
        --rc-bpmn-finding: var(--pq-rose-300);
    }
}

/* #392 — the structural set's run stamp. It is minted ON DEMAND and therefore
   deliberately behind the document between runs; a list that is behind must say
   so, or it reads as the app failing to notice rather than as a check nobody has
   re-run. Same discipline as the provisional # on a step number. */
.findings-run-stamp{
    font-size:11.5px; color:var(--text-muted,#64748b); font-style:italic;
    margin-left:8px; white-space:nowrap; align-self:center;
}
/* NEVER RUN is the one that must not read as reassurance — an empty list there
   means nobody looked, not that there is nothing to find. */
/* #405 sweep-on-touch: was a hardcoded amber. `--warn-fg` is not defined
   anywhere, so the literal fallback won every time and read at roughly
   3.3:1 against the dark background. `--btn-caution` is defined in both
   themes (:3134 / :3140). */
.findings-run-stamp.is-never{ color: var(--btn-caution); font-weight:600; font-style:normal; }
@media (prefers-color-scheme: dark) { .findings-run-stamp.is-never { color: #fbbf24; } }
@media (prefers-color-scheme: dark){
    .findings-run-stamp{ color:#94a3b8; }
    
}

/* ── #1747 Settings > External Data (and the housekeeping list on Maintenance) ──
   The settings family's calmer treatment (#1630): hairlines, whitespace, no
   boxed panels. Every colour is a TOKEN defined in both themes, and each chip is
   a background/ink PAIR from one family, so dark mode needs no second block. */
.xd-summary { margin-top: 10px; }
.xd-stats { display: flex; flex-wrap: wrap; gap: 28px; margin: 6px 0 10px; }
.xd-stat-n { font-size: 1.5rem; font-weight: 600; color: var(--text); line-height: 1.1; }
.xd-stat-l { font-size: 0.78rem; color: var(--text-muted); }
.xd-stat-failing .xd-stat-n { color: var(--conflict-text); }
.xd-stat-out_of_date .xd-stat-n { color: var(--gap-text); }
.xd-totals { font-size: 0.85rem; color: var(--text); margin-bottom: 10px; }
.xd-toolbar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.xd-regulator { margin-bottom: 8px; }
.xd-regulator > h4 { font-size: 0.9rem; font-weight: 600; color: var(--text); margin: 14px 0 2px; }
.xd-row {
    display: grid;
    grid-template-columns: minmax(260px, 3fr) minmax(150px, 1.2fr) minmax(170px, 1.3fr) minmax(170px, 1.4fr) auto;
    gap: 16px;
    align-items: start;
    padding: 14px 0;
    border-top: 1px solid var(--border);
    font-size: 0.85rem;
    color: var(--text);
}
.xd-name { font-weight: 600; font-size: 0.92rem; }
.xd-muted { color: var(--text-muted); font-size: 0.82rem; }
/* #1926 — the coverage report: one table per group, theme tokens only. */
.xd-coverage > h4 { font-size: 0.92rem; font-weight: 600; color: var(--text); margin: 16px 0 6px; }
.xd-cov-wrap { overflow-x: auto; }
.xd-cov { width: 100%; border-collapse: collapse; font-size: 0.85rem; color: var(--text); }
.xd-cov th { text-align: left; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em;
             color: var(--text-muted); padding: 6px 10px 6px 0; border-bottom: 1px solid var(--border); }
.xd-cov td { vertical-align: top; padding: 10px 10px 10px 0; border-top: 1px solid var(--border); }
.xd-cov td:first-child { min-width: 240px; }
.xd-cov-share { font-weight: 600; }
.xd-cov-atlas-yes { color: var(--text); font-weight: 600; }
.xd-cov-todo-nothing { color: var(--text-muted); }
.xd-cov-todo-credential, .xd-cov-todo-switch_on { color: var(--gap-text); font-weight: 600; }
.xd-loaders-head { margin-top: 22px; }
.xd-label {
    display: block; font-size: 0.72rem; text-transform: uppercase;
    letter-spacing: 0.04em; color: var(--text-muted); margin-bottom: 3px;
}
.xd-statusline { margin-top: 6px; display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
.xd-chip {
    display: inline-block; padding: 1px 9px; border-radius: 999px;
    font-size: 0.74rem; font-weight: 600; white-space: nowrap;
    background: var(--state-none-bg); color: var(--text-muted);
}
.xd-chip-current             { background: var(--inserted-bg); color: var(--inserted-text); }
.xd-chip-updating            { background: var(--primary-pale); color: var(--primary-ink); }
.xd-chip-failed,
.xd-chip-needs_setup         { background: var(--conflict-bg); color: var(--conflict-text); }
.xd-chip-stale,
.xd-chip-incomplete,
.xd-chip-never               { background: var(--gap-bg); color: var(--gap-text); }
.xd-chip-awaiting_permission { background: var(--unknown-bg); color: var(--unknown-text); }
.xd-chip-manual_only         { background: var(--vague-bg); color: var(--vague-text); }
.xd-chip-disabled            { background: var(--state-none-bg); color: var(--text-muted); }
.xd-error {
    margin-top: 8px; padding: 6px 0 6px 10px;
    border-left: 3px solid var(--conflict-text); color: var(--text);
}
.xd-unlock, .xd-manual, .xd-held, .xd-licence { margin-top: 8px; }
.xd-attrib { font-size: 0.8rem; color: var(--text-muted); font-style: italic; margin-top: 2px; }
.xd-raw summary { cursor: pointer; color: var(--text-muted); font-size: 0.78rem; }
.xd-raw code { display: block; white-space: pre-wrap; word-break: break-word; font-size: 0.76rem; }
.xd-sched { display: flex; flex-direction: column; gap: 5px; align-items: flex-start; }
.xd-select {
    padding: 3px 6px; font-size: 0.85rem; border: 1px solid var(--border);
    border-radius: 8px; background: var(--bg-white); color: var(--text);
}
.xd-next { font-size: 0.82rem; }
.xd-actions { display: flex; flex-direction: column; gap: 6px; align-items: stretch; }
.xd-actions .btn-sem, .xd-small { white-space: nowrap; }
.xd-small { font-size: 0.8rem; }
/* A steady indicator that stays until the run ends (#1747: "a steady progress
   indicator with counts, persisting until done"). The motion is a slow sweep;
   with reduced motion requested it is a still, half-filled bar — the words
   beside it carry the meaning either way. */
.xd-progress { margin-top: 8px; }
.xd-progress-bar {
    position: relative; height: 4px; border-radius: 999px; overflow: hidden;
    background: var(--primary-pale);
}
.xd-progress-bar span {
    position: absolute; top: 0; bottom: 0; width: 35%;
    background: var(--primary); border-radius: 999px;
    animation: xd-sweep 1.8s ease-in-out infinite;
}
@keyframes xd-sweep { 0% { left: -35%; } 100% { left: 100%; } }
@media (prefers-reduced-motion: reduce) {
  /* the still state shown in place of the motion (#1903: the motion itself
     is off for everything, by the one global rule near the top) */
  .xd-progress-bar span { left: 0; width: 50%; }
}
.xd-progress-text { font-size: 0.82rem; margin-top: 4px; }
.xd-table { width: 100%; border-collapse: collapse; font-size: 0.82rem; margin: 6px 0 14px; }
.xd-table th {
    text-align: left; font-weight: 600; color: var(--text-muted);
    border-bottom: 1px solid var(--border); padding: 4px 8px 4px 0;
}
.xd-table td { border-bottom: 1px solid var(--border); padding: 5px 8px 5px 0; vertical-align: top; }
.xd-num { text-align: right; font-variant-numeric: tabular-nums; }
.xd-url { max-width: 380px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#xd-detail-body h4 { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.04em;
                     color: var(--text-muted); margin: 18px 0 6px; }
@media (max-width: 1000px) {
    .xd-row { grid-template-columns: 1fr; }
    .xd-actions { flex-direction: row; flex-wrap: wrap; }
}

/* ═══ #460 — THE IDLE-LOGOUT WARNING ════════════════════════════════════════
   Craig 2026-07-29: "we need a 1 minute warning for logout on time, popup a
   dialog and then count down within it. Any mouse move cancels it".

   Branded modal pattern (logo sidebar left, content right) per the standing UI
   rule — never a native confirm()/alert(), which cannot be styled and which no
   countdown could live inside anyway.

   It carries NO buttons on purpose. The dismissal gesture IS presence: moving
   the mouse or pressing a key. A button would make the user act to stay where
   they already are, and a user who is reading would still be logged out.

   Above the modal band (11060 > 11050) — this is the ONE overlay that must be
   seen over anything else on screen, including a modal, because it is about to
   take the screen away. Deliberately outside the ordinal popover band
   (rc_anchor.js, 10150-10199), which is capped below modals for the opposite
   reason. */
.rc-idle-warn {
    position: fixed; inset: 0; z-index: 11060;
    display: flex; align-items: center; justify-content: center;
    background: rgba(15, 23, 42, .55);
}
.rc-idle-card {
    display: flex; min-width: 380px; max-width: 560px;
    border-radius: 10px; overflow: hidden;
    background: var(--bg-white, #fff); color: var(--text, #0f172a);
    box-shadow: 0 18px 50px rgba(15, 23, 42, .38);
}
.rc-idle-logo {
    flex: none; width: 92px; display: flex; align-items: center; justify-content: center;
    background: var(--primary, #1a2b45); padding: 18px 12px;
}
.rc-idle-logo img { width: 56px; height: auto; }
.rc-idle-main { padding: 18px 22px 20px; }
.rc-idle-main h2 { margin: 0 0 8px; font-size: 1.05rem; }
.rc-idle-main p { margin: 0 0 6px; font-size: .92rem; line-height: 1.45; }
.rc-idle-main b { font-variant-numeric: tabular-nums; }   /* the number must not jitter */
.rc-idle-hint { color: var(--text-muted, #64748b); font-size: .84rem; }

@media (prefers-color-scheme: dark) {
    .rc-idle-card { background: var(--bg-white, #111827); color: var(--text, #e5e7eb); }
    .rc-idle-hint { color: var(--text-muted, #94a3b8); }
}

/* ═══ #432 — THE DRAWER HOST: one implementation, one conditional ═══════════
   Craig 2026-07-29, rejecting both shapes I offered (restructure the layout, or
   float everywhere): "we cant do Either of those, we cant change the size or
   shape of the process - it would be a mess, floating is best in some cases and
   current behavior inthe document case. Can we put a single conditional block
   into the drawer to hadle different behavior on the different surfaces?"

   He is right about the canvas: reflowing a diagram to make room would move every
   object the reader is looking at, whereas a document reflows harmlessly and
   being COVERED would hide the very text a finding points at.

   HOW THE ONE CONDITIONAL WORKS. The host is ALWAYS fixed to the right edge and
   always out of flow, so it floats over whatever surface is showing. The
   per-surface difference is a RESERVE: the document surface pads itself by the
   open width and therefore still shrinks exactly as before, while Level 1,
   Level 2 and BPMN reserve nothing and their canvas never resizes.
   `--rc-drawer-reserve` is written by RCDrawers.render(), the one writer.

   Height matches .app-wrap's `calc(100vh - 52px)` — the 52px app header. */
.rc-drawerhost {
    position: fixed;
    right: 0;
    /* MEASURED per surface (RCDrawers.applyTop), not the app header's height:
       the canvas carries its own toolbar below that header, and the drawer's
       whole header band — the Content / Structural tabs, the run stamp,
       Auto-debate, Hide resolved — was sitting behind it. The fallback is the
       old constant, so a failed measurement degrades to the document surface's
       correct value rather than to nothing. */
    top: var(--rc-drawer-top, 52px);
    bottom: 0;
    /* Over both canvases AND their floating tool rails; under the popovers (700)
       and modals. #1902: at 30 the canvas's Control rail (.pg-palette, 200) sat on
       the drawer tabs whenever the open drawers reached the left of the canvas
       (two drawers from a Level 2 card, then BPMN), so they could not be pushed
       back in. A tab is how a drawer closes; it must never be under the canvas. */
    z-index: var(--z-dropdown);
    display: flex;
    align-items: stretch;
    pointer-events: none;     /* the empty strip must not eat canvas clicks … */
}
.rc-drawerhost > * { pointer-events: auto; }   /* … but the rail and drawers do */

/* The rail rides at the host's LEFT edge, so it travels OUTWARD as drawers open
   instead of staying welded to the window border. That is #453 — delivered by
   the geometry rather than by a second mechanism, because the host's right edge
   is pinned and it grows leftwards. */
.rc-drawerhost .rc-tabrail {
    position: absolute;
    right: 100%;
    top: 44px;                /* was 96px when it sat inside main, which began
                                 below the app header; the host already starts
                                 at 52px, so the offset is measured from there */
    z-index: 9;
}

/* THE RESERVE — the single conditional, and the only per-surface difference.
   Applied to the document surface alone; the canvas surfaces never see it. */
/* The RESERVING surfaces. The canvas surfaces are deliberately absent — Craig:
   "we cant change the size or shape of the process". The QMS list is here because
   it is document-like: it reflows harmlessly, and covering a table of documents to
   show progress about one of them would hide the thing being described. Kept in
   step with RC_DRAWER_RESERVING in app.js. */
.app-wrap,
.qms-access-wrap { padding-right: var(--rc-drawer-reserve, 0px); }

@media (prefers-reduced-motion: no-preference) {
    .app-wrap,
    .qms-access-wrap { transition: padding-right .18s ease; }
}

/* ── #510/#520 — the Level 2 GROUPING control panel ──────────────────────────
   A floating panel rather than a modal, deliberately: Craig asked to "change
   them in real time and see what happens", and a modal covers the very map the
   controls are changing. It is positioned against the palette button that opened
   it (the contextual-UI rule — contextual controls appear at the click).

   Every colour comes from the theme tokens, so the panel follows the viewer's
   light/dark setting with no second palette of its own. */
.pg-grouping-panel {
    position: fixed; z-index: 9000; width: 268px;
    background: var(--surface); color: var(--text);
    border: 1px solid var(--border); border-radius: 8px;
    box-shadow: 0 8px 28px rgba(0,0,0,0.18);
    font-size: 12px; padding: 0 0 8px;
    max-height: 76vh; overflow-y: auto;
}
.pg-gp-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 9px 10px; border-bottom: 1px solid var(--border);
    font-weight: 700; letter-spacing: 0.02em;
}


.pg-gp-warn {
    margin: 8px 10px; padding: 7px 9px; border-radius: 5px;
    background: var(--primary-pale); color: var(--primary-fg);
    border: 1px solid var(--border); line-height: 1.45;
}
.pg-gp-row { padding: 7px 10px 2px; }
.pg-gp-row label { display: block; color: var(--text-muted); margin-bottom: 3px; }
.pg-gp-row label b { color: var(--text); font-weight: 700; }
.pg-gp-row input[type="range"] { width: 100%; margin: 0; accent-color: var(--primary-light); }
.pg-gp-toggle {
    display: flex; align-items: center; gap: 8px;
    padding: 9px 10px; cursor: pointer;
}
.pg-gp-toggle span { color: var(--text); }
.pg-gp-toggle input { accent-color: var(--primary-light); }
.pg-gp-foot {
    display: flex; align-items: center; justify-content: space-between;
    gap: 8px; padding: 9px 10px 2px; margin-top: 4px;
    border-top: 1px solid var(--border);
}
/* #598 — the re-group row. Its own block below the foot, NOT another chip in the
   reset line: the resets replay stored parameters over the existing buckets and
   are instant, this throws the buckets away and re-reads the process with a model
   call per process. Putting a slow, destructive-of-derived-work action in a row of
   one-word buttons is how it gets pressed by accident. */
.pg-gp-regroup {
    display: flex; flex-direction: column; gap: 4px;
    padding: 8px 10px 10px; margin-top: 6px;
    border-top: 1px solid var(--border);
}
.pg-gp-regroup-btn {
    align-self: flex-start;
    padding: 5px 11px;
    border: 1px solid var(--border); border-radius: 999px;
    background: transparent; color: var(--text); font: inherit; cursor: pointer;
}
.pg-gp-regroup-btn:hover:not(:disabled) {
    border-color: var(--primary-fg); color: var(--primary-fg);
}
/* Disabled IS the progress state — the button relabels to "Re-grouping…" and
   stays that way for the duration, which is the persistent indicator the product
   standard asks for on anything over ~1-2s. */
.pg-gp-regroup-btn:disabled { opacity: 0.6; cursor: progress; }
.pg-gp-regroup-note { color: var(--text-muted); font-size: 0.72rem; line-height: 1.3; }

.pg-gp-status { color: var(--text-muted); line-height: 1.35; }
/* A refusal is not styled as a warning tint the eye slides past — the panel is
   small and the message is the only thing that explains why nothing moved. */
.pg-gp-status.pg-gp-bad { color: var(--danger, #b91c1c); font-weight: 600; }
.pg-gp-reset {
    flex-shrink: 0; background: transparent; cursor: pointer;
    border: 1px solid var(--border); border-radius: 5px;
    padding: 3px 9px; font-size: 11px; color: var(--text-muted);
}
.pg-gp-reset:hover { border-color: var(--primary-light); color: var(--primary-fg); }

/* ── #511/#428 — THE PROCESS HEADER RECORD ───────────────────────────────────
   Craig 2026-08-02, on why #428 could not simply be closed: "we were going to
   put a header record on for each process and that needs to be done before we
   can resolve #428." The 232px strip is not wasted canvas — it is reserved, and
   until it carried a real record that reservation looked like a bug.

   So the band's block becomes a CARD: name, summary, the counts that describe
   the process (activities, steps, decisions) and the findings badge. Every value
   is read from the payload; nothing here is computed from the drawing. */
.pg-gav2-gutter-card {
    margin: 0 10px 0 8px; padding: 8px 10px;
    /* Craig 2026-08-02: "I think the header should not be white, but say a light
       blue." This is `--pg-ga-phase-bg` — the colour the band header ALREADY
       used before it became a card — not a new blue. A second light blue that
       drifted from the first would be visible on the same screen. It carries a
       dark-mode variant, so the card follows the viewer's theme without a
       second rule here. */
    background: var(--pg-ga-phase-bg, #eef3f9);
    color: var(--pg-ga-phase-fg, #1a3a5c);
    border: 1px solid var(--border, #cbd5e0); border-radius: 8px;
    box-shadow: 0 1px 3px rgba(0,0,0,0.06);
    pointer-events: auto;
}
/* The counts sit ON the blue now, so they take the phase ink rather than the
   page's muted grey — which was chosen against a white card and goes muddy. */
.pg-gav2-gutter-card .pg-gav2-gutter-stats { color: var(--pg-ga-phase-fg, #1a3a5c); opacity: 0.75; }
.pg-gav2-gutter-card .pg-gav2-gutter-stats b { color: var(--pg-ga-phase-fg, #1a3a5c); opacity: 1; }
.pg-gav2-gutter-card .pg-gav2-gutter-summary {
    color: var(--pg-ga-phase-fg, #1a3a5c);
    opacity: 0.8;
    margin: 0 0 6px 0;
}
.pg-gav2-gutter-card .pg-gav2-gutter-label {
    margin: 0 0 4px 0; padding: 0; border: 0; background: none;
    font-size: 1.0rem;
}
/* The counts row. Muted and small — it describes the process, it is not the
   headline, and a band with no decisions should not shout about having none. */
.pg-gav2-gutter-stats {
    display: flex; flex-wrap: wrap; gap: 4px 10px;
    font-size: 0.74rem; color: var(--text-muted, #64748b);
    margin: 0 0 2px 0;
}
.pg-gav2-gutter-stats b { color: var(--text, #2d3748); font-weight: 700; }
.pg-gav2-gutter-card .pg-gav2-gutter-badge { margin: 6px 0 0 0; }

/* #533 — the reset group in the grouping panel. Three small buttons on one row;
   they wrap rather than squeeze, because "As ingested" is hidden on older
   documents and the row must look deliberate either way. */
.pg-gp-resets { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }

/* ── #590 · Admin → Structural Rules ─────────────────────────────────────────
   A switched-off rule stays legible rather than disappearing: the operator needs
   to see what they have turned off in order to turn it back on. Muted, not
   hidden. */
.sr-table tbody tr { border-bottom: 1px solid var(--border-subtle); }
.sr-table tbody tr.sr-off { opacity: 0.55; }
.sr-table select { padding: 4px 6px; border: 1px solid var(--border);
                   border-radius: 6px; background: var(--surface);
                   color: var(--text); font-size: 0.85rem; }
.sr-seeded {
    display: inline-block; margin-left: 6px; padding: 1px 6px;
    border: 1px solid var(--border); border-radius: 999px;
    font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--text-muted); vertical-align: middle;
}

/* ── #321 · the Structural drawer ────────────────────────────────────────────
   A PEER of Findings, so it inherits every .rc-drawer rule and needs almost
   nothing of its own. What it does need is its own head layout: the run stamp
   has to sit beside the title rather than wrap, because between runs it is the
   only thing distinguishing "checked and clean" from "nobody has looked". */
#rc-drawer-s .rc-drawer-head { flex-wrap: wrap; row-gap: 4px; }
#rc-drawer-s .findings-run-stamp { flex: 1 1 100%; order: 10; }

/* #624 — the structural rule's CODE, on its findings and on its settings row.
   The counterpart of a quality finding's framework citations: it is what lets a
   reader get from "what am I looking at" to "which switch turns it off".
   Deliberately quiet — it is provenance, not a severity. */
.cat-badge.rule-code-badge {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text-muted);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.66rem;
    letter-spacing: 0.02em;
}
.sr-code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--text);
}

/* #472 — the findings-drawer sort selector. Sits in the drawer head beside the
   toggle buttons and is deliberately quiet: it reports an order, it is not an
   action. Matches `.run-all-debates-btn`'s height so the head row stays level. */
.finding-sort-select {
    font-size: 0.72rem;
    font-weight: 600;
    padding: 3px 6px;
    border-radius: 7px;
    background: transparent;
    color: var(--text-muted);
    border: 1px solid var(--border);
    cursor: pointer;
    max-width: 130px;
}
.finding-sort-select:hover:not(:disabled) { border-color: var(--primary); color: var(--primary-ink); }
.finding-sort-select:disabled { opacity: .5; cursor: default; }

/* #668 — how many of this document's findings are NOT drawn on this surface.
   Sits in the relocated pill row, in the same words the Document surface uses
   for the same fact. Amber rather than red: it is a gap to be aware of, not a
   failure — the findings exist and are listed elsewhere, they are simply not
   attached to a step this surface draws (console_level_carries_meaning). */
.rc-pill-unplaced {
    margin-left: 10px;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 0.72rem;
    font-weight: 600;
    background: #fef3c7;
    color: #92400e;
    white-space: nowrap;
}
@media (prefers-color-scheme: dark) {
    :root .rc-pill-unplaced { background: #4a3410; color: #fcd34d; }
}

/* #634 — WHICH STEP a finding is about, on the card. Number AND label together:
   the number locates it, the label identifies it. The `is-none` variant is the
   honest refusal — the object could not name a step, so the card says that
   rather than composing something plausible (P3.1). */
.finding-step-badge {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 7px;
    border-radius: 9px;
    font-size: 0.7rem;
    font-weight: 600;
    background: var(--bg-subtle, rgba(127, 127, 127, 0.12));
    color: var(--text-muted);
    vertical-align: middle;
}
.finding-step-badge.is-none {
    font-weight: 400;
    font-style: italic;
    opacity: 0.85;
}

/* ═══════════════════════════════════════════════════════════════════════════
   STEP DETAIL (#435) — the one blocking modal that IS the step.
   Replaces the BPMN Detail drawer, the step editor's context, and the finding
   workbench. Craig: modal-sized, NOT full screen.

   EVERY COLOUR IS A THEME TOKEN — no literals. The app remaps the core vars
   under `@media (prefers-color-scheme: dark)` at :root, so writing only in
   var(--surface) / var(--text) / var(--border) / var(--text-muted) means dark
   mode is correct here by construction rather than by a second block that has
   to be kept in step. Craig's standing rule: interfaces respect user settings.
   ═══════════════════════════════════════════════════════════════════════════ */

.rc-sd-box {
    /* Modal-sized. `min()` so it never exceeds the viewport on a laptop, and the
       body scrolls internally rather than the page — a blocking dialog whose
       close button has scrolled off screen is a trap. */
    max-width: min(1080px, 94vw);
    width: min(1080px, 94vw);
    max-height: 88vh;
    display: flex;
    flex-direction: column;
}
.rc-sd-box .modal-split-body { overflow: hidden; min-height: 0; }
.rc-sd-box .modal-body-inner {
    display: flex; flex-direction: column; min-height: 0; overflow: hidden;
}

.rc-sd-navbar {
    display: flex; align-items: center; gap: 12px;
    padding: 12px 20px; border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}
.rc-sd-heading {
    flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 10px;
    flex-wrap: wrap;
}
.rc-sd-eyebrow {
    font-size: 0.68rem; font-weight: 700; letter-spacing: 0.08em;
    color: var(--text-muted);
}
.rc-sd-step { font-size: 1.05rem; font-weight: 700; color: var(--text); }
.rc-sd-ro {
    font-size: 0.62rem; font-weight: 700; letter-spacing: 0.08em;
    color: var(--text-muted); border: 1px solid var(--border);
    border-radius: 3px; padding: 2px 6px;
}
/* Soft outline, per the button standard. Disabled at the ends of the process —
   visible so the walk's extent is legible, rather than vanishing. */
.rc-sd-nav {
    background: transparent; border: 1px solid var(--border); color: var(--text);
    border-radius: 4px; width: 30px; height: 30px; cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    flex-shrink: 0;
}
.rc-sd-nav:hover:not(:disabled) { border-color: var(--primary); color: var(--primary-ink); }
.rc-sd-nav:disabled { opacity: 0.35; cursor: default; }

.rc-sd-cols {
    display: flex; gap: 0; min-height: 0; flex: 1; overflow: hidden;
}
.rc-sd-left {
    flex: 1 1 46%; min-width: 0; overflow-y: auto; padding: 16px 20px;
    border-right: 1px solid var(--border);
}
.rc-sd-right {
    flex: 1 1 54%; min-width: 0; overflow-y: auto; padding: 16px 20px;
}
/* Narrow viewports stack rather than crush two panes to unreadable columns. */
@media (max-width: 900px) {
    .rc-sd-cols { flex-direction: column; overflow-y: auto; }
    .rc-sd-left { border-right: none; border-bottom: 1px solid var(--border); }
    .rc-sd-left, .rc-sd-right { overflow: visible; flex: none; }
}

.rc-sd-essence {
    font-size: 0.95rem; font-weight: 600; color: var(--text); margin-bottom: 8px;
}
.rc-sd-text {
    font-size: 0.88rem; line-height: 1.65; color: var(--text);
    white-space: pre-wrap; margin-bottom: 14px;
}
.rc-sd-edit {
    background: transparent; border: 1px solid var(--border); color: var(--text);
    border-radius: 4px; padding: 5px 11px; font-size: 0.8rem; cursor: pointer;
    display: inline-flex; align-items: center; gap: 6px; margin-bottom: 16px;
}
.rc-sd-edit:hover { border-color: var(--primary); color: var(--primary-ink); }

.rc-sd-section { margin-bottom: 14px; }
.rc-sd-section-title, .rc-sd-pane-title {
    font-size: 0.7rem; font-weight: 700; letter-spacing: 0.06em;
    text-transform: uppercase; color: var(--text-muted);
    margin-bottom: 6px; display: flex; align-items: center; gap: 8px;
}
.rc-sd-pane-title { margin-top: 4px; }
.rc-sd-pane-title:not(:first-child) { margin-top: 18px; }
.rc-sd-count {
    font-weight: 700; font-size: 0.68rem; color: var(--text-muted);
    border: 1px solid var(--border); border-radius: 8px; padding: 0 6px;
}
.rc-sd-chips { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 4px; }
.rc-sd-chip {
    font-size: 0.75rem; padding: 2px 8px; border-radius: 3px;
    border: 1px solid var(--border); color: var(--text); background: transparent;
}
.rc-sd-chip-responsible { border-color: var(--primary); color: var(--primary-ink); font-weight: 600; }
.rc-sd-chip-code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.rc-sd-empty { font-size: 0.8rem; color: var(--text-muted); font-style: italic; }
/* #1493 — the step's own note on the detail panel. Craig: "i need to do another
   step to see the content - I think it should be all visible on this interface."
   One div per source line, because the reader keeps the paragraphs the author
   wrote rather than flattening them into one sentence. Colours are tokens, so it
   follows the viewer's light/dark choice like everything else on the panel. */
.rc-sd-notes { font-size: 0.82rem; line-height: 1.45; color: var(--text-primary); }
.rc-sd-notes > div { margin-bottom: 2px; }
.rc-sd-notes > div:last-child { margin-bottom: 0; }

/* A refusal, not a card — P3.1. Yellow because the app is healthy and a rule was
   applied (`a_refusal_is_a_warning_that_carries_its_next_step`); red here would be
   one more thing making red mean nothing. */
.rc-sd-refusal {
    font-size: 0.8rem; line-height: 1.5; color: var(--text);
    border-left: 3px solid #d9a441; background: rgba(217, 164, 65, 0.10);
    padding: 8px 10px; border-radius: 3px; margin-bottom: 10px;
}

/* #90 — the edit-in-place window is a PROPER editing surface.
   Craig 2026-07-03: "should open a larger window with the usual TipTap menu
   showing." The 520px box with a bare textarea it replaces made editing a passage
   of a controlled document feel nothing like editing the document.
   Colours are inherited from .edit-suggestion-modal, which is already tokenised. */
.edit-inplace-modal {
    width: min(860px, 94vw);
    max-height: 86vh;
}
/* THE TOOLBAR AND THE WRAPPER OWN THE LOOK; these two rules only seat them.
   `RCEditorToolbar.render` stamps `.rc-tiptap-toolbar`, which brings the flex
   layout, gap, padding, theme background and wrap behaviour (B-016); the
   `.rc-tiptap-wrap` around them brings the --rc-ed-* tokens those rules read AND
   the rounded bordered frame. Restating any of it here would be a second opinion
   about the editor's appearance, and the one that drifts. */
.edit-inplace-wrap { min-height: 0; }
.edit-inplace-toolbar { flex-shrink: 0; }
.edit-inplace-editor {
    /* Roomy, but bounded: the passage scrolls inside the editor so the Apply and
       Cancel buttons never leave the screen on a laptop. */
    min-height: 220px;
    max-height: 52vh;
    overflow-y: auto;
    padding: 10px 12px;
    background: var(--surface);
}
.edit-inplace-editor .ProseMirror { outline: none; min-height: 200px; }
.edit-inplace-editor .ProseMirror p { margin: 0 0 0.6em; line-height: 1.6; }

/* #139 — a persona slot whose saved model is not the one running.
   Yellow because it is a REFUSAL, not a failure: the app understood the setting,
   applied a rule and declined it, and everything still works. Painting it red
   here is how red stops meaning anything on a page that also reports faults. */
.admin-persona-correction {
    margin-top: 6px;
    padding: 6px 8px;
    font-size: 0.78rem;
    line-height: 1.45;
    color: var(--text);
    border-left: 3px solid #d9a441;
    background: rgba(217, 164, 65, 0.10);
    border-radius: 3px;
}
.admin-persona-correction code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.76rem;
}

/* #165 — a field that REFLECTS a value owned by another surface. Muted, and it
   says where the value comes from: a read-only box with no explanation reads as a
   bug ("why can't I type here?"). */

.qma-input[readonly] {
    background: var(--bg, #f0f2f5);
    color: var(--text-muted);
    cursor: default;
}

/* #2 — the always-visible Report an issue control.
   Bottom-LEFT, deliberately: the bottom-right corner is already occupied on the
   process surfaces by the Navigator minimap, and a control that sits on top of
   another one on three of four surfaces is not always-visible, it is sometimes-
   in-the-way. Small and quiet until hovered — it is on every screen and must not
   compete with the work. */
:root { --report-fab-inset: 14px; --report-fab-size: 34px; }   /* #1880 — the QMS sidebar clears it */
#rc-report-fab {
    position: fixed;
    left: var(--report-fab-inset);
    bottom: var(--report-fab-inset);
    z-index: 40;
    width: var(--report-fab-size);
    height: var(--report-fab-size);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: var(--surface);
    color: var(--text-muted);
    cursor: pointer;
    opacity: 0.55;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
    transition: opacity .12s ease, color .12s ease, border-color .12s ease;
}
#rc-report-fab:hover,
#rc-report-fab:focus-visible {
    opacity: 1;
    color: var(--primary-ink);
    border-color: var(--primary);
}
#rc-report-fab .ic { width: 17px; height: 17px; }
/* The login screen is not a place to report an issue about a document, and the
   modal needs a session to submit. Hidden while the login modal is up.
   #1836 (code review FE-F60) — a BODY CLASS now, set by the two functions that
   are the modal's only writers (showLogin / hideLogin, app.js). The rule it
   replaces, `body:has(#login-modal[style*="flex"])`, was a document-root :has with
   an attribute-substring match, re-checked by the browser on any style change
   anywhere. The first body-class version failed because NOTHING set its class
   (`body.rc-authed`); this one is set where the modal's display is. */
body.rc-login-open #rc-report-fab { display: none; }


/* #91/#115 — the welcome-message editors and the comms composer.
   Status text carries its meaning in colour AND in words: the words are what a
   screen reader and a colour-blind reader get, so the colour is the redundant
   half rather than the only signal. */
.wm-scope { margin-bottom: 16px; }
.wm-scope label { display: block; font-size: 0.85rem; font-weight: 600; margin-bottom: 4px; }
.wm-scope textarea,
#admin-comms-email input,
#admin-comms-email textarea { width: 100%; margin-bottom: 6px; }
.wm-status { margin-left: 10px; font-size: 0.8rem; color: var(--text-muted); }
.wm-status.is-ok   { color: light-dark(#2f855a, #68d391); }
/* #405 sweep-on-touch: hardcoded amber -> the themed caution token. */
.wm-status.is-warn { color: var(--btn-caution); }
@media (prefers-color-scheme: dark) { .wm-status.is-warn { color: #f6ad55; } }
.wm-status.is-err  { color: light-dark(#c53030, #fc8181); }

/* #648 — the QMS row's title is the opener, and the reclaimed column carries data.
   Craig: "convert the 5 icons on each QMS document into a popup list on the name
   of the document, save a great deal of real estate which we should then use by
   showing some more data around the QMS item." */
.qma-title-btn {
    background: none; border: none; padding: 0; margin: 0;
    font: inherit; color: inherit; text-align: left; cursor: pointer;
    display: inline-flex; align-items: center; gap: 5px;
}
.qma-title-btn:hover { color: var(--primary-ink); }
/* #2125 — the title says it heard the click while its document's record loads: the
   menu draws only when the record arrives, and a slow server left nothing on screen.
   No animation, so nothing moves for anyone who asks for reduced motion. */
.qma-title-btn.is-loading { cursor: progress; opacity: 0.6; }
/* #732 — the child (in-progress Draft) and history rows carry the same menu.
   No new colour and no new size: the tag inside keeps its own styling, so this
   only has to stop the button imposing the parent row's weight on it. The
   visual-standards ratchet (#743) counts a hardcoded colour here as debt and
   would be right to — there is none. */
.qma-title-btn-child { font: inherit; color: inherit; }
.qma-title-btn-child:hover { color: var(--primary-ink); }
/* The caret follows the row it is in, so an indented row reveals it on hover
   exactly as the parent does. */
.qma-row-child:hover .qma-title-caret,
.qma-row-history:hover .qma-title-caret { opacity: .7; }
/* The caret is the affordance — the title alone gives no sign there is a menu
   behind it. Quiet until the row is hovered so a list of thirty does not read as
   thirty controls. */
.qma-title-caret { opacity: 0; transition: opacity .12s ease; }
.qma-title-caret .ic { width: 13px; height: 13px; }
.qma-row:hover .qma-title-caret,
.qma-title-btn:focus-visible .qma-title-caret { opacity: .7; }
.qma-cell-analysis { font-size: 0.8rem; color: var(--text-muted); white-space: nowrap; }
.qma-an-none { font-style: italic; }   /* #1880 — the cell's muted ink at full strength; .75 measured 3.01 */
.qma-an-flag {
    margin-left: 6px; padding: 1px 6px; border-radius: 3px;
    border: 1px solid var(--border); font-size: 0.72rem;
}
/* A menu entry that cannot work is SHOWN disabled with the reason on hover, not
   hidden — the buttons used to disappear, which leaves a user wondering whether
   the feature exists at all. */



/* #405 — the Data stores browser on the Data Load admin tab.
 *
 * Craig 2026-07-28: "want a simple browser for the various databases and
 * document stores so I can see what is there."
 *
 * Every colour is a token, so the section follows the reader's theme rather
 * than pinning a palette into a surface that has a dark one. */
.store-group { margin-top: 14px; }
.store-group:first-child { margin-top: 4px; }
.store-group-name {
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-bottom: 6px;
}
.store-row {
    border: 1px solid var(--border);
    border-radius: 7px;
    margin-bottom: 6px;
    overflow: hidden;
}
.store-row.is-open { border-color: var(--text-muted); }
.store-head {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 8px 11px;
    border: 0;
    background: transparent;
    color: var(--text);
    font: inherit;
    font-size: 0.86rem;
    text-align: left;
    cursor: pointer;
}
.store-head:hover:not([disabled]) { background: var(--surface); }
.store-head[disabled] { cursor: default; opacity: 0.6; }
.store-caret { width: 14px; height: 14px; flex: 0 0 14px; }
.store-label { font-weight: 600; }
.store-count {
    margin-left: auto;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}
/* An empty store is the thing worth noticing — it is what #403 was about — so
 * it is marked, but as a WARNING not a failure: zero is frequently correct.
 * `a_refusal_is_a_warning_that_carries_its_next_step`, applied to a number.
 *
 * `--btn-caution`, which is defined in BOTH themes (styles.css:3134 light,
 * :3140 dark). The first draft used `var(--warn-text, #b45309)` — and
 * `--warn-text` does not exist anywhere, so the literal amber fallback would
 * have won every time and been wrong against the dark background. A token name
 * that is not defined is not a token; it is a hardcoded colour wearing one. */
.store-count-zero { color: var(--btn-caution); }
.store-count-err  { color: var(--conflict-text); font-weight: 500; }
.store-scope {
    flex: 0 0 auto;
    font-size: 0.74rem;
    color: var(--text-muted);
    min-width: 8.5rem;
    text-align: right;
}
.store-why {
    padding: 0 11px 9px 34px;
    font-size: 0.76rem;
    line-height: 1.45;
    color: var(--text-muted);
}
.store-rows { border-top: 1px solid var(--border); padding: 9px 11px; }
/* Wide content scrolls inside its own container — the tab must never scroll
 * sideways because one store has eight columns. */
.store-scroll { overflow-x: auto; }
.store-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.78rem;
    font-variant-numeric: tabular-nums;
}
.store-table th {
    text-align: left;
    font-weight: 600;
    color: var(--text-muted);
    padding: 3px 9px 5px;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}
.store-table td {
    padding: 4px 9px;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
    color: var(--text);
}
.store-table tr:last-child td { border-bottom: 0; }
.store-foot, .store-empty {
    margin-top: 7px;
    font-size: 0.75rem;
    color: var(--text-muted);
}

/* #677 — an authored metadata row on a version whose content is frozen. It stays
 * readable and stops inviting a click that the server can only refuse. */
.g-content.g-meta-locked { cursor: default; }
.g-content.g-meta-locked:hover { background: transparent; }

/* ══════════════════════════════════════════════════════════════════════════════
   #649 — THE ONE METADATA WINDOW (frontend/rc_meta_window.js, markup in
   index.html). Replaces both metadata drawers; invoked from every surface.

   Theme note: every colour here is a token with a light AND a dark definition
   (the tokens themselves live at the top of this file and in the N2 block).
   The two drawers this replaces both carried light-only literals, which is one
   of the things that made them read as two different products.
   ══════════════════════════════════════════════════════════════════════════════ */

/* Lifecycle-state chip colours. Derived from the state's is_effective /
   is_entry_point semantics, never from a {status: hex} map — org_lifecycle_states
   has no colour column, so a renamed state or a second effective-class state is
   covered with no code change (P8). Only the LITERALS moved here; the derivation
   stays in qms_access.js where it was. */
:root {
    --state-effective-bg: #d1fae5; --state-effective-fg: #065f46;
    --state-open-bg:      #fef3c7; --state-open-fg:      #92400e;
    --state-terminal-bg:  #e5e7eb; --state-terminal-fg:  #374151;
    --state-none-bg:      #fee2e2; --state-none-fg:      #991b1b;
}
@media (prefers-color-scheme: dark) {
    :root {
        --state-effective-bg: rgba(52,211,153,0.16); --state-effective-fg: var(--pq-green-300);
        --state-open-bg:      rgba(255,190,105,0.14); --state-open-fg: var(--pq-gold-400);
        --state-terminal-bg:  rgba(255,255,255,0.08); --state-terminal-fg: #9aa6b4;
        --state-none-bg:      rgba(239,106,90,0.14);  --state-none-fg:     #ef6a5a;
    }
}

/* Craig 2026-08-20: *"the window needs to be resizable and scrollable"*.
   `resize: both` works here because .modal-box already computes
   overflow:hidden; the field area below carries the scroll. */
/* #1937 (Craig 2026-10-06: "as I click down the options the window changes size,
   this needs to not happen") — ONE size for every section: a height, not a
   max-height, so the box never follows its content; the pane scrolls inside it. */
.rc-mw-box {
    width: min(920px, 96vw);
    height: min(720px, 88vh);
    max-height: 92vh;
    resize: both;
    min-width: 520px;
    min-height: 420px;
}
.rc-mw-banner {
    padding: 9px 16px;
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--text-muted);
    background: var(--bg-white);
    border-bottom: 1px solid var(--border);
}

/* ── #1937 · the header: the version at a glance, then ONE Next line ────────── */
/* THE HEADER CARD (Craig's look, 2026-10-06: "the header looks different") — on
   the page's own surface, not the navy modal band: number small above a bold
   title, History and the close beside them, then the shaded inset. */
.rc-mw-header {
    gap: 12px;
    align-items: flex-start;
    padding: 16px 20px 10px;
    background: var(--bg-white);
    color: var(--text);
    border-bottom: 0;
}
.rc-mw-titles { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.rc-mw-num { font-size: 0.74rem; font-weight: 600; letter-spacing: 0.02em; color: var(--text-muted); }
.rc-mw-num:empty { display: none; }
.rc-mw-header .modal-title {
    flex: none; min-width: 0;
    font-size: 1.12rem; font-weight: 700; line-height: 1.3; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rc-mw-hist { flex: none; }
/* Craig 2026-10-06: the inset box sits with the SAME gap above it as below it,
   not flush against the title band. */
.rc-mw-head {
    padding: 14px 20px 14px;
    background: var(--bg-white);
    border-bottom: 1px solid var(--border);
}
.rc-mw-inset {
    padding: 12px 14px;
    background: var(--pq-soft);   /* #1938 — the artboard's shaded box */
    border: 1px solid var(--border);
    border-radius: 10px;
}
.rc-mw-glance {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 8px 18px;
}
.rc-mw-g-k {
    font-size: 0.68rem; font-weight: 600; letter-spacing: 0.04em;
    text-transform: uppercase; color: var(--text-muted); margin-bottom: 4px;
}
.rc-mw-g-v { font-size: 0.95rem; font-weight: 600; color: var(--text); }
/* The status reads as a figure, as the design draws it: plain text in the
   state's own colour. Its owner (_renderStatusPill) paints the chip's colours
   inline, so the ground is cleared here, for this box only. */
.rc-mw-glance .rc-mw-pill {
    padding: 0; border-radius: 0;
    background: none !important;
    font-size: 0.95rem; letter-spacing: 0; text-transform: none;
}
.rc-mw-next {
    display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
    margin-top: 12px; padding-top: 12px;
    border-top: 1px solid var(--border);
    font-size: 0.86rem; color: var(--text);
}
.rc-mw-next-text { flex: 1; min-width: 12rem; }
.rc-mw-next-text:empty { display: none; }
.rc-mw-next-acts { display: flex; gap: 8px; flex-wrap: wrap; }
.rc-mw-status-sel { width: auto; min-width: 12rem; }

/* ── #1937 · the rail (reading order, no marks) and the pane (one section) ─── */
.rc-mw-body {
    flex: 1;
    min-height: 0;
    display: flex;
    background: var(--surface);
}
.rc-mw-rail {
    flex: none;
    width: 14.5rem;
    overflow-y: auto;
    padding: 10px 0;
    /* Craig 2026-10-06: the rail is the neutral ground and only the selected
       section is tinted - the inverse of a tinted rail with a white selection. */
    background: var(--bg-white);
    border-right: 1px solid var(--border);
}
.rc-mw-rail-item {
    display: block; width: 100%;
    padding: 10px 16px;
    text-align: left;
    background: transparent;
    border: 0; border-left: 3px solid transparent;
    color: var(--text);
    font: inherit;
    cursor: pointer;
}
.rc-mw-rail-item:hover { background: var(--bg); }
.rc-mw-rail-item:focus-visible { outline: 2px solid var(--btn-primary); outline-offset: -2px; }
/* Craig 2026-10-06: selection is the light blue everywhere; neutral grey elsewhere. */
.rc-mw-rail-item.is-current { border-left-color: var(--btn-primary); background: var(--primary-pale); }
.rc-mw-rail-t { display: block; font-size: 0.86rem; font-weight: 600; }
.rc-mw-rail-s {
    display: block; margin-top: 3px;
    font-size: 0.74rem; color: var(--text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rc-mw-rail-s.is-empty { font-style: italic; opacity: 0.8; }
.rc-mw-rail-sep { margin: 10px 16px; border-top: 1px solid var(--border); }
/* As printed is what the paper states, read after the record: muted, as drawn. */
.rc-mw-rail-item.is-printed:not(.is-current) .rc-mw-rail-t { color: var(--text-muted); font-weight: 500; }
/* THE PANE scrolls inside the fixed box; the shown section fills it, so its
   back/next row (.rc-mw-grp-ft) sits at the pane's foot when the section is
   short and stays pinned there when it is long. */
.rc-mw-pane {
    flex: 1; min-width: 0;
    overflow-y: auto;
    display: flex; flex-direction: column;
    background: var(--bg-white);
}
.rc-mw-pane > .rc-mw-banner { flex: none; }
.rc-mw-grp:not(.hidden) { flex: 1 0 auto; display: flex; flex-direction: column; }
.rc-mw-body.is-create .rc-mw-grp:not(.hidden) { flex: none; }
/* Create mode is one form: no rail and no header strip, every section stacked. */
.rc-mw-body.is-create .rc-mw-rail { display: none; }
.rc-mw-box:has(.rc-mw-body.is-create) .rc-mw-head { display: none; }
.rc-mw-load-fail {
    padding: 26px 18px;
    color: var(--btn-danger);
    font-size: 0.86rem;
    text-align: center;
}

/* ── D-022 sections — one shown at a time in the pane (#1937) ─────────────── */
.rc-mw-grp.hidden { display: none; }
.rc-mw-body.is-create .rc-mw-grp { border-bottom: 1px solid var(--border-subtle); }
/* The open section is tinted so "am I editing" needs no reading. */
.rc-mw-grp.is-editing { background: var(--primary-pale); }
.rc-mw-grp-hd {
    display: flex; align-items: center; gap: 8px;
    padding: 18px 22px 10px;
}
.rc-mw-grp-t { flex: 1; margin: 0; font-size: 1.05rem; font-weight: 700; color: var(--text); }
.rc-mw-grp-ft {
    display: flex; justify-content: space-between; align-items: center; gap: 8px;
    margin-top: auto;
    position: sticky; bottom: 0;
    padding: 10px 22px;
    background: inherit;
    border-top: 1px solid var(--border-subtle);
}
.rc-mw-grp:not(.is-editing) > .rc-mw-grp-ft { background: var(--bg-white); }
.rc-mw-grp-ft:empty { display: none; }
.rc-mw-grp.is-editing > .rc-mw-grp-ft { justify-content: flex-end; }

/* ── Rows ─────────────────────────────────────────────────────────────────── */
.rc-mw-f {
    display: flex; gap: 14px;
    padding: 7px 22px;
    align-items: baseline;
}
.rc-mw-f-k {
    width: 11rem; flex: none;
    font-size: 0.79rem;
    color: var(--text-muted);
}
.rc-mw-f-v {
    flex: 1; min-width: 0;
    font-size: 0.84rem;
    color: var(--text);
    word-break: break-word;
}
.rc-mw-f-v.is-empty { color: var(--text-muted); font-style: italic; }
/* #468's precedent — a read-only row carries the REASON it is read-only. An
   absent row reads as an oversight; a present row that cannot be edited, with
   the reason on it, reads as a rule. */
.rc-mw-f-why {
    display: block;
    margin-top: 2px;
    font-size: 0.7rem;
    font-style: normal;
    line-height: 1.4;
    color: var(--text-muted);
    opacity: 0.85;
}
/* #1758 — a name the field does not accept, said under the box it was typed in.
   A refusal, so the refusal colours (the same pair .rc-tell-warn uses). */
.rc-mw-f-err {
    display: block;
    margin-top: 4px;
    padding: 4px 8px;
    border-radius: 8px;
    font-size: 0.76rem;
    line-height: 1.4;
    background: var(--gap-bg);
    color: var(--gap-text);
}
.rc-mw-in[aria-invalid="true"] { border-color: var(--gap-text); }
.rc-mw-in, .rc-mw-status-sel {
    width: 100%;
    padding: 6px 8px;
    font: inherit;
    font-size: 0.84rem;
    color: var(--text);
    background: var(--bg-white);
    border: 1px solid var(--border);
    border-radius: 6px;
}
.rc-mw-in:focus { outline: 2px solid var(--btn-primary); outline-offset: -1px; }
textarea.rc-mw-in { resize: vertical; min-height: 54px; }
/* The lens trigger is a BUTTON that looks like a field, because what it opens is
   a picker rather than a text box. Left-aligned so it reads as a value. */
button.rc-mw-in { text-align: left; cursor: pointer; }

/* ── Buttons — the house standard, stated canonically at the `.btn-sem` block
   (search "N2 — semantic button tiers"). Soft outline by default, ONE filled
   primary per context. Referenced rather than restated (#758). ───────────── */
.rc-mw-btn {
    display: inline-flex; align-items: center; gap: 6px;
    font: 600 0.76rem/1 inherit;
    padding: 6px 10px;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: transparent;
    color: var(--text);
    cursor: pointer;
    text-transform: none;
    letter-spacing: 0;
}
.rc-mw-btn:hover { border-color: var(--btn-primary); color: var(--btn-primary); }
.rc-mw-btn.is-primary {
    background: var(--btn-primary);
    border-color: var(--btn-primary);
    color: #fff;
}
.rc-mw-btn.is-primary:hover { background: var(--btn-primary-h); color: #fff; }
.rc-mw-btn[disabled] { opacity: 0.45; cursor: not-allowed; }
.rc-mw-btn[disabled]:hover { border-color: var(--border); color: var(--text); }
.rc-mw-btn .ic { width: 13px; height: 13px; }

/* ── Status + signatures ──────────────────────────────────────────────────── */
.rc-mw-pill {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    vertical-align: middle;
}
/* #1590 — `.rc-mw-sig-note` REMOVED. The caution-toned box that stated the
   signing rule above the Change status control is gone on Craig's ruling; the
   control's own `title=` says it is signed and asks for a password, and the
   signing dialog says the rest at the moment it applies. `.rc-mw-sig-scope`
   below is NOT that class — muted text, no caution tokens, and it stays. */
.rc-mw-sig-scope {
    margin: 7px 22px 0;
    font-size: 0.7rem;
    color: var(--text-muted);
}
.rc-mw-signers-list { font-size: 0.8rem; }
.rc-mw-signers-acts { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
.rc-mw-signer-row   { display: flex; gap: 6px; align-items: center; padding: 3px 0; }
.rc-mw-signer-row.is-locked { opacity: 0.7; }
.rc-mw-signer-row select { flex: 1; min-width: 8rem; }
.rc-mw-signer-name  { min-width: 9rem; }
.rc-mw-signer-cap   { color: var(--text-muted); }
/* #2314 — when the signature was made and what it means, from the signature record */
.rc-mw-signer-when, .rc-mw-signer-meaning { color: var(--text-muted); font-size: .9em; }
.rc-mw-signer-note  {
    color: var(--text-muted);
    font-size: 0.85em;
    margin-top: 6px;
    padding-top: 6px;
    border-top: 1px solid var(--border-subtle);
}
.rc-mw-signed       { color: var(--state-effective-fg); font-weight: 600; }
.rc-mw-unsigned     { color: var(--state-open-fg); }
.rc-mw-signers-fail { color: var(--btn-danger); }
/* #2257 — Reject on the signer's own awaited line, and the record of each rejection. */
.rc-mw-signer-row .rc-mw-reject { margin-left: auto; padding: 1px 8px; }
.rc-mw-signer-row .rc-mw-reject:hover { border-color: var(--btn-danger); color: var(--btn-danger); }
.rc-mw-rejection    { padding: 2px 0; }
.rc-mw-rejected     { color: var(--btn-danger); font-weight: 600; }
.rc-mw-rejection-why { color: var(--text); }
/* #2269 — the back-review's lines: the document, then the term and where it was found. */
.qma-tax-backreview-row  { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.qma-tax-backreview-doc  { min-width: 14rem; flex: 1 1 14rem; color: var(--text); }
.qma-tax-backreview-why  { color: var(--text-muted); font-size: 0.85em; flex: 2 1 12rem; }
/* #2258 — the reviewers, apart from the signature block: a record, not a signature. */
.rc-mw-reviews-t    { font-weight: 600; color: var(--text); margin-bottom: 2px; }
.rc-mw-review-row   { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; padding: 2px 0; }
.rc-mw-review-row .rc-mw-review { margin-left: auto; padding: 1px 8px; }

/* ── Footer ───────────────────────────────────────────────────────────────── */
.rc-mw-foot {
    display: flex; align-items: center; justify-content: flex-end; gap: 10px;
    padding: 10px 16px;
    border-top: 1px solid var(--border);
    background: var(--bg-white);
}
/* The window closes from its header (#1937, Craig's look): the foot exists only
   in create mode, for Cancel and Create. */
.rc-mw-foot:empty { display: none; }
.rc-mw-foot-acts { display: flex; gap: 8px; flex: none; }

/* ── #108 · The notification bell and panel ────────────────────────────────
   Every colour here is a token, so both modes and any future palette move come
   for free — the three severities read off `--pq-*` semantic roles rather than
   naming a hex, and the ONE hardcoded value in this block is the badge's text
   colour, which is white because it sits on a filled danger chip in both modes.
   The bell lives in the header, which has a dark ground in BOTH modes (it is
   `--primary`), so its ink is the header's own foreground, not the page's. */
.notif-bell-btn {
    position: relative;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 6px;
    color: inherit;
    cursor: pointer;
    padding: 5px 7px;
    display: inline-flex;
    align-items: center;
    line-height: 1;
}
.notif-bell-btn:hover { border-color: currentColor; }
.notif-bell-btn .ic { width: 18px; height: 18px; }
/* A critical item outstanding tips the bell itself, so the state is legible
   without reading the number — but the number stays the authority. */
.notif-bell-btn.notif-has-critical { color: var(--pq-danger); }
.notif-badge {
    position: absolute;
    top: -2px; right: -3px;
    min-width: 16px;
    padding: 0 4px;
    border-radius: 9px;
    background: var(--pq-danger);
    color: var(--pq-on-danger);   /* #1880 — white on the dark-mode coral was 2.54 */
    font-size: 10px;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
}

.notif-panel {
    position: fixed;
    z-index: 9200;
    width: min(420px, 94vw);
    display: flex;
    flex-direction: column;
    background: var(--pq-elevated);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.22);
    overflow: hidden;
}
.notif-panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 9px 12px;
    border-bottom: 1px solid var(--border);
    font-weight: 600;
    font-size: 13px;
}
.notif-panel-head .ic { width: 15px; height: 15px; vertical-align: -2px; }


/* The body owns its own scrolling. RCFloating caps the panel's height and only
   takes over overflow when the stylesheet has not decided — it has, here, so
   the head stays put and one gesture scrolls one thing. */
.notif-panel-body { overflow-y: auto; max-height: 62vh; }

.notif-row {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    padding: 10px 12px;
    border-bottom: 1px solid var(--border-subtle);
}
.notif-row:last-child { border-bottom: 0; }
.notif-dot {
    flex: 0 0 auto;
    width: 8px; height: 8px;
    margin-top: 5px;
    border-radius: 50%;
    background: var(--text-muted);
}
.notif-sev-warn     .notif-dot { background: var(--pq-warn); }
.notif-sev-critical .notif-dot { background: var(--pq-danger); }
.notif-main { flex: 1 1 auto; min-width: 0; }
.notif-title { font-size: 13px; font-weight: 600; }
.notif-sev-critical .notif-title { color: var(--pq-danger); }
.notif-body {
    font-size: 12px;
    color: var(--text-muted);
    margin-top: 2px;
    overflow-wrap: anywhere;
}
.notif-meta { font-size: 11px; color: var(--text-muted); margin-top: 3px; }
/* A dismissed row that is still on screen (include_acknowledged) reads as past
   tense rather than disappearing mid-list under the reader's eye. */
.notif-acked { opacity: 0.55; }
/* #1754 (#1539) — a row is a way in: it opens its audit event. */
.notif-row[role="button"] { cursor: pointer; }
.notif-row[role="button"]:hover, .notif-row[role="button"]:focus-visible { background: var(--bg-hover); outline: none; }
.notif-row[role="button"]:focus-visible { box-shadow: inset 0 0 0 2px var(--accent); }
.notif-go { flex: none; align-self: center; color: var(--text-muted); width: 16px; height: 16px; }
.notif-seen .notif-title { font-weight: 500; }
/* the audit line: an open alert, and the one place it is cleared */
.audit-open-alert { color: var(--accent); margin-right: 4px; }
.audit-open-alert svg.ic { width: 13px; height: 13px; vertical-align: -2px; }
.audit-open-alerts { display: flex; align-items: center; gap: 6px; font-size: 0.82rem; height: 32px; cursor: pointer; }
.audit-alert-clear { display: flex; align-items: center; justify-content: space-between; gap: 12px;
    margin-top: 12px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-subtle); }
/* #1754 — the "so what" at the head of the audit event detail. Theme tokens only. */
.audit-meaning { margin-bottom: 12px; padding: 10px 12px; border-left: 3px solid var(--accent);
    border-radius: var(--radius-sm); background: var(--bg-subtle); }
.audit-meaning-hd { font-size: 0.78rem; font-weight: 600; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: .04em; margin-bottom: 4px; }
.audit-meaning p { margin: 0; font-size: 0.9rem; line-height: 1.45; color: var(--text); }


.notif-empty {
    padding: 18px 14px;
    text-align: center;
    font-size: 12px;
    color: var(--text-muted);
}
/* "We could not read it" is not "there is nothing" — it is coloured as the
   warning it is, so an empty panel cannot be mistaken for a quiet one. */
.notif-empty-err { color: var(--pq-warn); }
/* end #108 notifications */

/* ── #709 · Previous versions, inside the metadata window ──────────────────
   Craig asked for "a popup list with fixed width cols ... with version and
   dates". FIXED is load-bearing, not cosmetic: a history list is read by
   scanning DOWN a column ("which one was effective in March"), and ragged
   columns defeat exactly that. `table-layout: fixed` is what holds the widths
   when one state name is longer than its neighbours. Every colour is a token,
   so both modes follow the palette. */
.rc-mw-hwrap { padding: 4px 2px 10px; }
.rc-mw-hnote {
    font-size: 12px;
    color: var(--text-muted);
    margin: 0 0 10px;
    line-height: 1.5;
}
.rc-mw-herr { color: var(--pq-warn); font-size: 12.5px; line-height: 1.6; }
.rc-mw-history {
    width: 100%;
    table-layout: fixed;
    border-collapse: collapse;
    font-size: 12.5px;
}
.rc-mw-history th {
    text-align: left;
    font-weight: 600;
    color: var(--text-muted);
    font-size: 11.5px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 0 8px 6px 0;
    border-bottom: 1px solid var(--border);
}
.rc-mw-history td {
    padding: 8px 8px 8px 0;
    border-bottom: 1px solid var(--border-subtle);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rc-mw-history th:nth-child(1), .rc-mw-history td:nth-child(1) { width: 24%; }
.rc-mw-history th:nth-child(2), .rc-mw-history td:nth-child(2) { width: 30%; }
.rc-mw-history th:nth-child(3), .rc-mw-history td:nth-child(3) { width: 23%; }
.rc-mw-history th:nth-child(4), .rc-mw-history td:nth-child(4) { width: 23%; }
.rc-mw-hrow { cursor: pointer; }
.rc-mw-hrow:hover td, .rc-mw-hrow:focus td {
    background: var(--hover-bg);
    outline: none;
}
.rc-mw-hrow:focus-visible td { box-shadow: inset 0 0 0 2px var(--primary-fg); }
/* end #709 previous versions */

/* ── #789 §13.1.3 — a citation with no retrieved regulation text ────────────
 *
 * A WARNING, not an error. The system is healthy and applied a rule: the
 * analysis ran, the citation is real, and no corpus text was retrieved to back
 * it. Painting it red would put it beside genuine failures, and when everything
 * is red, red carries no information.
 *
 * `--btn-caution` IS THE TOKEN, and this file records why twice. My first draft
 * used `var(--warn-border, ...)`, `var(--warn-bg, ...)` and `var(--warn-text,
 * ...)` — and **none of those three is defined anywhere**, so every literal
 * fallback would have won, giving a hardcoded amber that is wrong against the
 * dark background. In this file's own words: *a token name that is not defined
 * is not a token; it is a hardcoded colour wearing one.*
 *
 * `--btn-caution` and `--btn-caution-bg` are BOTH declared in BOTH themes
 * (:3506 light, :3512 dark), so the colour follows the user's setting rather
 * than a guess. Using the existing PAIR rather than deriving a background with
 * `color-mix` — a derived value would be a second definition of the same fact,
 * and the dark theme already tunes its own (`rgba(255,190,105,0.12)`) rather
 * than taking a flat percentage of the foreground.
 */
.card-ungrounded {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 6px;
    border: 1px solid var(--btn-caution);
    /* #1164 — 8px, the standard corner. This shipped at 10px with #789 and is
       the single value that took the ratchet from 541 to 542.
       THE PILL (999px) WAS CONSIDERED AND NOT CHOSEN. It is explicitly
       legitimate for a chip, and the sibling precedent is genuinely split —
       `.fwp-badge` is 999px, `.cl-scope-badge` and `.domain-builtin-badge` are
       3px, `.rc-reason-chip` is 10px, and the finding chip is 3px (it was a
       20px `.stat-pill` capsule until #1653). With no consistent
       answer in the estate, following the declared standard is defensible and
       picking a shape is taste. If Craig wants it round it is one value. */
    border-radius: 8px;
    background: var(--btn-caution-bg);
    color: var(--btn-caution);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    white-space: nowrap;
    cursor: help;
    vertical-align: middle;
}

               
#maintenance-close .ic { width: 17px; height: 17px; }

/* ── #1498 — the permission switch row ───────────────────────────────────────
   Craig, 2026-09-04, testing the user window: "the checkbox above the item is
   inherrently ambiguous (which line does it belong to)".

   THE CAUSE WAS A CLASS THAT DOES NOT EXIST HERE. The window was built from
   `frontend/user-creation-sandbox.html`, whose `.sw` rule lives in that file's
   own <style> block. The markup was ported and the rule was not, so the app had
   no `.sw` at all and the boxes fell to their own lines — a box on its own line
   binds to nothing, on a form whose entire content is permissions.

   That is the general trap in sandbox-to-production porting: the markup is what
   you copy and the stylesheet is what you forget, and the result looks like a
   design decision rather than a missing rule.

   `align-items:start` rather than `center`: the label is one line and the hint
   below it is often two, so centring floats the box down beside the wrong text. */
.sw {
    display: flex;
    gap: 9px;
    align-items: start;
    margin-bottom: 12px;
}
.sw > input[type="checkbox"] {
    flex: 0 0 auto;
    margin: 2px 0 0;          /* optical alignment with the label's cap height */
}
.sw .t { font-weight: 600; line-height: 1.35; }
.sw .d { color: var(--text-muted); font-size: 0.85rem; line-height: 1.4; margin-top: 2px; }
/* A refused control keeps its reason legible rather than fading it out with the
   control — the reason is the one thing the administrator still needs to read. */
.sw > input[type="checkbox"]:disabled + div .t { opacity: 0.55; }

/* #1498 — a refusal reads BELOW its control, never inside the label.
   It was appended to the label's own line ("New password That account holds
   more authority than yours. Ask a system..."), so label and reason ran into
   one sentence and wrapped four lines deep inside a grid column. Same slot as
   the hint that appears when the control is allowed, so the eye finds the
   explanation in one place either way. */
/* #1791 — a save the server refused: the reason under its field and the status
   line, in the refusal colour (yellow family), not the muted permission note. */
/* #1864 — the line's resting look lives HERE, not inline: an inline colour beat
   the refusal rule below, so a refusal rendered grey (test pass 2026-10-01). */
#user-window-status { font-size: 0.8rem; color: var(--text-muted); }
#user-window-status.save-refused,
.uw-refusal[data-refused-for] { color: var(--gap-text); }
.uw-refusal {
    color: var(--text-muted);
    font-size: 0.85rem;
    line-height: 1.4;
    margin-top: 4px;
}

/* ══ #1505 — the sandbox's look and feel, ported into the two Settings windows ══
   Craig, 2026-09-04, after the walkthrough passed on function: "the interface
   works but does not match the sandbox you set up which looked mutch better.
   Better fonts, look and feel spacing."

   SAME FAULT AS #1498, one level up. Both windows were built from
   `frontend/user-creation-sandbox.html` by porting its MARKUP; its <style> block
   never came with them. The missing `.sw` rule was the first symptom. This is
   the rest — type scale, field rhythm, label weight, input sizing.

   The general trap, worth naming because it will recur: WHEN YOU PORT A SANDBOX,
   THE MARKUP IS WHAT YOU COPY AND THE STYLESHEET IS WHAT YOU FORGET. The result
   does not look broken, it looks like a plainer deliberate design, so nobody
   reports it until the two are side by side.

   SCOPED BY ID, and that is deliberate rather than defensive. `.auth-label`,
   `.org-input` and `.opt-hint` are used across the whole application — the login
   screen, the organisation panels, the admin tabs. Restyling them globally would
   change surfaces nobody has looked at in order to fix two windows, which is how
   a tidy becomes a regression. These rules reach exactly the two windows Craig
   was looking at, and no further.

   PORTED AGAINST THE APP'S OWN TOKENS, never the sandbox's literals. The sandbox
   hardcoded #7a8699 and 6px because it had no tokens to reach for; this file
   has both, and re-introducing literals would trip the colour and corner
   ratchets (#743) and re-open the drift they exist to stop. So: var(--text),
   var(--text-muted), var(--border), and the standard 8px corner. */

#user-window .modal-body,
#invite-window .modal-body {
    font-size: 0.86rem;
    line-height: 1.5;
}

/* THE LABEL. Production carried `margin-bottom: -4px` — a negative gap that
   pulled every label onto its field and is most of why the form read as
   cramped. The sandbox sits the label 3px clear of its input, and colours it
   with the body text rather than the muted grey: a field's name is not
   secondary information, it is what tells you what you are typing. */
#user-window .auth-label,
#invite-window .auth-label {
    display: block;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text);
    margin-bottom: 3px;
}

/* THE INPUT. Roomier than the app default (5px/8px) and on the standard 8px
   corner rather than the 4px it carried, so these fields match every other
   surface built since #743 instead of quietly predating it. */
#user-window .org-input,
#invite-window .org-input {
    padding: 7px 9px;
    font-size: 0.83rem;
    border-radius: 8px;
}
/* A DISABLED FIELD IS RECESSED TO THE PAGE GROUND, in var(--bg), and the token
   choice is the whole point rather than a detail. The first draft of this rule
   reached for var(--bg-subtle), which resolves to var(--pq-blue-050) = #e2f1fb
   and had NO dark twin in this file until #1938 — so in dark mode it would have
   painted a light-blue box carrying var(--text-muted) text. That is the exact
   fault recorded eight lines above, where .org-input's own dark rule exists
   because --input-bg was undefined and gave light-on-white (Craig D8). Writing
   it a second time, directly beneath its own post-mortem, is why this comment
   names the token rather than the symptom. var(--bg) is var(--pq-ground), which
   IS themed — #e8f7f2-ish in light and #111721 in dark — so one rule recesses
   the field correctly in both schemes with no media query to keep in step. */
#user-window .org-input:disabled,
#invite-window .org-input:disabled {
    background: var(--bg);
    color: var(--text-muted);
}

/* THE HINT had NO RULE ANYWHERE in the application — `.opt-hint` is written in
   the markup and styled by nothing, so hints rendered at full body size in full
   body colour and competed with the labels they sit under. That is the single
   biggest reason the form read as a wall of text. */
#user-window .opt-hint,
#invite-window .opt-hint {
    font-size: 0.73rem;
    line-height: 1.45;
    color: var(--text-muted);
    margin-top: 3px;
}

/* FIELD RHYTHM. One rule for the gap between fields, so a new field inherits
   the spacing instead of carrying its own inline margin — five inline
   `margin-bottom:14px` declarations were five places for it to drift. */
#user-window .modal-body > div,
#invite-window .modal-body > div {
    margin-bottom: 13px;
}
#user-window .modal-body > div:last-child,
#invite-window .modal-body > div:last-child {
    margin-bottom: 0;
}
#user-window .user-edit-grid,
#invite-window .user-edit-grid {
    gap: 12px;
    margin-bottom: 13px;
}

/* THE SWITCH BLOCK, set off from the fields above it. The divider was an inline
   `border-top` on a bare div; as a rule it applies wherever the block appears
   and cannot be forgotten by the next window that needs one. */
#user-window .sw,
#invite-window .sw {
    padding: 5px 0;
    margin-bottom: 4px;
}
#user-window .sw .t,
#invite-window .sw .t {
    font-size: 0.8rem;
}
#user-window .sw .d,
#invite-window .sw .d {
    font-size: 0.73rem;
}

/* ═══════════════════════════════════════════════════════════════════════════
   #1628 / QMS-DS-001 §10.22 — THE USAGE GLANCE VIEW.

   Craig asked for "a fixed size and cleaner, more empty look and feel". Empty
   is done with SPACE and hairlines, not with boxes: there is not a single
   panel, card border or filled background below. Every colour is a token, so
   the whole thing follows prefers-color-scheme without a dark-mode block —
   hardcoding a palette here is the one thing this view must not do.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The plan, beside the title rather than in the body — the screenshot's badge
   sits top right and it is context, not content. margin-left:auto pushes it to
   the far side of the header; the close button follows it. */
.usage-plan-badge {
    margin-left: auto;
    margin-right: 10px;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 3px 10px;
    white-space: nowrap;
}
.usage-plan-badge:empty { display: none; }   /* no plan, no empty pill */

/* THE HEADLINE. Large type is the whole design idea of the screenshot Craig
   sent — the sentence is the page, and the numbers underneath support it. */
.usage-headline {
    font-size: 1.12rem;
    line-height: 1.55;
    color: var(--text);
    margin: 0 0 6px;
}
.usage-subline {
    font-size: 0.78rem;
    color: var(--text-muted);
    margin: 0 0 22px;
}

/* Label left, value right, hairline between — the density idiom from the
   screenshot, and the reason nothing here needs a box to look organised. */
.usage-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    padding: 9px 0;
    border-bottom: 1px solid var(--border);
    font-size: 0.87rem;
}
.usage-row:last-child { border-bottom: none; }
.usage-row .usage-k { color: var(--text-muted); }
.usage-row .usage-v { font-weight: 600; color: var(--text); }
.usage-row .usage-note {
    font-weight: 400;
    font-size: 0.76rem;
    color: var(--text-muted);
    margin-left: 8px;
}

.usage-section {
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin: 26px 0 8px;
}
.usage-section:first-child { margin-top: 0; }

/* THIN BARS, per §10.22.2.3 and the screenshot. 6px: present enough to read at
   a glance, quiet enough not to become the loudest thing on a calm page. */
.usage-bar-track {
    height: 6px;
    border-radius: 3px;
    background: var(--border);
    overflow: hidden;
    margin: 6px 0 2px;
}
.usage-bar-fill {
    height: 100%;
    border-radius: 3px;
    background: var(--primary);
}
/* Nearly gone is worth SEEING before you read the number. Not red — this is a
   warning, not a failure, and the refusal/failure/note colour rule applies to
   a progress bar exactly as it does to a message. */
.usage-bar-fill.usage-bar-low { background: var(--pq-gold-400); }

.usage-bar-meta {
    display: flex;
    justify-content: space-between;
    font-size: 0.74rem;
    color: var(--text-muted);
}

/* The batch table. Borderless except for hairline row rules, numbers right so a
   column of them lines up down the page — the same call #1618 made on the
   Wallet tab, kept identical here on purpose. */
.usage-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.82rem;
}
.usage-table th {
    text-align: left;
    font-weight: 600;
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
    padding: 0 10px 6px 0;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}
.usage-table td {
    padding: 8px 10px 8px 0;
    border-bottom: 1px solid var(--border);
    color: var(--text);
}
.usage-table tr:last-child td { border-bottom: none; }
.usage-table .num { text-align: right; }
.usage-table .when { white-space: nowrap; color: var(--text-muted); }

.usage-empty {
    font-size: 0.85rem;
    color: var(--text-muted);
    padding: 10px 0;
}

@media (prefers-reduced-motion: no-preference) {
    .usage-bar-fill { transition: width 220ms ease; }
}


/* ── #1665 — RESTORED AFTER MY OWN MERGE DESTROYED THEM ──────
   The duplicate-collapsing pass for this item used a brace parser
   that desyncs on this stylesheet: measured, it cannot see
   `.pg-bpmn-gutter` as a rule at all, so a later span swallowed it
   and deleting a duplicate's block deleted these six with it.

   Restored verbatim from the committed file. Appended rather than
   slotted back where they were: their neighbourhoods have moved, and
   re-inserting by guessed position is how a second defect is
   introduced while fixing the first. Position is only load-bearing
   against an equal-specificity competitor, and none of these has one
   — duplicates in this file are now zero, which is this item's whole
   point.

   #1846 — two of the six, `.pg-bpmn-gutter` and its inner column, are gone:
   they styled the hand-built view's lane gutter, which nothing builds since
   bpmn_view.js was burned. The four figure rules below remain. */

.g-content.g-figure { padding: 10px 12px; }

.g-figure-cap {
    margin-top: 6px;
    font-size: 0.8rem;
    line-height: 1.45;
    color: var(--text-muted);
}

.g-figure-img {
    display: block;
    max-width: 100%;
    height: auto;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--bg-white);
}

.g-figure-label {
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-bottom: 6px;
}

/* #1540 — the account lifecycle banner and the organisation-card pill. A warning,
   not an error: nothing is broken. Built from the state tokens, which already
   carry their dark values and honour the theme override, so there is no local
   dark block to drift (#1703: the first version read tokens that did not exist). */
.rc-account-banner {
    padding: 8px 16px;
    font-size: 0.85rem;
    background: var(--state-open-bg);
    color: var(--state-open-fg);
    border-bottom: 1px solid var(--pq-warn);
}
.org-pill-account { background: var(--state-open-bg); color: var(--state-open-fg); }
.org-pill-account[data-state="archived"] { background: var(--surface-2); color: var(--text-secondary); }

/* #1735 — the expired-trial screen and the banner's way back to it. Tokens only,
   so light and dark both follow the user's setting. */
.rc-account-banner-btn { margin-left: 12px; padding: 2px 10px; font-size: 0.8rem; }
.trial-expired-body p { margin: 0 0 10px; line-height: 1.45; }
.trial-expired-btns { display: flex; gap: 8px; flex-shrink: 0; flex-wrap: wrap; }
.trial-expired-progress { margin-top: 14px; }
.trial-expired-progress-bar {
    height: 8px;
    background: var(--border-color);
    border-radius: 4px;
    overflow: hidden;
}
.trial-expired-progress-fill {
    height: 100%;
    width: 0%;
    background: var(--accent);
    transition: width 0.35s ease;
}
.trial-expired-progress-text {
    margin-top: 6px;
    font-size: 0.85rem;
    color: var(--text-secondary);
    overflow-wrap: anywhere;
}
/* #1735 — the plan chooser on the expired-trial screen. Tokens only. */
.trial-expired-plans { margin-top: 12px; }
.trial-expired-plan-list { display: flex; flex-direction: column; gap: 6px; }
.trial-expired-plan {
    display: flex; align-items: center; gap: 10px; cursor: pointer;
    padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius-md);
    background: var(--bg-white); color: var(--text);
}
.trial-expired-plan:has(input:checked) { border-color: var(--primary-fg); }
.trial-expired-plan-name { font-weight: 600; flex: 1; }
.trial-expired-plan-price { color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.trial-expired-plan-actions { display: flex; align-items: center; gap: 10px; margin-top: 10px; flex-wrap: wrap; }


/* ── #1700 — ONE CLOSE BUTTON ────────────────────────────────────────────────
   Craig 2026-09-20: "a complete review of the UI, the close buttons etc are still
   inconsistent". There were about 29 dismissal classes and the same glyph drew at
   nine sizes; on a Mac some windows showed the red circle and others a cross in
   the same session. Now every close is `.rc-close`, and nothing else styles one.

   ITS COLOUR IS INHERITED, deliberately: a close sits on a navy modal header, a
   light panel, a dark chat header or a banner, and `currentColor` takes each
   ground's own text colour, so one rule reads on all of them in light and dark.
   A soft-outlined square, never a bare glyph: WT-5 was Craig unable to find the
   way out of a drawer whose close read as decoration.

   `.rc-close-inline` is a dismissal INSIDE content (a banner, one notification,
   the floating document close) rather than a window's own close, so it never
   takes the operating system's window chrome.

   The window-control group (.rc-win-*, #1968: the expert-review and document windows) is a window-control
   GROUP with its own per-OS layout and is deliberately not this. */
.rc-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 24px;
    height: 24px;
    padding: 0;
    margin-left: auto;
    border: 1px solid color-mix(in srgb, currentColor 28%, transparent);
    border-radius: 5px;
    background: transparent;
    color: inherit;
    opacity: 0.8;
    cursor: pointer;
    line-height: 1;
}
.rc-close .ic { width: var(--icon-md); height: var(--icon-md); }
.rc-close:hover { opacity: 1; background: color-mix(in srgb, currentColor 14%, transparent); }
.rc-close:focus-visible { opacity: 1; outline: 2px solid currentColor; outline-offset: 1px; }

/* macOS: a window's close is the red circle on the LEFT of its header, and shows
   its cross on hover as the OS does. Absolutely placed closes keep their place. */
body.os-mac .rc-close:not(.rc-close-inline) {
    order: -1;
    width: 13px;
    height: 13px;
    margin-left: 0;
    margin-right: 8px;
    border: none;
    border-radius: 50%;
    background: #ff5f57;
    opacity: 1;
}
body.os-mac .rc-close:not(.rc-close-inline) .ic { width: 70%; height: 70%; color: rgba(0, 0, 0, 0.55); visibility: hidden; }
body.os-mac .rc-close:not(.rc-close-inline):hover .ic,
body.os-mac .rc-close:not(.rc-close-inline):focus-visible .ic { visibility: visible; }
body.os-mac .rc-close:not(.rc-close-inline):hover { background: #ff5f57; filter: brightness(0.92); }
/* #1700 — THE TARGET IS BIGGER THAN THE DOT. A 13px dot was the whole clickable
   area, under the 24px minimum (WCAG 2.2, 2.5.8): a click 8px off centre landed
   on the header (measured on v4.3.1280). An invisible ring 6px out makes the
   target 25px while the dot stays 13px. The positioning it needs carries NO
   specificity (:where), so any close placed absolutely keeps its place. */
:where(body.os-mac .rc-close:not(.rc-close-inline)) { position: relative; }
body.os-mac .rc-close:not(.rc-close-inline)::before {
    content: ''; position: absolute; inset: -6px; border-radius: 50%;
}

/* WHERE three closes sit, and nothing about how they look. */
.diag-banner > .rc-close,
.warn-banner > .rc-close { position: absolute; top: 8px; right: 10px; }
#maintenance-close { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); }
/* #1968 — the document close that floated over the card (#1773) is GONE: it is
   the red control on the document window's title bar, directly above the page. */


/* ── #1701 — MODAL SIZES ARE CLASSES ────────────────────────────────────────
   28 of 30 modals set their own size inline: 16 widths and 6 heights, each
   hand-picked when the modal was written, so no two windows agreed. Four widths
   now, and one fixed height for windows whose content changes while they are
   open (Usage, the QE chat) so they do not resize under the reader. */
.modal-box.modal-sm { width: min(460px, 94vw); }
.modal-box.modal-md { width: min(600px, 94vw); }
.modal-box.modal-lg { width: min(760px, 95vw); }
.modal-box.modal-xl { width: min(1200px, 96vw); }
.modal-box.modal-h-fixed { height: min(640px, 90vh); max-height: 90vh; }

/* ── #1701 — ONE ACTION ROW ─────────────────────────────────────────────────
   .modal-footer was named three times and styled nowhere; 27 dialogs put their
   buttons in an inline-styled div, and those rows disagreed on gap, margin and
   alignment. Cancel left, confirm right — the one axis that was already
   consistent — is the order inside it. */
.modal-footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 18px;
    flex-wrap: nowrap;
}
/* A second line under the title, on the header's own ground — so it takes the
   header's colour and is only quieter, never a muted grey that vanishes on it. */
.modal-header .modal-subtitle { font-size: 0.78rem; opacity: 0.85; }
.modal-footer-split { justify-content: space-between; }       /* a note on the left */
.modal-footer-bar {                                           /* the window's bottom edge */
    margin-top: 0;
    padding: 12px 24px;
    border-top: 1px solid var(--border);
}

/* ═══ #1903 — THE PERSON'S SETTINGS: theme-aware colour for what the code review
   found light-only (AJ-C4, FE-F17, FE-F35, FE-F42, FE-F43). Every colour here is a
   token or light-dark(); nothing is drawn for one scheme only. ═══ */

/* FE-F17 — Level 1's palette in dark (the light half is on .pg-level1 itself);
   never under .rc-export-light, so an exported image is always light (#304). */
@media (prefers-color-scheme: dark) {
    .pg-level1:not(.rc-export-light) {
        --rc-l1-bg: #0f1c2b; --rc-l1-card: #182838; --rc-l1-line: #2b3d52;
        --rc-l1-ink: #e7eef6; --rc-l1-eyebrow: #89c0f5;
    }
}

/* #2413 — the Time cell wraps BETWEEN its date and its time, each kept whole
   (app.js _auditRowHtml draws them as two spans). AJ-C5 kept the whole timestamp
   on one line, and "10/25/2026, 03:34:08 PM" is wider than its column, so it ran
   under the Event name (Craig 2026-10-25, v4.3.2004) - #720's defect again. The
   column-1 rule's mid-word breaking is turned off here: a date breaks only at
   its space. */
.audit-table td.audit-ts-cell { word-break: normal; overflow-wrap: normal; }
.audit-ts-cell > span { white-space: nowrap; }

/* FE-F35 — the halo's ring of round handles (was a stylesheet injected by
   rc_halo.js with its own light hexes and a dark twin). */
.rc-halo-ring { display: flex; gap: 6px; padding: 6px; align-items: center; }
.rc-halo-handle {
    width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--border-color);
    background-color: var(--surface); background-repeat: no-repeat; background-position: center;
    background-size: 24px 24px; cursor: pointer; box-shadow: 0 1px 3px rgba(0, 0, 0, .18);
}
.rc-halo-handle:hover { background-color: var(--bg-hover); border-color: var(--text-muted); }

/* ══ #1746 · THE TAXONOMY VIEWS — the sphere and Findings Intelligence ═══════
   kg_views.js. Craig 2026-09-26: "the sort of thing a statistician, data analyst
   and quality person would feel comfortable using" — so quiet, dense and exact:
   tabular figures, real axes with units, a legend naming what colour and size
   mean, a table under every chart, no chart junk.

   THE CATEGORY PALETTE (--kg-cat-1..8) is the dataviz reference palette, RUN
   THROUGH ITS VALIDATOR against THIS app's two surfaces (2026-09-26): light on
   #ffffff — every adjacent check passes (worst CVD dE 9.1, normal-vision 19.6);
   three slots sit under 3:1 contrast, so identity never rests on colour alone:
   the legend names every colour, the hover names the cluster and the table
   carries it as a column. Dark on #192536 — every check passes, contrast
   included. Clusters past the eighth fold into --kg-other, never a ninth hue.
   Every drawing colour is a token here; kg_views.js reads them and names any
   that is missing. Three theme states, as the --pq-* ramp does. */
:root {
    --kg-cat-1: #2a78d6; --kg-cat-2: #eb6834; --kg-cat-3: #1baf7a; --kg-cat-4: #eda100;
    --kg-cat-5: #e87ba4; --kg-cat-6: #008300; --kg-cat-7: #4a3aa7; --kg-cat-8: #e34948;
    --kg-other: #9aa0a6;
    --kg-link: #94a3b8;
    --kg-mapped: #c4c8ce;
    --kg-surface: var(--card);
    /* #1746 — the panel's quiet grounds, mixed from tokens that flip with the colour
       scheme. --bg-subtle and --hover-bg are a fixed pale blue in both, which drew
       white bands under the dark theme's light text (measured on v4.3.1273). */
    --kg-subtle: color-mix(in oklab, var(--pq-ink) 5%, var(--pq-surface));
    --kg-hover: var(--hover-bg);   /* #1952/#1938 — hover is neutral; selection is --primary-pale */
    --kg-ink: var(--text);
    --kg-line: var(--border);
    --kg-grid: var(--border);
    --kg-select: var(--pq-accent);
    --kg-role: var(--kg-cat-1);
    --kg-prov: var(--kg-cat-2);
    --kg-area: var(--kg-cat-3);
    --kg-section: var(--kg-cat-7);
    --kg-group: var(--kg-cat-4);   /* #1763 — document groups in the sphere */
    --kg-doc: var(--kg-other);
    --kg-shadow: 0 1px 2px rgba(16, 24, 40, 0.06), 0 4px 14px rgba(16, 24, 40, 0.08);
    --kg-warn-bg: #fdf6e7;
    --kg-warn-ink: var(--pq-warn);
    --kg-fail-bg: #fbeeea;
}
@media (prefers-color-scheme: dark) {
    :root {
        --kg-cat-1: #3987e5; --kg-cat-2: #d95926; --kg-cat-3: #199e70; --kg-cat-4: #c98500;
        --kg-cat-5: #d55181; --kg-cat-6: #008300; --kg-cat-7: #9085e9; --kg-cat-8: #e66767;
        --kg-other: var(--pq-grey-500);
        --kg-link: #64748b;
        --kg-mapped: #4b5563;
        --kg-shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 6px 20px rgba(0, 0, 0, 0.45);
        --kg-warn-bg: rgba(255, 190, 105, 0.12);
        --kg-warn-ink: var(--pq-gold-400);
        --kg-fail-bg: rgba(239, 106, 90, 0.14);
    }
}

.qma-tool-panel.kg-tool { padding: 14px 16px; }
.kg-page { display: flex; flex-direction: column; gap: var(--space-md); color: var(--text); }
.kg-page .qma-tool-header { margin-bottom: 0; flex-wrap: wrap; }
.kg-head-note { font-weight: 400; font-size: var(--font-size-sm); }
.kg-num { font-variant-numeric: tabular-nums; }
.kg-muted { color: var(--text-muted); }
.kg-views-ctl { margin-left: auto; display: inline-flex; gap: var(--space-xs); align-items: center; flex-wrap: wrap; font-weight: 400; }
.kg-toolbar { display: flex; flex-wrap: wrap; gap: var(--space-sm); align-items: center; }
.kg-input {
    font: inherit; font-size: var(--font-size-md); padding: 5px 8px; border: 1px solid var(--border);
    border-radius: var(--radius-md); background: var(--input-bg); color: var(--text);
}
.kg-find { min-width: 280px; }
.kg-count { margin-left: auto; color: var(--text-muted); font-size: var(--font-size-sm); }
.kg-check { display: inline-flex; gap: var(--space-xs); align-items: center; font-size: var(--font-size-sm); color: var(--text); }
.kg-check.is-ignored, .kg-dd.is-ignored { opacity: 0.55; }
.kg-page kbd, .kg-panel kbd, .rc-halo-kbd {
    font: 600 0.7rem/1 var(--mono); border: 1px solid var(--border); border-bottom-width: 2px;
    border-radius: var(--radius-xs); padding: 1px 4px; color: var(--text-muted); background: var(--surface);
}
.kg-card { background: var(--kg-surface); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--kg-shadow); }
.kg-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(300px, 380px); gap: var(--space-lg); align-items: start; }
.kg-stage-card, .kg-chart-card { display: flex; flex-direction: column; min-width: 0; }
.kg-stage-wrap { position: relative; }
.kg-stage { position: relative; width: 100%; background: var(--kg-surface); outline: none; }
.kg-stage:focus-visible { box-shadow: inset 0 0 0 2px var(--kg-select); }
.kg-sphere-stage { height: max(460px, calc(100vh - 340px)); border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
/* #1841 — how the sphere's other connections are drawn: a labelled group; the
   chosen one is PRESSED. */
.kg-linkmode { display: inline-flex; flex-wrap: wrap; gap: var(--space-xs); align-items: center; font-size: var(--font-size-sm); }
.kg-linkmode .btn-sem[aria-pressed="true"] { border-color: var(--accent); background: var(--bg-hover); color: var(--text); }
.kg-bundle-layer { position: absolute; left: 0; top: 0; pointer-events: none; }
.kg-cloud-stage { height: 460px; }
.kg-map-wrap { height: 460px; background: var(--kg-surface); }
.kg-map-stage { position: absolute; width: auto; height: auto; background: transparent; }   /* the grid shows through */
/* #1746 — THE SPHERE DID NOT ANSWER THE MOUSE because of this overlay. `display: flex`
   beats the `hidden` attribute (no author rule restored it), so the empty message box
   stayed on top of the stage and took every drag, wheel and click; only the side panel,
   outside it, still worked (Craig 2026-09-27). Hidden means gone, and a SHOWN message
   covers only its own box: the overlay itself lets the pointer through. */
.kg-stage-msg { position: absolute; inset: 16px; display: flex; align-items: center; justify-content: center; pointer-events: none; }
.kg-stage-msg[hidden] { display: none; }
.kg-stage-msg > * { pointer-events: auto; }
/* #1963 — at the BOTTOM of the drawing, centred, in a row (Craig 2026-10-07: "the same
   plus/minus look as on other screens (ie bottom of the screen)"). Its look is the palette
   rule above; this places it, below the tooltip (.kg-tip, z-index 5). The map lifts it
   above its axis band from JS (PAD.bottom), so the number lives once. */
.kg-zoom { left: 50%; bottom: 10px; transform: translateX(-50%); flex-direction: row; z-index: 4; }
.kg-zoom button:disabled { opacity: 0.4; cursor: default; }
.kg-zoom button:hover:not(:disabled) { background: var(--kg-hover); }   /* = the palette's --hover-bg, through the kg token */
.kg-tip {
    position: absolute; z-index: 5; pointer-events: none; max-width: 300px; padding: 8px 10px;
    background: var(--tip-bg); color: var(--tip-fg); border-radius: var(--radius-md);
    border: 1px solid var(--tip-border);
    font-size: var(--font-size-xs); line-height: 1.35; box-shadow: var(--kg-shadow);
}
.kg-tip .kg-muted { color: inherit; opacity: 0.75; }
.kg-tip-t { border-collapse: collapse; margin-top: 4px; }
.kg-tip-t th { text-align: left; font-weight: 400; padding-right: 10px; }
.kg-tip-t td { text-align: right; }
.kg-axes { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
.kg-plot { fill: none; stroke: var(--border); }
.kg-grid { stroke: var(--kg-grid); stroke-width: 1; opacity: 0.7; }
.kg-qline { stroke: var(--text-muted); stroke-dasharray: 4 3; stroke-width: 1; }
.kg-axis-line { stroke: var(--border); }
.kg-tick { fill: var(--text-muted); font-size: 11px; font-variant-numeric: tabular-nums; }
.kg-axis-title { fill: var(--text); font-size: 12px; font-weight: 600; }
.kg-quad { fill: var(--text-muted); font-size: 11px; font-style: italic; }
.kg-spark-bar { fill: var(--kg-cat-1); }
.kg-legend {
    display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: center; padding: 8px 12px;
    font-size: var(--font-size-xs); color: var(--text); border-top: 1px solid var(--border);
}
.kg-legend-k { font-weight: 600; color: var(--text-muted); }
.kg-legend-item {
    display: inline-flex; align-items: center; gap: 6px; background: none; border: 1px solid transparent;
    border-radius: var(--radius-sm); padding: 2px 4px; color: inherit; font: inherit; cursor: pointer;
}
span.kg-legend-item { cursor: default; }
button.kg-legend-item:hover, button.kg-legend-item:focus-visible, .kg-legend-item.is-pinned { border-color: var(--border); outline: none; }
.kg-sw { width: 10px; height: 10px; border-radius: 50%; display: inline-block; flex: none; }
.kg-sw-ring { background: transparent; border: 3px solid; box-sizing: border-box; }
.kg-size { display: inline-flex; align-items: center; gap: var(--space-2xs); }
.kg-size-dot { display: inline-block; border-radius: 50%; background: var(--kg-other); }
.kg-rule { font-size: var(--font-size-xs); padding: 0 12px 8px; }
.kg-inline { display: inline; }
.kg-inline summary { display: inline; cursor: pointer; color: var(--primary-fg); }
.kg-card-head { display: flex; justify-content: space-between; gap: var(--space-lg); padding: 10px 12px; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.kg-card-title { margin: 0; font-size: var(--font-size-lg); color: var(--text); }
.kg-card-sub { font-size: var(--font-size-xs); max-width: 60ch; }
.kg-card-tools { display: flex; gap: var(--space-sm); align-items: center; flex-wrap: wrap; }
.kg-sel { display: inline-flex; gap: var(--space-xs); align-items: center; font-size: var(--font-size-sm); color: var(--text-muted); }
.kg-fallback { padding: 8px 12px 0; }

/* Findings Intelligence: both charts on screen — side by side when THEIR
   CONTAINER is wide enough, one above the other when it is not (Craig: "side by
   side if there is space, one above the other if there isn't"). A container
   query, not a device sniff: the panel beside them takes its share first. */
.kg-fi-body { display: grid; grid-template-columns: minmax(0, 1fr) minmax(300px, 380px); gap: var(--space-lg); align-items: start; }
.kg-fi-views { container-type: inline-size; min-width: 0; }
.kg-fi-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-lg); }
@container (min-width: 1040px) {
    .kg-fi-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
.kg-panel-card { position: sticky; top: 0; max-height: calc(100vh - 170px); overflow-y: auto; padding: 12px 14px; }
/* #1952 (Craig 2026-10-07: "the button on the top of the panel is cut off at the
   top") — on the sphere the panel scrolls WITH the sphere, one unit. Sticky inside a
   parent barely taller than itself, the browser pushed it up as the page scrolled,
   so its name, purpose and first buttons went under the edge while the sphere beside
   it stayed. Its own content still scrolls inside it. */
.kg-sphere-page .kg-panel-card { position: static; max-height: max(520px, calc(100vh - 250px)); }
/* #1951 — the sphere filling the screen: its own page element, so the renderer,
   camera and selection are untouched; the stage fills what the toolbars leave and
   the details panel floats over its right edge, and can be hidden. */
.kg-fs-only { display: none; }
.kg-is-full .kg-fs-only { display: inline-flex; }
.kg-sphere-page.kg-is-full {
    display: flex; flex-direction: column; box-sizing: border-box;
    width: 100vw; height: 100vh; padding: 12px 16px; overflow: hidden; background: var(--bg);
}
.kg-sphere-page.kg-full-overlay { position: fixed; inset: 0; z-index: var(--z-dialog); }   /* on the scale (#1654) */
.kg-is-full .kg-split { flex: 1; min-height: 0; display: block; position: relative; }
.kg-is-full .kg-stage-card { height: 100%; }
.kg-is-full .kg-stage-wrap { flex: 1; min-height: 0; }
.kg-is-full .kg-sphere-stage { position: absolute; inset: 0; height: auto; }
.kg-is-full .kg-panel-card {
    position: absolute; top: 12px; right: 12px; bottom: 12px; z-index: 2;
    width: min(380px, 40vw); max-height: none;
    box-shadow: 0 8px 24px color-mix(in srgb, var(--text) 18%, transparent);
}
.kg-is-full.kg-panel-hidden .kg-panel-card { display: none; }
/* #1952 — every panel section folds to its heading; steps grouped by document. */
details.kg-sec > summary { cursor: pointer; list-style-position: outside; }
details.kg-sec > summary h3 { display: inline; }
.kg-sec-n { font-weight: 400; color: var(--text-muted); }
.kg-steplist { margin: 2px 0 6px; padding-left: 16px; }
.kg-steplist li { font-size: 0.86em; }
.kg-stepno { color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
@media (max-width: 900px) {
    .kg-split, .kg-fi-body { grid-template-columns: minmax(0, 1fr); }
    .kg-panel-card { position: static; max-height: none; }
}
.kg-statusline { display: flex; align-items: center; gap: var(--space-lg); flex-wrap: wrap; }
.kg-statusline .kg-count { margin-left: 0; }
.kg-sources { font-size: var(--font-size-xs); display: flex; flex-direction: column; gap: 2px; padding-top: 4px; }
.kg-attr { font-size: var(--font-size-2xs); color: var(--text-muted); }

/* The one filter */
.kg-filter {
    display: flex; flex-wrap: wrap; gap: 6px 8px; align-items: center; padding: 8px 10px;
    background: var(--kg-surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
}
.kg-filter-k {
    font-size: var(--font-size-2xs); font-weight: 700; color: var(--text-muted); text-transform: uppercase;
    letter-spacing: 0.04em; display: inline-flex; gap: var(--space-2xs); align-items: center;
}
.kg-filter-sep { width: 1px; align-self: stretch; background: var(--border); margin: 0 4px; }
.kg-dd { padding: 5px 9px; font-weight: 500; }
.kg-dd .kg-dd-k { color: var(--text-muted); }
.kg-dd.is-set { border-color: var(--kg-select); }
.kg-dd.is-set .kg-dd-v { font-weight: 700; }
.kg-years { display: inline-flex; gap: var(--space-xs); align-items: center; font-size: var(--font-size-sm); color: var(--text-muted); }
.kg-note { font-size: var(--font-size-xs); margin-top: 4px; display: flex; gap: var(--space-xs); align-items: center; }
.kg-dd-pop {
    width: 320px; background: var(--kg-surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
    box-shadow: var(--kg-shadow); padding: 8px; display: flex; flex-direction: column; gap: var(--space-xs); color: var(--text);
}
.kg-dd-head { font-weight: 700; font-size: var(--font-size-sm); }
.kg-dd-list { max-height: 300px; overflow-y: auto; display: flex; flex-direction: column; }
.kg-dd-row {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: var(--space-sm); align-items: center;
    padding: 3px 4px; border-radius: var(--radius-sm); font-size: var(--font-size-sm); cursor: pointer;
}
.kg-dd-row:hover { background: var(--kg-hover); }
.kg-dd-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kg-dd-none { padding: 6px 4px; font-size: var(--font-size-sm); }
.kg-dd-foot { display: flex; gap: var(--space-xs); align-items: center; justify-content: flex-end; }
.kg-dd-n { margin-right: auto; font-size: var(--font-size-xs); color: var(--text-muted); }

/* The one detail panel */
.kg-panel { font-size: var(--font-size-md); color: var(--text); }
.kg-panel-empty { color: var(--text-muted); padding: 12px 4px; display: flex; gap: var(--space-xs); align-items: flex-start; }
.kg-panel-head .kg-kind { font-size: var(--font-size-2xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); font-weight: 700; }
/* #1746 F7 — the title wraps and grows its block, three lines at most, then ends in an
   ellipsis with the whole title on hover (its title attribute). Nothing below it moves under it. */
.kg-panel-head { display: block; margin-bottom: var(--space-xs); }
.kg-panel-title { font-size: var(--font-size-xl); line-height: 1.25; margin: 2px 0; overflow-wrap: anywhere; color: var(--text);
                  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
.kg-panel-sub { color: var(--text-muted); font-size: var(--font-size-sm); }
.kg-badge {
    display: inline-block; font-size: var(--font-size-2xs); font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.04em; padding: 2px 6px; border: 1px solid currentColor; border-radius: var(--radius-xs);
    margin: 6px 6px 0 0; color: var(--text-muted);
}
.kg-status-unconfirmed { color: var(--kg-warn-ink); }
.kg-status-confirmed { color: var(--success-text); }
/* #1746 — --kg-subtle (defined with the kg tokens) flips with the colour scheme. */
.kg-purpose {
    display: flex; gap: var(--space-sm); align-items: center; margin: 10px 0; padding: 6px 8px;
    background: var(--kg-subtle); color: var(--text);
    border-radius: var(--radius-md); font-size: var(--font-size-sm); font-weight: 600;
}
.kg-purpose select { flex: 1; min-width: 0; }
.kg-panel-actions { display: flex; gap: var(--space-xs); flex-wrap: wrap; margin: 6px 0; }
.kg-sec { border-top: 1px solid var(--border); padding: 10px 0 6px; }
.kg-sec h3, .kg-sec > summary {
    font-size: var(--font-size-2xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted);
    margin: 0 0 6px; font-weight: 700;
}
.kg-sec-folded > summary { cursor: pointer; }
.kg-kv { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 3px 12px; margin: 0 0 8px; font-size: var(--font-size-sm); }
.kg-kv dt { color: var(--text-muted); }
.kg-kv dd { margin: 0; text-align: right; }
.kg-mini { width: 100%; border-collapse: collapse; font-size: var(--font-size-xs); margin: 6px 0; }
.kg-mini caption { text-align: left; color: var(--text-muted); font-weight: 600; padding-bottom: 2px; }
.kg-mini th, .kg-mini td { padding: 2px 4px; border-bottom: 1px solid var(--border); text-align: left; font-weight: 400; }
.kg-mini .kg-num { text-align: right; }
.kg-barcell { width: 40%; }
.kg-bar { display: block; height: 6px; border-radius: 3px; background: var(--kg-cat-1); }
.kg-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-xs); font-size: var(--font-size-sm); }
.kg-right { float: right; color: var(--text-muted); }
.kg-link {
    background: none; border: none; padding: 0; color: var(--primary-fg); cursor: pointer; font: inherit; text-align: left;
    text-decoration: underline; text-decoration-color: var(--border); text-underline-offset: 2px;
}
.kg-link:hover, .kg-link:focus-visible { text-decoration-color: currentColor; outline: none; }
.kg-chip { font-size: var(--font-size-2xs); border: 1px solid var(--border); border-radius: var(--radius-xs); padding: 0 5px; color: var(--text-muted); }
.kg-quote { margin: 4px 0; padding: 4px 8px; border-left: 3px solid var(--border); color: var(--text); font-size: var(--font-size-sm); white-space: pre-wrap; }
.kg-reg { padding-bottom: 6px; border-bottom: 1px dashed var(--border); }
.kg-ext { display: inline-flex; gap: var(--space-2xs); align-items: center; font-size: var(--font-size-xs); color: var(--primary-fg); }
.kg-regtext {
    white-space: pre-wrap; max-height: 260px; overflow-y: auto; font-size: var(--font-size-sm); padding: 6px 8px;
    background: var(--kg-subtle); color: var(--text); border-radius: var(--radius-md); margin-bottom: 6px;
}
.kg-chain { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--space-sm); }
.kg-chain-clause { font-weight: 600; display: flex; gap: var(--space-2xs); align-items: center; }
.kg-anchor-foot { display: flex; justify-content: space-between; gap: var(--space-sm); align-items: center; margin-top: 2px; }
.kg-open { padding: 4px 8px; font-size: var(--font-size-xs); }
.kg-beside { font-size: var(--font-size-xs); color: var(--text-muted); }
.kg-panel-foot { border-top: 1px solid var(--border); margin-top: 10px; padding-top: 10px; display: flex; flex-wrap: wrap; gap: var(--space-xs); }
.kg-msg {
    padding: 8px 10px; border-radius: var(--radius-md); font-size: var(--font-size-sm); background: var(--kg-subtle);
    color: var(--text); display: flex; gap: var(--space-xs); align-items: center; flex-wrap: wrap;
}
.kg-msg.warn { background: var(--kg-warn-bg); color: var(--kg-warn-ink); }
.kg-msg.fail { background: var(--kg-fail-bg); color: var(--danger); }
.kg-raw { font-family: var(--mono); font-size: var(--font-size-2xs); white-space: pre-wrap; }
.kg-p { margin: 4px 0; }
.kg-spark { margin: 6px 0; }
.kg-spark figcaption { font-size: var(--font-size-2xs); }

/* The data table under every chart */
.kg-table-host { border-top: 1px solid var(--border); padding: 8px 12px 12px; }
.kg-table-cap { display: flex; justify-content: space-between; align-items: center; gap: var(--space-sm); font-size: var(--font-size-xs); color: var(--text-muted); margin-bottom: 6px; }
.kg-table-scroll { max-height: 280px; overflow: auto; border: 1px solid var(--border); border-radius: var(--radius-md); }
.kg-table { width: 100%; border-collapse: collapse; font-size: var(--font-size-xs); }
.kg-table th { position: sticky; top: 0; background: var(--card); z-index: 1; text-align: left; border-bottom: 1px solid var(--border); padding: 0; }
.kg-table th.kg-num { text-align: right; }
.kg-th { background: none; border: none; width: 100%; text-align: inherit; padding: 5px 6px; font: inherit; font-weight: 700; color: var(--text); cursor: pointer; white-space: nowrap; }
.kg-table td { padding: 3px 6px; border-bottom: 1px solid var(--border); max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kg-table td.kg-num { text-align: right; }
.kg-table tr.is-selected td { background: var(--primary-pale); }   /* the ONE selection colour */
.kg-more { margin-top: 6px; }

/* The larger window */
.kg-popout-body { padding: 10px; height: min(78vh, 900px); overflow: hidden; }
.kg-pop-grid { display: grid; grid-template-columns: minmax(0, 1fr) 360px; gap: 10px; height: 100%; }
.kg-pop-chart { overflow-y: auto; }
.kg-pop-chart .kg-cloud-stage, .kg-pop-chart .kg-map-wrap { height: min(56vh, 640px); }
.kg-pop-grid .kg-panel-card { position: static; max-height: none; overflow-y: auto; }

/* The halo menu, as the taxonomy views use it: a shortcut on the right and, for
   an action the server says cannot run, its reason beneath. */
.rc-halo-menuitem { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 2px 14px; }
.rc-halo-menuitem:disabled { cursor: not-allowed; opacity: 0.62; }
.rc-halo-menuitem:disabled:hover { background: transparent; }
.rc-halo-why, .rc-ctxmenu-detail { grid-column: 1 / -1; font-weight: 400; font-size: 11.5px; color: var(--text-muted); white-space: normal; max-width: 320px; }

/* #1901 — THE ONE MENU's box (rc_menu.js): every menu in the app, canvas and page.
   Tokens only, so it follows light and dark; it was inline light hexes with no dark
   counterpart (FE-F28). Its layer is RCAnchor.raise()'s (the floating-window band),
   never one pinned between the blocking dialogs and the refusal channel. */
.rc-menu {
    position: fixed; display: flex; flex-direction: column; overflow-y: auto;
    background: var(--pq-elevated); border: 1px solid var(--pq-border); color: var(--pq-ink);
    box-shadow: 0 12px 30px rgba(8, 20, 40, .24);
    font-family: var(--sans, system-ui, sans-serif); user-select: none;
}

/* #1901 (FE-F28) — THE REVIEW-QUESTIONS DRAWER (review_panel.js), on the theme
   tokens. It injected its own stylesheet of light colours with a private dark
   block. It stays an overlay rather than the shared `.rc-drawer`: that shell is the
   docked rail of the analysis layout, and this drawer opens over the QMS list,
   where there is no rail to dock to. */
.rc-rq-overlay {
    position: fixed; inset: 0; z-index: calc(var(--z-dialog) + 40); display: flex; justify-content: flex-end;
    background: rgba(0, 0, 0, .4);
}
.rc-rq-drawer {
    position: relative; width: min(440px, 100%); height: 100%; overflow-y: auto;
    padding: 1.1rem 1.2rem; background: var(--pq-elevated); color: var(--pq-ink);
    box-shadow: -3px 0 16px rgba(0, 0, 0, .3);
}
.rc-rq-drawer h2 { font-size: 1.05rem; font-weight: 600; margin: .2rem 0 1rem; padding-right: 2.6rem; }
.rc-rq-drawer h3 {
    font-size: .8rem; font-weight: 600; text-transform: uppercase; letter-spacing: .03em;
    color: var(--text-muted); margin: 1.2rem 0 .4rem;
}
/* WT-5 — the close control is the shared .rc-close (#1700); only WHERE it sits. */
.rc-rq-drawer > .rc-close { position: absolute; top: .8rem; right: .9rem; }
.rc-rq-item {
    border: 1px solid var(--pq-border); border-radius: 8px; padding: .7rem .8rem; margin: .5rem 0;
    background: var(--pq-surface);
}
.rc-rq-item .code { font-family: var(--mono); font-size: .72rem; font-weight: 600; color: var(--btn-caution); }
.rc-rq-item .prompt { margin: .35rem 0; font-size: .9rem; }
.rc-rq-item .ev { font-size: .75rem; color: var(--text-muted); margin: .25rem 0; }
.rc-rq-item input, .rc-rq-item select {
    width: 100%; padding: .4rem; margin: .3rem 0; font: inherit; font-size: 14px;
    border: 1px solid var(--pq-border); border-radius: 6px; background: var(--input-bg); color: inherit;
}
.rc-rq-row { display: flex; gap: .5rem; margin-top: .4rem; }
.rc-rq-row button { flex: 1; justify-content: center; }
.rc-rq-done { color: var(--text-muted); font-size: .82rem; padding: .35rem .5rem; border-bottom: 1px dotted var(--pq-border); }
.rc-rq-empty { color: var(--text-muted); font-size: .9rem; margin: 1rem 0; }

/* #1698 — THE ACTION MENU IS A STANDARD CONTEXT MENU. Craig 2026-09-27: "The menu
   looks ugly and not like a standard pop up menu". Flat full-width rows with no
   border of their own, one tinted highlight on hover and on keyboard focus, a
   title strip, thin separators, a danger colour. Tokens only, so it follows light
   and dark; the tint is mixed from the accent so it reads in both. */
.rc-ctxmenu { padding: 4px; border-radius: 8px; min-width: 200px; }
.rc-ctxmenu .rc-ctxmenu-title {
    margin: 2px 8px 4px; padding-bottom: 6px;
    border-bottom: 1px solid var(--border);
}
/* #2176 R3 — why the entries below are greyed (a held or Effective document) */
.rc-ctxmenu .rc-ctxmenu-notice {
    margin: 0 8px 6px; padding: 6px 8px; max-width: 320px;
    border-radius: var(--radius-sm); background: var(--surface-2);
    color: var(--text); font-size: 12px; line-height: 1.4;
}

/* #2176 R1 — a finding whose step was edited since it was raised, and the
   re-check's proposal waiting for a person. Tokens only: light and dark. */
.rc-recheck { display: flex; flex-direction: column; gap: 6px; margin: 6px 0 2px; font-size: 12px; }
.rc-recheck-changed, .rc-recheck-proposal, .rc-recheck-bar {
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px;
}
.rc-recheck-chip {
    padding: 1px 8px; border-radius: 999px; border: 1px solid var(--conflict-bar);
    color: var(--conflict-text); background: var(--surface);
}
.rc-recheck-proposal {
    padding: 6px 8px; border-radius: var(--radius-sm);
    border: 1px solid var(--border); background: var(--surface-2); color: var(--text);
}
.rc-recheck-verdict { font-weight: 600; }
.rc-recheck-reason { flex: 1 1 100%; color: var(--text); }
.rc-recheck-decided { color: var(--text-muted); }
.rc-recheck-actions { display: inline-flex; gap: 6px; }
.rc-recheck-btn, .rc-recheck-agree, .rc-recheck-disagree {
    padding: 2px 10px; border-radius: var(--radius-sm); border: 1px solid var(--border);
    background: var(--surface); color: var(--text); font: inherit; cursor: pointer;
}
.rc-recheck-btn:hover, .rc-recheck-agree:hover, .rc-recheck-disagree:hover { background: var(--hover-bg); }
.rc-recheck-bar {
    margin: 0 0 8px; padding: 6px 8px; border-radius: var(--radius-sm);
    border: 1px solid var(--conflict-bar); background: var(--surface); color: var(--text); font-size: 12px;
}
.rc-recheck-bar-text { flex: 1 1 auto; }
/* #2176 R2 — a finding shown inside the "Edit step" window, at the click */
.pg-stepform-finding {
    margin: 8px 0 0; padding: 8px 10px; border-radius: var(--radius-sm);
    border: 1px solid var(--border); background: var(--surface-2); color: var(--text); font-size: 12.5px;
}
.pg-stepform-finding-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.pg-stepform-finding-head strong { flex: 1 1 auto; }
.pg-stepform-finding-label { color: var(--text-muted); margin-right: 4px; }
.pg-stepform-finding p { margin: 2px 0; }
.rc-ctxmenu .rc-halo-menuitem {
    margin: 0; width: 100%; text-align: left;
    padding: 6px 12px; border: 0; border-radius: var(--radius-sm);
    background: transparent; color: var(--pq-ink);
    font-size: 13px; font-weight: 400; line-height: 1.35;
}
/* #1967 — a submenu row's chevron, in the shortcut column; the open row stays lit */
.rc-ctxmenu-chev { justify-self: end; color: var(--text-muted); font-size: 15px; line-height: 1; }
.rc-ctxmenu .rc-halo-menuitem[aria-expanded="true"],
.rc-ctxmenu .rc-halo-menuitem:hover:not(:disabled),
.rc-ctxmenu .rc-halo-menuitem:focus-visible {
    background: color-mix(in oklab, var(--pq-accent-2) 16%, transparent);
    outline: none;
}
.rc-ctxmenu .rc-ctxmenu-danger { color: var(--pq-danger); }
.rc-ctxmenu-sep { height: 1px; margin: 4px 6px; background: var(--border); }
@media (prefers-contrast: more) {
    .rc-ctxmenu .rc-halo-menuitem:focus-visible { outline: 2px solid var(--pq-ink); outline-offset: -2px; }
}

/* #1883 — THE GROUP COVERAGE REPORT window (rc_coverage.js). Every colour is a
   theme token mixed with the surface, so the light and dark schemes both hold. */
.rc-cov-controls { display: flex; flex-wrap: wrap; gap: 10px 14px; align-items: flex-end; margin-bottom: 12px; }
.rc-cov-kind { border: 1px solid var(--border); border-radius: var(--radius-md); padding: 4px 10px 6px; margin: 0; display: flex; gap: 14px; }
.rc-cov-kind legend { font-size: var(--font-size-xs); color: var(--text-muted); padding: 0 4px; }
.rc-cov-kind label { font-size: var(--font-size-md); display: flex; gap: 5px; align-items: center; }
.rc-cov-runs-label { font-size: var(--font-size-xs); color: var(--text-muted); display: flex; flex-direction: column; gap: 3px; min-width: 260px; }
.rc-cov-report { max-height: 68vh; overflow: auto; font-size: var(--font-size-md); }
.rc-cov-muted { color: var(--text-muted); }
.rc-cov-statement { font-style: italic; color: var(--text-muted); margin: 0 0 10px; }
.rc-cov-summary { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 10px; }
.rc-cov-tile { border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 8px 12px; min-width: 300px; }
.rc-cov-tile-head { font-weight: 600; margin-bottom: 6px; }
.rc-cov-counts { display: flex; flex-wrap: wrap; gap: 6px; }
.rc-cov-chip, .rc-cov-tag { display: inline-block; border-radius: 999px; padding: 1px 9px; font-size: var(--font-size-xs);
    border: 1px solid var(--rc-cov-c, var(--border));
    background: color-mix(in oklab, var(--rc-cov-c, var(--border)) 12%, var(--pq-surface)); color: var(--text); }
.rc-cov-gap, .rc-cov-potential_conflict { --rc-cov-c: var(--danger); }
.rc-cov-partial, .rc-cov-restatement { --rc-cov-c: var(--pq-accent); }
.rc-cov-overlap, .rc-cov-duplicate { --rc-cov-c: var(--primary); }
.rc-cov-covered, .rc-cov-sufficient { --rc-cov-c: var(--ok); }
.rc-cov-read { border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 6px 12px; margin-bottom: 12px; }
.rc-cov-read summary, .rc-cov-group summary { cursor: pointer; }
.rc-cov-read h4 { margin: 10px 0 4px; font-size: var(--font-size-md); }
.rc-cov-reg { margin: 16px 0 6px; font-size: var(--font-size-2xl); border-bottom: 1px solid var(--border); padding-bottom: 4px; }
.rc-cov-group { margin: 6px 0 10px; }
.rc-cov-parts, .rc-cov-passages, .rc-cov-pairs { list-style: none; padding-left: 14px; margin: 6px 0; }
.rc-cov-part { padding: 6px 0; border-bottom: 1px solid color-mix(in oklab, var(--border) 60%, transparent); }
.rc-cov-part-head { font-weight: 600; }
.rc-cov-reason { color: var(--text-muted); margin: 2px 0 4px; }
.rc-cov-passage { margin: 5px 0; }
.rc-cov-excerpt { margin: 2px 0 0 2px; padding-left: 8px; border-left: 2px solid var(--border); white-space: pre-wrap; }
.rc-cov-link { font-weight: 600; }
.rc-cov-failed { border: 1px solid var(--danger); border-radius: var(--radius-lg); padding: 10px 12px; }
.rc-cov-progress { padding: 24px 0; display: flex; flex-direction: column; gap: 8px; max-width: 520px; }
.rc-cov-progress progress { width: 100%; accent-color: var(--primary); }

/* "Open at the paragraph": where the paragraph is, for a moment. */
.kg-para-flash { outline: 3px solid var(--kg-select); outline-offset: 2px; }
@media (prefers-reduced-motion: no-preference) {
    .kg-para-flash { transition: outline-color 0.3s ease; }
}

/* #1806 — THE EDITING CONTROLS: Undo · Redo · Revert · Save, in each surface's top
   toolbar (rc_edit_controls.js). They replace #1768's floating bottom bar, which
   read as a prompt after every change. Save is the one filled button; a dot on it
   says there is unsaved work, because a disabled Save alone cannot say that in a
   way that survives greyscale. Tokens only, so light and dark follow the app. */
.rc-edit-host[hidden] { display: none; }
.rc-edit-controls { display: inline-flex; align-items: center; gap: 4px; margin-left: 6px; }
.rc-edit-controls .btn-sem { padding-top: 3px; padding-bottom: 3px; }
.rc-edit-controls .rc-edit-flip { transform: scaleX(-1); }
.rc-edit-controls.is-dirty .rc-edit-save { position: relative; }
.rc-edit-controls.is-dirty .rc-edit-save::after {
    content: ""; position: absolute; top: -3px; right: -3px; width: 8px; height: 8px;
    border-radius: 50%; background: var(--btn-caution); border: 1px solid var(--pq-surface);
}
@media (prefers-contrast: more) {
    .rc-edit-controls.is-dirty .rc-edit-save::after { border-color: var(--text); }
}
/* #1843 — who holds the Draft, for as long as they hold it, beside the controls;
   Release for an administrator. [hidden] is restated because .btn-sem sets its
   own display, which beats the browser's hidden rule. */
.rc-edit-controls [hidden] { display: none; }
.rc-edit-held {
    display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px;
    border-radius: 999px; font-size: 0.8rem; font-weight: 600; white-space: nowrap;
    background: var(--gap-bg); color: var(--gap-text); border: 1px solid var(--gap-bar);
}

/* ══ #1938 · ONE SELECTION COLOUR (Craig 2026-10-06, comment 21760) ══════════
   "the selected item highlighted ... that should match other locations ... I am
   preferring the light blue". Every rail, list, tab and segmented control marks
   its chosen item with --primary-pale and the ordinary ink — not a filled blue
   here and a tint there. On/off toggle chips (reason chips, filter chips, the
   editor's format buttons) are not a selected ITEM and keep their own look.
   :is() takes its most specific argument, so this outranks each original rule. */
:is(.sa-rail-item.active, .sa-seg .sa-runmode-opt.active, .runmode-opt.active, .chat-mode-btn.active,
    .domain-list-item.active, .admin-tab.active, #admin-modal .admin-tab.active,
    .qma-tax-map-row.is-selected, .kg-table tr.is-selected td, .ui-prefs-seg [aria-pressed="true"],
    .doc-palette-btn.active,
    .pg-palette [data-tool="flow-v2"].is-active, .rc-help-topic.is-active,
    .qma-pick-folder[aria-selected="true"]) {
    background: var(--primary-pale); color: var(--text);
}
.sa-rail-item.active .sa-rail-count { background: var(--hover-bg); color: var(--text); }


/* ── #1968 — THE DOCUMENT WINDOW (rc_doc_window.js) ──────────────────────────
   Craig 2026-10-07, boards "2 + 3" and "Scrolled: what stays frozen" (option A).
   One implementation on the Document view and the canvas: a title bar directly
   above the page (window controls, number and title, status), the open documents
   as tabs along the bottom, and Open documents (n) in the top bar. Theme tokens
   throughout, so it follows prefers-color-scheme; only the Mac's dots are literal. */
.rc-docwin-bar[hidden], .rc-docwin-tabs[hidden] { display: none; }
.rc-docwin-bar {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 34px;
    padding: 4px 4px 4px 12px;
    color: var(--text);
    font-size: 13px;
}
.rc-docwin-ident { flex: 1 1 auto; min-width: 0; display: flex; align-items: baseline; gap: 8px; }
.rc-docwin-num { flex: none; font-weight: 700; white-space: nowrap; }
.rc-docwin-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rc-docwin-status { flex: none; padding: 0 8px; font-size: 12px; color: var(--text-muted); white-space: nowrap; }
/* #2383 — the Draft is still being prepared after its import: steady until it lands
   (the house rule for anything over a second or two), in the same tokens as the
   "Held by" marker, so it follows light and dark. The clock pulses gently unless
   the reader has asked for reduced motion; the words carry the meaning either way. */
.rc-docwin-preparing {
    flex: none; display: inline-flex; align-items: center; gap: 4px; margin-right: 8px;
    padding: 1px 8px; border-radius: 999px; font-size: 12px; font-weight: 600; white-space: nowrap;
    background: var(--gap-bg); color: var(--gap-text); border: 1px solid var(--gap-bar);
}
.rc-docwin-preparing .ic { animation: rc-preparing-pulse 1.6s ease-in-out infinite; }
@keyframes rc-preparing-pulse { 0%, 100% { opacity: 0.45; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .rc-docwin-preparing .ic { animation: none; } }

/* THE DOCUMENT VIEW: the title bar, the Document / Quality report switch, the
   page and the tabs are ONE window, as wide as the page (--doc-card-w, the same
   variable the sheet takes) and stacked with no gap. The scroll box keeps a
   gutter on both sides so the page stays centred under the bar. */
#doc-frame > .rc-docwin-bar,
#doc-frame > .doc-surface-tabs,
#doc-frame > .rc-docwin-tabs {
    box-sizing: border-box;
    width: min(var(--doc-card-w), calc(100% - 64px));
    margin-left: auto;
    margin-right: auto;
    flex-shrink: 0;
}
#doc-frame > .rc-docwin-bar {
    margin-top: 12px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-bottom: 0;
    border-radius: 10px 10px 0 0;
}
#doc-frame > .doc-surface-tabs { border-left: 1px solid var(--border); border-right: 1px solid var(--border); }
#doc-frame > .doc-scroll { padding-top: 0; padding-bottom: 0; scrollbar-gutter: stable both-edges; }
#doc-frame > .rc-docwin-bar:not([hidden]) ~ .doc-scroll .doc-sheet,
#doc-frame > .rc-docwin-bar:not([hidden]) ~ .doc-scroll .doc-qr { border-top: 0; border-radius: 0; }   /* #2113 */

/* OPTION A — THE HEADER BLOCK STAYS IN VIEW. The control statement and the
   document header are held at the top of the page by the browser while the body
   scrolls beneath: position sticky, in normal flow inside the sheet (no bar
   painted over a scrolling page). It reaches the sheet's edges, so the rows that
   pass under it are covered edge to edge, and carries the sheet's own ground. */
.doc-sheet-head {
    position: sticky;
    top: 0;
    z-index: 4;
    margin: -20px -20px 0;
    padding: 20px 20px 16px;
    background: var(--bg-white);
}
/* nothing open (the all-documents summary): no header block either */
.doc-sheet:not(:has(> #doc-body:not(.hidden), > #doc-structure:not(.hidden), > #doc-routing:not(.hidden))) > .doc-sheet-head { display: none; }
/* THE CANVAS: the title bar is its header's first row */
.graph-view-header > .rc-docwin-bar { padding: 0; min-height: 26px; font-size: 1rem; }

/* GREEN — the page takes the window's full width */
/* both card widths (freeform and structure) give way, so the rule names both */
body.rc-doc-fullwidth #doc-frame,
body.rc-doc-fullwidth .doc-frame:has(#doc-structure:not(.hidden)) { --doc-card-w: 100000px; }

/* THE TABS, along the bottom of the page: the one showing joins the page above it */
.rc-docwin-tabs {
    display: flex;
    align-items: flex-start;
    gap: 4px;
    padding: 0 6px 6px;
    border-top: 1px solid var(--border);
    font-size: 12px;
}
#doc-frame > .rc-docwin-tabs { margin-bottom: 8px; }
.process-graph-wrap > .rc-docwin-tabs { padding: 0 12px 4px; background: var(--background); flex-shrink: 0; }
.rc-docwin-tablist {
    display: flex;
    gap: 2px;
    min-width: 0;
    flex: 0 1 auto;
    overflow-x: auto;
    scrollbar-width: thin;
}
.rc-docwin-tab {
    flex: none;
    display: flex;
    align-items: center;
    gap: 6px;
    max-width: 260px;
    margin-top: -1px;
    padding: 5px 12px 6px;
    border: 1px solid var(--border);
    border-top-color: transparent;
    border-radius: 0 0 6px 6px;
    background: var(--surface-2);
    color: var(--text-muted);
    font: inherit;
    cursor: pointer;
}
.rc-docwin-tab:hover { color: var(--text); }
.rc-docwin-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.rc-docwin-tab[aria-selected="true"] {
    background: var(--bg-white);
    border-top-color: var(--bg-white);
    color: var(--text);
    font-weight: 600;
}
.rc-docwin-tab-num { flex: none; white-space: nowrap; }
.rc-docwin-tab-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 400; }
.rc-docwin-tab-count {
    flex: none;
    min-width: 18px;
    padding: 0 5px;
    border-radius: 9px;
    background: var(--border);
    color: var(--text);
    font-size: 11px;
    text-align: center;
}
.rc-docwin-add {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 26px;
    margin-top: -1px;
    border: 1px solid var(--border);
    border-top-color: transparent;
    border-radius: 0 0 6px 6px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
}
.rc-docwin-add:hover { color: var(--text); background: var(--surface-2); }
.rc-docwin-add .ic { width: 14px; height: 14px; }
.rc-docwin-progress { margin-left: auto; align-self: center; padding-top: 4px; color: var(--text-muted); white-space: nowrap; }
.rc-docwin-progress:empty { display: none; }

/* OPEN DOCUMENTS (n), in the top bar: the bell's look, with its words */
.rc-open-docs-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: 8px;
    padding: 5px 9px;
    border: 1px solid color-mix(in srgb, currentColor 30%, transparent);
    border-radius: 6px;
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: 0.8rem;
    cursor: pointer;
    white-space: nowrap;
}
.rc-open-docs-btn:hover, .rc-open-docs-btn:focus-visible { border-color: currentColor; outline: none; }
.rc-open-docs-btn .ic { width: 16px; height: 16px; }
body.popup-mode .rc-open-docs-btn { display: none; }


/* #1952 — the sphere's find box: every match, with its kind, narrowable by kind */
.kg-findbox { position: relative; display: inline-flex; }
.kg-find-pop {
    position: absolute; top: calc(100% + 4px); left: 0; z-index: var(--z-dropdown);
    width: max(100%, 380px); max-height: 380px; display: flex; flex-direction: column;
    background: var(--bg-white); color: var(--text); border: 1px solid var(--border); border-radius: 6px;
    box-shadow: 0 6px 20px color-mix(in srgb, var(--text) 16%, transparent);
}
.kg-find-pop[hidden] { display: none; }
.kg-find-kinds { display: flex; gap: 4px; padding: 6px; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.kg-find-kind { font: inherit; font-size: 12px; padding: 2px 9px; border: 1px solid var(--border); border-radius: 12px; background: transparent; color: var(--text-muted); cursor: pointer; }
.kg-find-kind[aria-pressed="true"] { background: var(--primary-pale); color: var(--text); border-color: var(--primary-fg); }
.kg-find-list { list-style: none; margin: 0; padding: 4px 0; overflow-y: auto; flex: 1 1 auto; min-height: 0; }
.kg-find-list [role="option"] { display: flex; gap: 8px; align-items: baseline; padding: 5px 10px; cursor: pointer; font-size: 13px; }
.kg-find-list [role="option"]:hover { background: var(--kg-hover); }
.kg-find-list [role="option"][aria-selected="true"] { background: var(--primary-pale); }
.kg-find-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kg-find-tag { flex: none; font-size: 11px; color: var(--text-muted); }
.kg-find-more { padding: 6px 10px; font-size: 12px; border-top: 1px solid var(--border); }

/* #2412 — a debate turn's citations, quietly under the turn; one outside the
   analysis's framework items says so. Theme tokens only (light and dark). */
.er-turn-cites { display: flex; flex-wrap: wrap; gap: 4px 8px; margin: 2px 0 6px 6px; font-size: 0.74rem; color: var(--text-muted); }
.er-turn-cite { display: inline-flex; align-items: center; gap: 4px; }
.er-turn-cite-ref { cursor: pointer; }
.er-turn-cite-out { font-size: 0.68rem; color: var(--text-muted); border: 1px solid var(--border); border-radius: 3px; padding: 0 4px; }
