/*
  Pure-Bootstrap app shell chrome. Every rule is built on Bootstrap 5.3 --bs-*
  variables so the shell follows the active colour mode (data-bs-theme). The palette is Bootstrap's,
  with four changes made once, under "Palette" below (#6835, #6861) and "Link treatment" (#5755): a
  canvas below the surface and a navy top bar (--rd-canvas and --rd-topbar, the two house surface
  tokens), the guide's #006bb7 as primary, and the guide's link colour. Every value they introduce is
  written in those two blocks' tokens, with one
  exception: the focused switch knob is a data URI, which cannot read a token, so its rule under
  "Palette" repeats --rd-focus-border URL-encoded, and PaletteConventionTests holds the two equal.
  app.css is fully retired; this is the only app-owned stylesheet (alongside the
  scoped *.razor.css). Holds the navbar, the section rails, page-width helpers,
  and a few small utilities Bootstrap doesn't ship.
*/

/* ---- Reading measure (#4849) ------------------------------------------ */
/* The one place the measure is set. `ch` is the width of "0" — about 1.33x the average
   glyph in running prose here — so the density guide's old literal 65ch rendered at ~87
   characters, over the 60-75 the guide itself requires. 52ch is calibrated by measurement
   in the browser (94 rendered lines, 14px and 12.25px): longest line 74, median 63.
   Re-measure, don't re-guess, if the UI font changes. Density guide S2 carries the same
   number and the same reason.

   Two values, because a `ch` is not a character and the gap between them depends on the
   weight: Bootstrap's .lead is font-weight 300, whose glyphs run narrower against the same
   "0", so the identical ch count renders a LONGER line. Measured: 52ch on a lead gives 86
   characters, 45ch gives 72. Any new text style at a different weight gets measured, not
   assumed. */
:root {
    --reading-measure: 52ch;       /* body prose, 400 weight — longest line 74 */
    --reading-measure-lead: 45ch;  /* .lead, 300 weight — longest line 72 */
}


/* ---- Type scale and weights (#6412) ------------------------------------- */
/* THE STYLE GUIDE'S SCALE, APPLIED ONCE. UI_STYLE_GUIDE.md § Type Scale has said h1 2rem, h2 1.5rem,
   h3 1.25rem, h4 1.125rem, h5 1rem, all at 600, since #5833 A3 chose it — and until this block no
   stylesheet RiskCheck loads set a heading rule at all, so Bootstrap's own ran everywhere but the
   finding page (.rd-finding-*, the one place the scale was applied, below).

   WHY THE WEIGHTS MATTER MORE THAN THE SIZES. Bootstrap draws headings at 500 and `strong`, `b`,
   badges and table column headers at 700, so a bold phrase, a status badge or a column label always
   outweighed any heading, and size alone had to say "heading" — which the per-page size classes then
   took away. Headings go to 600 here, and every bold that is not a heading comes down to 600 with them,
   so nothing that is merely emphasised is heavier than the structure.

   SIZES REACH ONLY THE BARE ELEMENTS. A `.h2`–`.h6` class outranks an element selector, so the 349
   headings that carry one keep Bootstrap's size for that class (the page title is `<h1 class="h2">`,
   2rem at desktop width, which is the scale's h1 already). The weight reaches them all, through the
   class list below. Removing the size classes is the follow-up once this reads right. */
h1 { font-size: 2rem; }
h2 { font-size: 1.5rem; }
h3 { font-size: 1.25rem; }
h4 { font-size: 1.125rem; }
h5, h6 { font-size: 1rem; }

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 { font-weight: 600; }

b, strong, th { font-weight: 600; }

.badge { --bs-badge-font-weight: 600; }


/* ---- Link treatment (#5755 / I10.6) ------------------------------------ */
/* THE GUIDE'S BLUE, BOUND ONCE. docs/UI_STYLE_GUIDE.md has specified --link-color: #006bb7 since it
   was written, and the token appeared in no stylesheet the estate serves — so every anchor rendered
   Bootstrap's own #0d6efd in light mode and #6ea8fe in dark, and a product owner called the result
   "a stark blue ... it does not work with the aesthetic of the design".

   BOUND TO THE -rgb TOKENS, WHICH IS THE PART THAT RENDERS. Bootstrap 5.3 draws an anchor with
   `color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1))` and swaps
   `--bs-link-color-rgb: var(--bs-link-hover-color-rgb)` on hover. Setting only the hex pair would
   have been inert — a binding that changes nothing, which is the shape of the defect this fixes.
   Both forms are set: the hex for anything reading it (Bootstrap's .link-* utilities do) and for a
   reader of this file, the rgb because that is what paints.

   THE DARK VALUE IS DERIVED, NOT INVENTED. Bootstrap makes its own dark link colour by tinting its
   light one 40% and its dark hover by tinting 52%; verified by reproducing #6ea8fe and #8bb9fe
   exactly from #0d6efd. The same rules on #006bb7 give #66a6d4, which measures 4.37:1 on
   --bs-secondary-bg and fails AA, because our base is darker than Bootstrap's. The tint is raised to
   45%, giving 4.80:1 there — the margin Bootstrap allows itself (4.76:1) — and the hover keeps
   Bootstrap's +12-point step, so 57%.

   Measured against every ground an anchor sits on, WCAG 2.2 AA (1.4.3) needing 4.5:1. The canvas
   column is #6835's: since then the page itself is a ground (--rd-canvas, under "Palette" below).

     light   --bs-body-bg #ffffff   --bs-secondary-bg #e9ecef   --rd-canvas #f3f4f6
       rest  #006bb7      5.55                4.68                   5.04
       hover #005692      7.65                6.45                   6.95

     dark    --bs-body-bg #212529   --bs-secondary-bg #343a40   --bs-tertiary-bg #2b3035   --rd-canvas #1a1d20
       rest  #73aed7      6.44                4.80                       5.56                   7.07
       hover #91bfe0      7.88                5.88                       6.81                   8.65

   None claims AAA. Re-measure, don't re-guess, if a surface colour moves.

   Bootstrap's own pattern for the pair: :root and an explicit light together, dark after, at equal
   specificity so the later block wins. Following it rather than inventing a selector. */
:root,
[data-bs-theme="light"] {
    --bs-link-color: #006bb7;
    --bs-link-color-rgb: 0, 107, 183;
    --bs-link-hover-color: #005692;
    --bs-link-hover-color-rgb: 0, 86, 146;
}

[data-bs-theme="dark"] {
    --bs-link-color: #73aed7;
    --bs-link-color-rgb: 115, 174, 215;
    --bs-link-hover-color: #91bfe0;
    --bs-link-hover-color-rgb: 145, 191, 224;
}

/* ---- Palette: a canvas below the surface, and the guide's blue as primary (#6835) ---------------- */
/* THE CURATOR'S DECISION, 3 October 2026, on options A-D: "approve A & C. However it doesn't work with Dark
   mode!" So A and C hold in BOTH themes, and this block gives each a dark form. B and D were not taken.

   A — THE CANVAS. --bs-body-bg stays the SURFACE: cards, inputs, menus, modals and popovers take it, so
   nothing that reads it changes. One new token, --rd-canvas, paints body and nothing else. Before this the
   page and its cards were one colour in each theme (#ffffff, #212529) and only a 1px line told them apart.
   The step is the same in both themes, 1.10:1, and in dark the canvas goes BELOW the surface, because in a
   dark theme the raised surface is the lighter one. #f3f4f6 is the value the curator's preview used, a
   choice made by eye at 1366x768 (wc-ops' #f9fafb is 1.05:1 against white, too faint to read as a step).
   NOT !important: print.css sets `body { background: #fff !important; }`, and paper must stay white.

   THE TOP BAR (#6861). The curator's answer, 4 October 2026, to a table of five bar colours, the bar
   disappearing into a light browser's white toolbar at 1.00:1: "Agree 3. Navy". The bar is #002b49 in BOTH
   themes, the darkest shade of the approved blue (shade 60 of #006bb7, the light --bs-primary-text-emphasis).
   It is its own fixed token, --rd-topbar, because that emphasis token is #73aed7 in dark. The <nav> carries
   data-bs-theme="dark", so everything in it, and the menus that open from it, take Bootstrap's dark tokens in
   both themes: the curator's build decision, 4 October 2026, "(a) Dark menus." The bar's bottom border is
   the bar's colour, so no line shows between bar and page, and its height, and so the sticky offsets that
   clear it, do not change. print.css hides nav.navbar.

   C — THE PRIMARY. Bootstrap's fill becomes the guide's #006bb7, one fill in both themes as Bootstrap keeps
   one. The subtle family is re-derived by Bootstrap's own rules, verified by reproducing #052c65, #cfe2ff,
   #9ec5fe, #031633 and #084298 exactly from #0d6efd: text-emphasis shade 60 (light) and, in dark, the
   #5755 link tone (tint 45); bg-subtle tint 80 / shade 80; border-subtle tint 60 / shade 40. The solid
   button's hover and active fills are shade 20 and shade 25, the values the issue approved (#005692 is also
   the light link hover). Two more follow Bootstrap's rules for focus: the input focus border is tint 50
   (Bootstrap's #86b7fe from #0d6efd), and a solid button's focus shadow mixes in 15% white.

   SETTING --bs-primary ALONE CHANGES NO BUTTON. Bootstrap compiles #0d6efd into each component's own
   variables, the trap #5755 recorded for links. So the component rules below the tokens set those
   variables. Which components, and why no others, is under "Where the stock blue could still reach a page".

   THE GUIDE'S BLUE HAS TWO TONES, AND WHICH ONE A RULE TAKES DEPENDS ON WHAT IT DRAWS (#6844 review).
   - A FILL that carries white text is the primary, #006bb7, in both themes: a solid button, a checked box,
     a filled badge. White on it is 5.55:1.
   - A LINE OR A MARK that the eye must find against the page is the link tone, --bs-link-color: #006bb7 in
     light, where it is the primary, and #73aed7 in dark. That covers text, a state border, a focus
     outline, a bar and a stroke. In dark #006bb7 is 2.78:1 against the surface, under WCAG 1.4.11's 3:1
     for a state or focus cue, where #0d6efd was 3.43:1. #73aed7 is 6.44:1 there and 7.07:1 on the canvas.
   So no rule in RiskCheck's own CSS draws var(--bs-primary) as a line. The progress bar is a bar, so it
   reads the link tone in both themes. Bootstrap's line utilities, .text-primary, .border-primary and
   .bg-primary on a progress bar, take the link tone in dark below. PaletteConventionTests holds the
   readers of var(--bs-primary) to the fills, as an equality.
   - THE CURRENT ITEM IN DARK takes the subtle form (the curator's decision on #6844, 4 October 2026, quoted
     under "The current item in dark" below). A fill whose only job is to mark the current or selected item
     is no cue in dark at 2.78:1, so there the item is --bs-primary-bg-subtle behind
     --bs-primary-text-emphasis, with an edge in the link tone, and its text and edge carry the state.

   Measured with the WCAG 2.2 relative-luminance formula, translucent colours composited on their ground
   first. 1.4.3 asks 4.5:1 of text. The link column is in the #5755 table above.

     text on the canvas                         light #f3f4f6           dark #1a1d20
       body                                     14.02 (#212529)         13.00 (#dee2e6)
       secondary (--bs-secondary-color, 75%)     6.41                    7.84
       outline-secondary label (rule below)     12.27 (#2b2f32)          7.40 (#a7acb1)
       Bootstrap's grey #6c757d                  4.26 FAILS              3.61 FAILS   (4.69 on white)
     surface against canvas                      1.10                    1.10
     white on #006bb7: 5.55 (white on #0d6efd was 4.50); on the hover fill 7.65, on the active fill 8.37
     text-emphasis on bg-subtle                 #002b49 on #cce1f1 10.83   #73aed7 on #001525 7.73
     #006bb7 as TEXT on #cce1f1                  4.12 FAILS. Text on a primary tint takes text-emphasis,
                                                the style guide's "text on a tint is its own token".
     #006bb7 as a FILL against the dark canvas   3.05, and 2.78 on the dark surface (#0d6efd: 3.76, 3.43).
                                                Not a text pair: a button's label is the text, and 1.4.11
                                                asks 3:1 of a fill only where its edge identifies the
                                                control. Where a fill is the only cue for a state (the
                                                current item in a list), see the two tones above.
     the link tone as a LINE in dark            6.44 on the surface, 7.07 on the canvas, 4.80 on
                                                --bs-secondary-bg, its lowest ground.
     the progress bar against its track         light #006bb7 on #e9ecef 4.68; dark #73aed7 on #343a40
                                                4.80 (the #006bb7 fill was 2.07, #0d6efd 2.56)
     the current item in dark (subtle form)     text #73aed7 on the fill #001525 7.73; edge #73aed7
                                                against the surface 6.44, the canvas 7.07, the fill 7.73;
                                                the fill alone against the surface 1.20, so not the cue
     on the top bar #002b49 (#6861), both      nav link (Bootstrap's dark navbar, white at .55) 5.37; hover
     themes, its contents in dark tokens        (.75) 8.74; current page and brand (white) 14.57; firm name
                                                and user role (--bs-secondary-color) 6.87; user name 11.19;
                                                outline-secondary label #a7acb1 6.37; Ctrl K on its #2b3035
                                                chip 6.51. Non-text, 3:1: outline-secondary border #6c757d
                                                3.11; the shield (.text-primary, the link tone) 6.08; the
                                                menu toggler's lines (white at .55) 5.37
     the top bar against its neighbours        Chrome's white toolbar 14.57, the light canvas 13.24; the
                                                dark canvas 1.16 and the dark surface 1.06, where the bar is
                                                a blue band against neutral greys and its hue, not its
                                                luminance, sets it apart
     a menu that opens from the bar, both       item #dee2e6 on #212529 11.85, hovered on #2b3035 10.23,
     themes (dark tokens)                       header 7.31; current item: text #73aed7 on #001525 7.73,
                                                its ring 6.44 against the menu
     a subtle current item in light (#6855)     the #4063 toggle, the palette row, ThemeToggle: text
                                                #002b49 on the fill #cce1f1 10.83; edge #006bb7 against
                                                white 5.55, the canvas 5.04, the fill 4.12; the fill
                                                alone against white 1.35, and the old edge #99c4e2 1.85

   Re-measure, don't re-guess, if a surface colour moves. PaletteConventionTests holds the tokens and these
   ratios. */
:root,
[data-bs-theme="light"] {
    --rd-canvas: #f3f4f6;
    --rd-topbar: #002b49;
    --bs-primary: #006bb7;
    --bs-primary-rgb: 0, 107, 183;
    --bs-primary-text-emphasis: #002b49;
    --bs-primary-bg-subtle: #cce1f1;
    --bs-primary-border-subtle: #99c4e2;
    --bs-focus-ring-color: rgba(0, 107, 183, 0.25);
    --rd-primary-hover: #005692;
    --rd-primary-active: #005089;
    --rd-primary-focus-shadow-rgb: 38, 129, 194;
    --rd-focus-border: #80b5db;
}

[data-bs-theme="dark"] {
    --rd-canvas: #1a1d20;
    --bs-primary-text-emphasis: #73aed7;
    --bs-primary-bg-subtle: #001525;
    --bs-primary-border-subtle: #00406e;
}

body {
    background-color: var(--rd-canvas);
}

/* The top bar (#6861): navy in both themes, under "THE TOP BAR" above. The border is drawn here, not by
   .border-bottom, so it takes the bar's colour without !important; it is 1px, as that utility's was, so the
   bar's height is unchanged. */
.rd-topbar {
    background-color: var(--rd-topbar);
    border-bottom: var(--bs-border-width) solid var(--rd-topbar);
}

/* WHERE THE STOCK BLUE COULD STILL REACH A PAGE. Enumerated from Bootstrap 5.3.8's shipped stylesheet, the
   version App.razor links, rather than recalled: every declaration carrying #0d6efd, its rgb triple, or a
   tint or shade of it (#0a58ca, #0b5ed7, #0a53be, #86b7fe, #b6d4fe, 49,132,253). Each one RiskCheck can
   render is set below. The ones it cannot render are left alone, with the reason, so a new use knows to
   come here: .form-range (no use), an indeterminate checkbox (nothing sets .indeterminate), .link-primary
   (no use), .table-primary (no use), .dropdown-menu-dark (no use), .accordion (no .accordion-button), and
   .focus-ring, which reads --bs-focus-ring-color above (no use). --bs-blue is read by no rule. Platform.UI's
   own #0d6efd literals are in components RiskCheck does not render (CaseTimeline, CaseMessageList,
   SystemInfoCard, SearchFilterBar, DataTable). A Bootstrap upgrade re-runs this enumeration. */

/* Focus first, so a checked box keeps its checked border while focused, as in Bootstrap's own order. */
.form-control:focus,
.form-select:focus,
.form-check-input:focus {
    border-color: var(--rd-focus-border);
    box-shadow: 0 0 0 0.25rem var(--bs-focus-ring-color);
}

/* A data URI cannot read a token, so the switch knob's focus colour is the one hex outside the token
   blocks: --rd-focus-border, URL-encoded.
   ONLY WHILE UNCHECKED (#6844 review). Bootstrap writes the knob's focus rule before its checked rule, at
   equal specificity, so a focused, checked switch keeps the white knob. This stylesheet loads after
   Bootstrap's, so a plain :focus here would win on a checked switch too and paint the pale knob on the
   blue track: #80b5db on #006bb7 is 2.52:1, where the white knob is 5.55:1. Clicking a switch on leaves
   it focused, so that is the state a reader sees right after switching. */
.form-switch .form-check-input:focus:not(:checked) {
    --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2380b5db'/%3e%3c/svg%3e");
}

.form-check-input:checked {
    background-color: var(--bs-primary);
    border-color: var(--bs-primary);
}

.btn-primary {
    --bs-btn-bg: var(--bs-primary);
    --bs-btn-border-color: var(--bs-primary);
    --bs-btn-hover-bg: var(--rd-primary-hover);
    --bs-btn-hover-border-color: var(--rd-primary-hover);
    --bs-btn-focus-shadow-rgb: var(--rd-primary-focus-shadow-rgb);
    --bs-btn-active-bg: var(--rd-primary-active);
    --bs-btn-active-border-color: var(--rd-primary-active);
    --bs-btn-disabled-bg: var(--bs-primary);
    --bs-btn-disabled-border-color: var(--bs-primary);
}

/* The outline button's label and border are text on the page, so they take the guide's blue AS TEXT, which
   is the link tone: #006bb7 in light and #73aed7 in dark, measured on every ground in the #5755 table.
   #006bb7 as text on the dark surface would be 2.78:1. Hover and active fill with the primary under white. */
.btn-outline-primary {
    --bs-btn-color: var(--bs-link-color);
    --bs-btn-border-color: var(--bs-link-color);
    --bs-btn-hover-bg: var(--bs-primary);
    --bs-btn-hover-border-color: var(--bs-primary);
    --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
    --bs-btn-active-bg: var(--bs-primary);
    --bs-btn-active-border-color: var(--bs-primary);
    --bs-btn-disabled-color: var(--bs-link-color);
    --bs-btn-disabled-border-color: var(--bs-link-color);
}

.btn-link {
    --bs-btn-focus-shadow-rgb: var(--rd-primary-focus-shadow-rgb);
}

.btn-close {
    --bs-btn-close-focus-shadow: 0 0 0 0.25rem var(--bs-focus-ring-color);
}

.nav-link:focus-visible {
    box-shadow: 0 0 0 0.25rem var(--bs-focus-ring-color);
}

.nav-pills {
    --bs-nav-pills-link-active-bg: var(--bs-primary);
}

.list-group {
    --bs-list-group-active-bg: var(--bs-primary);
    --bs-list-group-active-border-color: var(--bs-primary);
}

.pagination {
    --bs-pagination-focus-box-shadow: 0 0 0 0.25rem var(--bs-focus-ring-color);
    --bs-pagination-active-bg: var(--bs-primary);
    --bs-pagination-active-border-color: var(--bs-primary);
}

.dropdown-menu {
    --bs-dropdown-link-active-bg: var(--bs-primary);
}

/* The bar is a LINE found against its track, so it takes the link tone (#6844 review): #006bb7 in light,
   4.68:1 on the #e9ecef track, and #73aed7 in dark, 4.80:1 on #343a40. As the primary fill the dark bar was
   2.07:1. No bar in the primary carries text. A bar marked .bg-primary is the dark rule below. */
.progress,
.progress-stacked {
    --bs-progress-bar-bg: var(--bs-link-color);
}

/* .text-primary as TEXT in dark mode takes the link tone, as the outline button's label does above.
   Bootstrap paints it from --bs-primary-rgb in both themes, so in dark it was #0d6efd on #212529, 3.43:1,
   already under AA, and the guide's darker blue would make it 2.78:1. With this it is #73aed7: 6.44:1 on
   the surface, 7.07:1 on the canvas. Light keeps the primary, 5.55:1 on white and 5.04:1 on the canvas.
   Its uses are labels and headings (the reports hub's "Open" lines, the gap page's heading) and icons.
   .border-primary is the same case as a LINE (#6844 review): the pressed filter tile, the active stage
   card, the next getting-started step, the current relevance decision and the open assessment are told
   apart by that border alone, and #006bb7 is 2.78:1 against the dark surface, under 1.4.11's 3:1. */
[data-bs-theme="dark"] .text-primary {
    color: rgba(var(--bs-link-color-rgb), var(--bs-text-opacity)) !important;
}

[data-bs-theme="dark"] .border-primary {
    border-color: rgba(var(--bs-link-color-rgb), var(--bs-border-opacity)) !important;
}

/* A progress bar that marks its unfinished state with .bg-primary (PhaseEvidenceStrip, the register's rows,
   AssessmentDashboard's stage bars) is painted by Bootstrap's !important utility, not by --bs-progress-bar-bg,
   so in dark it takes the link tone here, as the bar's own variable does above. */
[data-bs-theme="dark"] .progress-bar.bg-primary {
    background-color: rgba(var(--bs-link-color-rgb), var(--bs-bg-opacity)) !important;
}

/* ---- Danger and success as text in dark (#6855) -------------------------------------------------------
   THE DECISION, 4 October 2026, quoted with the question as put to the curator. "#6855's fix changes how red
   and green text and some state cues look in dark mode. Do you approve the fix direction on the issue as the
   design, or should the writer post a design for approval first?" — "Approve as written." Its first part:
   text-danger and text-success used as text take their -emphasis tones in dark, by one rule here.

   WHY. Bootstrap paints .text-danger and .text-success from --bs-danger-rgb and --bs-success-rgb in both
   themes, so in dark they are the fill tones, #dc3545 and #198754: 3.41 and 3.40:1 on the dark surface,
   under 1.4.3's 4.5:1 for text. Their -emphasis tones are Bootstrap's own dark text tones for the role,
   #ea868f and #75b798, and the move is the one .text-primary takes above and #4661 took for muted text.
   .link-danger is the same tone on a link (RiskAssessmentHub's violation list), so it is the same rule.
   .invalid-feedback needs nothing: Bootstrap paints it from --bs-form-invalid-color, already #ea868f in dark.

     dark, WCAG 2.2                     #dc3545 / #198754 (before)    #ea868f / #75b798 (now)
       on the surface #212529              3.41 / 3.40                   6.10 / 6.60
       on the canvas #1a1d20               3.74 / 3.74                   6.69 / 7.24
       on a card's header or footer        3.15 / 3.15                   5.64 / 6.10
       on --bs-tertiary-bg #2b3035         2.94 / 2.94                   5.27 / 5.70
       on --bs-secondary-bg #343a40        2.54 / 2.54                   4.55 / 4.92
       on the current menu item #001525    4.09 / 4.09                   7.32 / 7.92

   TEXT ONLY: AN ICON WITH ITS OWN CLASS, AND LARGE TEXT, KEEP THE FILL TONE. 1.4.11 asks 3:1 of an icon and
   1.4.3 asks 3:1 of large text, and the fill tones clear both on the surface and the canvas. #6844 kept its
   band glyphs on the role colour by giving the glyph its own class (EvidenceFindingPresentation.OutcomeBand.
   IconClass). An icon that carries the class on the glyph itself is a .bi element, so :not(.bi) leaves it
   alone. An icon that takes its colour from a red or green element holding text follows that text: a glyph
   inline in a run of words, as it always has, and one block-level icon, TaskList's "Nothing outstanding"
   check, which is #75b798 in dark (6.60:1 on the surface, where it was #198754, 3.40:1). Large text is
   Bootstrap's three heading sizes, fs-1 to fs-3 (ManagementReport's residual figures, fs-3 bold on their
   role's tint, 3.96 and 3.99:1 in dark). RoleToneTextConventionTests holds that a control whose only content
   is a glyph carries the class on the glyph, which is where this rule can see it.

   The rule reads the -emphasis token, so it follows the nearest theme. That matters on one ground that does
   not follow the theme: Bootstrap 5.3 paints .table-danger #f8d7da with black text in BOTH themes, so the
   invalid row of PersonnelImport and OrgStructureImport carries data-bs-theme="light", and its error text
   takes the light emphasis tone, #58151c, 10.22:1 there in both themes. The dark tone would be 1.89:1 on
   that pink. Light does not change. */
[data-bs-theme="dark"] .text-danger:not(.bi):not(.fs-1):not(.fs-2):not(.fs-3),
[data-bs-theme="dark"] .link-danger:not(.bi):not(.fs-1):not(.fs-2):not(.fs-3) {
    color: var(--bs-danger-text-emphasis) !important;
}

[data-bs-theme="dark"] .link-danger:not(.bi):not(.fs-1):not(.fs-2):not(.fs-3) {
    text-decoration-color: var(--bs-danger-text-emphasis) !important;
}

[data-bs-theme="dark"] .text-success:not(.bi):not(.fs-1):not(.fs-2):not(.fs-3) {
    color: var(--bs-success-text-emphasis) !important;
}

/* ---- The current item in dark (#6844, the curator's decision 1) ---------------------------------------
   THE DECISION, 4 October 2026, quoted with the question as put to him. "#6844, dark mode: the current item
   in each section rail is filled with the new blue, at 2.78:1 against the dark surface (WCAG 1.4.11 asks
   3:1). Which form should the current item take in dark?" — "Subtle form."

   So in dark a current or selected item is Bootstrap's subtle form: --bs-primary-bg-subtle behind
   --bs-primary-text-emphasis, with an edge in the link tone. Its text and its edge carry the state, and the
   fill does not have to. Light keeps the filled form, white on #006bb7.

     dark, WCAG 2.2                             ratio   needs
       text #73aed7 on the fill #001525         7.73    4.5 (1.4.3)
       edge #73aed7 against the surface         6.44    3 (1.4.11); 7.07 on the canvas, 7.73 on the fill
       the fill alone against the surface       1.20    not the cue
       the filled form it replaces, #006bb7     2.78    under 3:1, the failure this removes

   WHERE IT REACHES. The two components Bootstrap fills with the primary for the current item that RiskCheck
   renders. The list group: the four section rails (FrameworkSubNav, CatalogueSubNav, ReportsSubNav,
   AssessmentStageNav), the register's rail, and the selected rows of ConditionEscalationReview,
   AuthoringRunMonitor and Settings. The dropdown menu: the navbar's More and theme menus, HelpMenu and
   FilterDropdown. A solid .btn-primary that marks the selected button of a toggle group, which says so
   with aria-pressed="true", or aria-current on a link. And the estate's own subtle toggle, which needs only
   its edge (the last rule here). The pill nav and pagination are filled the same way, and nothing renders
   them, so PaletteConventionTests holds them unused until they get this form too.

   THE EDGE. A bordered list group draws a border round its current item, and a button has a border, so
   there the border is the edge. A flush list group draws only a line under each item, and a dropdown item
   draws none, so they take a 1px ring inset in the box. The flush item's own lines take the fill's colour,
   so its ring reads as one line on every side. The ring is a box-shadow, so it leaves the outline free:
   the browser's keyboard focus ring still draws outside the item, as it does in light. */
[data-bs-theme="dark"] .list-group {
    --bs-list-group-active-color: var(--bs-primary-text-emphasis);
    --bs-list-group-active-bg: var(--bs-primary-bg-subtle);
    --bs-list-group-active-border-color: var(--bs-link-color);
}

[data-bs-theme="dark"] .list-group-flush > .list-group-item.active {
    border-color: var(--bs-list-group-active-bg);
    box-shadow: inset 0 0 0 1px var(--bs-list-group-active-border-color);
}

[data-bs-theme="dark"] .dropdown-menu {
    --bs-dropdown-link-active-color: var(--bs-primary-text-emphasis);
    --bs-dropdown-link-active-bg: var(--bs-primary-bg-subtle);
}

[data-bs-theme="dark"] .dropdown-item.active,
[data-bs-theme="dark"] .dropdown-item:active {
    box-shadow: inset 0 0 0 1px var(--bs-link-color);
}

[data-bs-theme="dark"] .btn-primary[aria-pressed="true"],
[data-bs-theme="dark"] .btn-primary[aria-current] {
    --bs-btn-color: var(--bs-primary-text-emphasis);
    --bs-btn-bg: var(--bs-primary-bg-subtle);
    --bs-btn-border-color: var(--bs-link-color);
    --bs-btn-hover-color: var(--bs-primary-text-emphasis);
    --bs-btn-hover-bg: var(--bs-primary-bg-subtle);
    --bs-btn-hover-border-color: var(--bs-link-hover-color);
    --bs-btn-active-color: var(--bs-primary-text-emphasis);
    --bs-btn-active-bg: var(--bs-primary-bg-subtle);
    --bs-btn-active-border-color: var(--bs-link-hover-color);
    --bs-btn-disabled-color: var(--bs-primary-text-emphasis);
    --bs-btn-disabled-bg: var(--bs-primary-bg-subtle);
    --bs-btn-disabled-border-color: var(--bs-link-color);
}

/* The estate's own subtle toggle, a .btn given bg-primary-subtle, text-primary-emphasis and
   border-primary-subtle while it is set (#4063, "not dark blue"): FilterDropdown, FilterMultiSelect and the
   register's work filter. It is the subtle form already, and its edge was the border-subtle tone: #00406e in
   dark, 1.44:1 against the surface (Bootstrap's #084298 was 1.65:1), and #99c4e2 in light, 1.85:1 against
   white with its fill at 1.35:1 (Bootstrap's were 1.77 and 1.31). The edge is a line, so it is the link tone
   in BOTH themes: in dark by #6844, and in light by #6855's part 4 (the curator's "Approve as written."),
   under the two-tone rule above. Dark 6.44:1 against the surface, 7.73:1 against its fill; light 5.55:1
   against white, 5.04:1 against the canvas, 4.12:1 against its fill. The utility is !important, so this is.

   Two more current items take the subtle form with this edge, in their own components' styles, because
   their fill alone was the cue: the command palette's current row (CommandPaletteDialog) and Platform's
   ThemeToggle current option, which RiskCheck renders in the avatar menu (#6855 part 3). Both read these
   tokens, so their values stay here. */
.btn.border-primary-subtle {
    border-color: var(--bs-link-color) !important;
}

/* A LIGHT BADGE IS A SURFACE CHIP (#6844 review, the issue's item 3 applied to chips). Bootstrap's light
   chip, .text-bg-light or .bg-light on a .badge, is #f8f9fa: 1.05:1 against white, where its border was
   always what drew it, and 1.04:1 against the canvas, where it would read as a hole in the page rather
   than a chip on it. So in light every light badge takes the surface colour, as a panel on the canvas
   does, and keeps its border and its text colour. This is one rule rather than an edit at each chip
   because the chips that sit on the canvas take their class from markup, from shared components
   (CountryMark, ExternalGovernanceNote) and from C# helpers (StageStateDisplay.BadgeClass on nine hub
   pages, and the document, finding and firm-term badges), and the same helpers paint chips inside cards.
   It comes BEFORE the dark block below, at the same specificity, so in dark the remap there wins and the
   chip keeps --bs-secondary-bg, #343a40, which is 1.47:1 against the dark canvas. Panels that are not
   badges (a .bg-light div inside a card) keep their tint. PaletteConventionTests holds the rule and its
   order. */
.badge.text-bg-light,
.badge.bg-light {
    background-color: var(--bs-body-bg) !important;
}

/* DARK THEME HAS NO LIGHT PATCHES (curator, 23 Sep 2026: "dark theme should be a dark theme fully").
   Bootstrap 5.3's light utilities are NOT theme-aware — .text-bg-light, .bg-light and .table-light paint
   the light palette in both themes — and the estate uses them as its neutral chip and panel: 203
   .text-bg-light, 37 .bg-light (most beside .text-dark), 5 .table-light. So in dark mode every neutral
   badge was a near-white pill on a dark page. They are remapped here, once, to the theme's own surface
   and text tokens, rather than rewritten across 76 files — and IconConsistency-style, the census is
   DarkThemeConventionTests: every light-only utility the UI uses must have a rule in this block.

   Deliberately NOT remapped: .text-dark on its own. Its uses sit on fixed-colour fills
   (.text-bg-warning / .text-bg-info — yellow and cyan in both themes), where dark text is correct.
   Only .text-dark riding on a remapped .bg-light moves, because that pairing is the light chip. */
[data-bs-theme="dark"] .text-bg-light {
    color: var(--bs-emphasis-color) !important;
    background-color: var(--bs-secondary-bg) !important;
}

[data-bs-theme="dark"] .bg-light {
    background-color: var(--bs-secondary-bg) !important;
}

[data-bs-theme="dark"] .bg-light.text-dark {
    color: var(--bs-emphasis-color) !important;
}

[data-bs-theme="dark"] .table-light {
    --bs-table-color: var(--bs-emphasis-color);
    --bs-table-bg: var(--bs-secondary-bg);
    --bs-table-border-color: var(--bs-border-color);
    --bs-table-striped-bg: var(--bs-tertiary-bg);
    --bs-table-striped-color: var(--bs-emphasis-color);
    --bs-table-active-bg: var(--bs-tertiary-bg);
    --bs-table-active-color: var(--bs-emphasis-color);
    --bs-table-hover-bg: var(--bs-tertiary-bg);
    --bs-table-hover-color: var(--bs-emphasis-color);
    color: var(--bs-table-color);
    border-color: var(--bs-table-border-color);
}

/* HOVER THICKENS THE UNDERLINE (curator's decision on #5755). GOV.UK's idiom, and GOV.UK is the
   design system UI_INTERACTION_GUIDE.md already cites for links — so this is adoption, not a
   preference distilled locally. Not a colour shift alone, because colour alone is what WCAG §1.4.1
   is about and adds nothing a screen already carrying an underline does not have; and not
   suppressing the rest underline so hover can restore it, because I10.6 enumerates the suppression
   contexts and body copy is none of them.

   THE SAME WEIGHT WHETHER THE UNDERLINE WAS THERE AT REST OR RESTORED BY THE RULE ABOVE. Without
   this a suppressed anchor and a body anchor hover differently — two idioms for one state, which is
   what I10.6 exists to end. Hence both selectors, and the !important to match the restoring rule's.

   THE VALUES ARE MEASURED. Thickness stays in rem so the thickening reads equally at every size the
   product uses -- 2px against Bootstrap's ~1px auto. The OFFSET is in em, and that is the part a
   first attempt got wrong: at 0.1875rem the underline sat 3px below the baseline at every size while
   16px body text has 4px of descender ink, so it CUT THROUGH the descenders of p, y, g, j and q at
   the largest size and merely touched them at 14, 13 and 12. Measured per size against the font's
   actual ink (canvas actualBoundingBoxDescent), 0.3em is the smallest offset that clears every size
   in the scale -- gaps 0.8 / 1.2 / 0.9 / 0.6 / 1.36px at 16, 14, 13, 12 and 11.2px. Smaller touches;
   larger pushes the rule away from the text it belongs to. */
a:hover,
a:focus-visible,
a.text-decoration-none:hover,
a.text-decoration-none:focus,
a.rd-record-link:hover,
a.rd-record-link:focus {
    text-decoration-thickness: 0.125rem;
    text-underline-offset: 0.3em;
}
/* ---- Reduced motion (#5855 / I13.7) ------------------------------------ */
/* One home. Bootstrap stills its own transitions under prefers-reduced-motion (26 blocks in 5.3);
   this block extends the same respect to everything the product adds, wherever it is written (this
   stylesheet, scoped razor.css, inline <style> blocks, element.style set from script) — the two 2.5s
   fades (.rd-entity-focus, .rd-row-flash), the chevron and skip-link transitions, the walkthrough
   spotlight and ring, the JS-applied row highlight —
   and to any transition or animation a later rule introduces, which is why it is written over the
   universal selector rather than as a list that has to be kept. Durations are near-zero rather than
   `none` so `animationend`/`transitionend` still fire and nothing that waits on them hangs.
   Programmatic smooth scrolling is not CSS: window.rdScroll (App.razor) reads the same media query.
   MotionConventionTests holds this block — once, both durations, scroll-behavior — and pins every
   scrollIntoView call site to the helper. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* ---- Page scroll root ------------------------------------------------- */
/* Reserve the vertical-scrollbar lane on every page (#3113) so centred content
   never shifts left when a page happens to scroll — the gutter is identical
   whether or not the page has a scrollbar. */
html {
    scrollbar-gutter: stable;
}

/* ---- Keyboard shortcut hint (#4808) ------------------------------------ */
/* A <kbd> is a whisper, not a shout: Bootstrap's default renders an inverse block
   (body-colour background, body-bg text) — a near-black slab that dominated the top
   bar's Ctrl K hint. One quiet bordered token for every shortcut hint in the app
   (top bar, command palette, shortcuts help), in the same voice as .rd-entity-id,
   themed by --bs-* tokens in both colour modes. The hint stays; only its volume drops. */
kbd {
    background-color: var(--bs-tertiary-bg);
    color: var(--bs-secondary-color);
    border: 1px solid var(--bs-border-color);
    border-radius: 0.25rem;
    padding: 0.1rem 0.35rem;
    box-shadow: none;
}

/* ---- Live-link highlight (#4828) --------------------------------------- */
/* Debug-mode chrome: which internal links are live in the export, outlined ON the page in the
   capture graph's own colour language (success = live, warning = selected-not-captured-yet).
   Outline, never border or padding — the highlight must not shift layout. The data-rd-live
   attribute is stripped from every capture clone, so these rules ship in the exported stylesheet
   but can never act in an export. */
a[data-rd-live="live"] {
    outline: 2px solid var(--bs-success);
    outline-offset: 2px;
}

a[data-rd-live="pending"] {
    outline: 2px dashed var(--bs-warning);
    outline-offset: 2px;
}

/* ---- Entity id chip (#3126) ------------------------------------------- */
/* One design language for entity identifiers (risk / control / obligation / OU /
   noncompliance-mode / suggestion). Neutral monospace chip with an EXPLICIT size so it
   never inherits a heading, and a secondary colour — never Bootstrap's <code> pink
   (--bs-code-color #d63384), which reads as danger on a plain identifier. Built on
   --bs-* so it follows the colour mode. Reserve <code> for actual code / paths.
   Ids are never truncated (a clipped id loses its meaning): a long id (control ids
   reach ~90 chars) wraps instead of forcing horizontal scroll, and box-decoration-break
   keeps the chip chrome whole on each wrapped line rather than splitting the border. */
.rd-entity-id {
    font-family: var(--bs-font-monospace);
    font-size: 0.8125rem;
    font-weight: 400;
    color: var(--bs-secondary-color);
    background-color: var(--bs-tertiary-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: 0.25rem;
    padding: 0.05rem 0.35rem;
    overflow-wrap: anywhere;
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
}

/* #5547 / I10.6 — no link is identifiable by colour alone in its interactive states. 194 anchors
   suppress the underline at rest (122 when this rule was written; #5755's count, and growth since —
   legitimately or not, the at-rest adjudication is the #5313 sweep's); this single cascade-level rule
   restores it on hover and focus for all of them, so no markup opt-out can drop the non-colour cue
   (WCAG 2.2 §1.4.1, G183). !important because Bootstrap's .text-decoration-none utility is
   !important; equal importance at higher specificity, later in the cascade, wins.
   LinkAffordanceConventionTests holds this rule — once, both pseudo-classes, the !important — and pins
   the class-based suppressions below that it cannot reach (#5848). #6693 — it restores the register title
   link's suppression too (a.rd-record-link, below), so both kinds of suppressed link come back from one rule. */
a.text-decoration-none:hover,
a.text-decoration-none:focus,
a.rd-record-link:hover,
a.rd-record-link:focus {
    text-decoration: underline !important;
}

/* #6693 — the register title link (the design's K1). A row whose subject has a page of its own links to it
   from its title, and that is the row's one link. I10.6 names this position as one that carries the affordance
   itself: "a table or card whose row or title position is the established link idiom in that pattern, wherever
   it renders", adjudicated on #6496 for a register's title link alone in its cell (Appendix C, I10.6). So the
   at-rest suppression is this one class, decided once, instead of text-decoration-none claimed title by title,
   and it returns on hover and focus through the rule above. 500 is the title's one weight on every register.
   RecordLinkConventionTests pins where the class may sit: a register row's identity cell, and nowhere else,
   because on a link in running text it would be #6496's defect back through an approved door. */
a.rd-record-link {
    text-decoration: none;
    font-weight: 500;
}

a.rd-entity-id {
    text-decoration: none;
}

a.rd-entity-id:hover,
a.rd-entity-id:focus-visible {
    color: var(--bs-body-color);
    background-color: var(--bs-secondary-bg);
    border-color: var(--bs-secondary-color);
}

/* #3783 P2 — brief ring drawn on an entity chip when a chat deep-link spotlights it
   (rdFocusEntity adds/removes .rd-entity-focus). --bs-* so it reads in light + dark. */
.rd-entity-focus {
    outline: 2px solid var(--bs-warning);
    outline-offset: 2px;
    animation: rd-entity-focus-fade 2.5s ease-out;
}

@keyframes rd-entity-focus-fade {
    0%, 55% { box-shadow: 0 0 0 0.25rem var(--bs-warning-bg-subtle); }
    100% { box-shadow: 0 0 0 0.25rem transparent; }
}

/* #6507 / #4819 — the target a deep-link presented keeps its mark after the ring fades, until another
   target is presented (rdFocusEntity moves it; aria-current carries the same fact for assistive tech).
   A table row takes Bootstrap 5.3's own row-state variable, so the tint covers every cell and follows the
   colour mode; anything else takes a quiet outline. No motion, so nothing for reduced motion to settle.
   The row's bar is a line, so it takes the link tone (#6844 review, "two tones" under Palette): in dark
   #73aed7 is 7.73:1 on the row's tint and 6.44:1 on the surface, where #006bb7 was 2.78:1. */
tr.rd-entity-target {
    --bs-table-bg-state: var(--bs-primary-bg-subtle);
}
tr.rd-entity-target > :first-child {
    box-shadow: inset 3px 0 0 var(--bs-link-color), inset 0 0 0 9999px var(--bs-primary-bg-subtle);
}
.rd-entity-target:not(tr) {
    outline: 2px solid var(--bs-primary-border-subtle);
    outline-offset: 2px;
}
/* In dark the outline is a line, so it takes the link tone (the curator's decision on #6844, 4 October 2026).
   The question as put to him: "#6844 makes the quiet outline on a linked row (the deep-link target) fainter
   in dark: 1.65:1 before, 1.44:1 now. Screen readers already announce the row as current (aria-current).
   Accept it, or fix it in this PR?" His answer: "Fix it in this PR." #00406e, the dark border-subtle tone,
   is 1.44:1 against the surface and 1.58:1 against the canvas. #73aed7 is 6.44:1 and 7.07:1. The token
   keeps its value, because chips and panels read it as a border and not as a state. Light keeps its quiet
   #99c4e2 outline (1.85:1 on white, Bootstrap's was 1.77:1), which the decision does not reach. */
[data-bs-theme="dark"] .rd-entity-target:not(tr) {
    outline-color: var(--bs-link-color);
}

/* ---- Progress-bar fill ------------------------------------------------ */
/* The ISO process stepper's rules lived around this one until #6559 retired the stepper
   (the assessment rail lists the stages); nothing renders .rd-stepper or .rd-step since. */
/* Dynamic progress-bar fill — the runtime percentage rides on a CSS variable
   so the width rule lives in a class rather than an inline style (#3036). */
.rd-progress-fill {
    width: var(--rd-fill, 0%);
}

/* ---- Main column ------------------------------------------------------ */
.rd-main {
    min-width: 0; /* let flex/grid children shrink instead of overflowing */
}

/* ---- Lead paragraph ---------------------------------------------------- */
/* Caps the page-intro lead at a comfortable measure, matching the style
   guide's .sg-lead. Pairs with Bootstrap's .lead on RdPageHeader. */
.rd-lead {
    max-width: var(--reading-measure-lead);
}

/* ---- Record prose ------------------------------------------------------ */
/* #4849 — the reading measure the density guide has always stated (60-75 characters per line)
   applied to body text inside a record: the rationale, the requirement, a citation quote, a
   shortfall, an analysis note. Measured before this: ~174 characters per line.

   The measure itself is --reading-measure, set once at the top of this sheet. */
.rd-prose { max-width: var(--reading-measure); }
/* #6361 F1 — a definition list whose values are sentences: the catalogue record pages (an obligation's
   text, a mode's rationale, an exemption's condition). The value column is a body-text container in
   practice, so it wears the measure rather than the grid column's 75% of the card — measured before
   this at up to 155 characters a line. The term column keeps its grid width. */
.rd-record-dl > dd { max-width: var(--reading-measure); }
/* #6495 — the measure above broke the pairing it sat inside. The list is a Bootstrap .row, a flex row
   that pairs a term with its value only because col-md-3 and col-md-9 add up to the line: once the value
   stopped at the measure there was room left on the line, the next dt moved up beside it, and from the
   second pair on every label sat beside the wrong value. A flex row has no way to tie a dd to its dt
   (I16.3: the framework's answer is widths that add up, and the cap is what removes it), so from
   Bootstrap's sm breakpoint the list is a grid and each dt and dd is PLACED in its column. The pairing on
   screen is then by structure, as the dl already makes it programmatic (WCAG 2.2 §1.3.1, §1.3.2),
   whatever width either has.
   - The shares are the markup's own: 1/3 + 2/3 from sm, 1/4 + 3/4 from md, at Bootstrap's 576 and 768px.
     So above sm the col-sm-4/col-md-3 and col-sm-8/col-md-9 classes no longer decide the share; the grid
     does, with the same numbers. They stay as the label-and-value pattern's vocabulary
     (UI_DENSITY_GUIDE.md §13.3), and below sm the stacked layout is still theirs.
   - width: auto, because in a grid a percentage width resolves against the cell, and col-md-3's 25% would
     shrink each term to a quarter of its own column. .rd-record-dl > dt (0,1,1) outranks .col-md-3 and
     .row > * (0,1,0), so no !important.
   - The row's negative margins and each cell's gutter padding are kept, so the record card spaces its
     pairs as the other dl.row lists do.
   - Child combinators only: a SeverityKey or RdTermKey inside a value keeps its own dl and its own rules.
   Measured on the four catalogue record pages at 1440 and 1366: every pair on one row, where before only
   each list's first pair was; list heights unchanged. */
@media (min-width: 576px) {
    .rd-record-dl { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
    .rd-record-dl > dt { grid-column: 1; width: auto; }
    .rd-record-dl > dd { grid-column: 2; width: auto; }
}
@media (min-width: 768px) {
    .rd-record-dl { grid-template-columns: minmax(0, 1fr) minmax(0, 3fr); }
}
/* ---- Alerts whose text is prose (#5077; generalised #5700) ------------- */
/* Written for the two banners that reported a whole session's state — SessionRestoreNotice (#4482), still
   here and the one wearer, and SessionSupersededBanner (#5067, retired with the whole-session sync in
   #5363, which deleted the component and left this comment naming it) — both set no measure, so their
   prose ran the full viewport.
   Measured in the browser at 1710px: a longest line of 1251px, about 179 characters, against the
   60-75 the density guide states as a must (§2) and the 74 that --reading-measure is calibrated to
   give at 14px (#4849).

   The BOX takes the measure, not the line inside it, following #4853: constraining only the text
   left a full-width tinted band with its prose stopping partway, which read as an empty slab. The
   allowance over the measure is the chrome this alert wraps around its text — the icon, the flex
   gap and the alert's own horizontal padding.

   +2rem is CALIBRATED, not derived, which is what this sheet's own header asks for ("measure
   characters, never ch"). The trap: ch resolves against the element it is set on, and .alert
   inherits the 16px body size while its prose renders at 14px inside .small — so 52ch here buys a
   wider box than 52ch would inside the text. Measured at 1710px:

     + 4rem -> 76 characters   (over the bound, by one)
     + 2rem -> 69 characters   <- chosen: mid-band, so a type-scale change does not push it over
     + 0rem -> 65 characters   (inside, but the box then hugs tighter than the prose needs)

   Re-measure rather than re-derive if the type scale moves.

   These are the surfaces a reviewer reads when something has gone wrong with their session, which
   is when comprehension matters most and they are least receptive to hunting for the return sweep.
   The banner grows from 140px to 224px at this width — the cost of a real measure is lines, and it
   is only paid in the failure case, since the banner renders nothing at all when healthy.

   #5700 (#5313 surface 5) generalised the class from the two banners to ANY alert whose text is
   prose, and renamed it for the job rather than the wearer: the management report's scope note and
   appetite alert, the register's outstanding warning and the audit body's per-risk status alerts ran
   the full card (a longest line of 188 characters on the appetite alert at 1400px). The calibration
   transfers because the +2rem is the alert's own chrome — icon, gap, padding — and not anything the
   session banners did; an alert with a different chrome gets its own measurement, not this number. */
.rd-alert-prose { max-width: calc(var(--reading-measure) + 2rem); }

/* ---- Sticky navigator rails ------------------------------------------- */
/* A navigator that sits beside the content it addresses and stays put while that content
   scrolls: the risk register's regulation tree, and (#5685) a document's own contents list.
   One rule, two selectors — the offsets that clear the sticky navbar are stated once. */
@media (min-width: 992px) {
    .rd-tree-rail,
    .rd-document-outline {
        position: sticky;
        top: 4.5rem;
        /* Cap to the viewport so the whole rail stays on screen. */
        max-height: calc(100vh - 5.5rem);
        display: flex;
        flex-direction: column;
    }
    /* At run028 scale the rail holds 40+ regulation refs — taller than the
       viewport. Scroll the ref list inside the rail (the REGULATION header
       stays pinned) so every ref is reachable without scrolling the page.
       A long document's heading list does the same. */
    .rd-tree-rail .list-group,
    .rd-document-outline > ul {
        overflow-y: auto;
    }
}

/* ---- Framework configuration-hub side rail (#3026) -------------------- */
/* Vertical section switcher for the Framework area. The horizontal pill
   strip outgrew the viewport once it reached nine sections; a vertical rail
   keeps every item visible and scales. A fixed-width sticky column on lg+,
   clearing the sticky navbar like .rd-tree-rail; below lg a drawer since
   #6559 (it stacked above the content until then). All four section rails
   wear this class, through SectionRail. */
@media (min-width: 992px) {
    .rd-framework-rail {
        flex: 0 0 15rem;
        width: 15rem;
        align-self: flex-start;
        position: sticky;
        top: 4.5rem;
        max-height: calc(100vh - 5.5rem);
        overflow-y: auto;
    }
}
/* #6559 — below 992px every section rail is a Bootstrap offcanvas-lg drawer (SectionRail), opened from a slim
   bar at the top of the content; the rule above applies from 992px only, so nothing here is needed for it.
   The assessment rail's stage marker: the circled number, or a tick once the stage is done. */
.rd-stage-mark {
    width: 1.5rem;
    height: 1.5rem;
}
/* #6665 — the assessment rail's foot, the RCSA view switch, is pinned to the bottom of whatever scrolls the
   rail: the rail itself from 992px up, the drawer's body below that. #6559 put the switch at the foot so it is
   visible on every assessment page, and a rail taller than the window hid it (about 996px shown in 680px at
   1366x768, measured on the Aurora overview with guidance on). Pinned, the stages scroll under it; scrolled to
   the end, it sits below the last entry in its own place, so it never covers that entry for good. Andrew,
   2 October 2026. The z-index is above Bootstrap's 2 for a list group's active entry, which is positioned and
   would otherwise paint over the foot as it scrolls under. */
.rd-rail-foot {
    position: sticky;
    bottom: 0;
    z-index: 3;
}
/* In the drawer the scroller is .offcanvas-body, which is padded, and a sticky box stops at its scroller's
   padding: the foot sat 16px above the drawer's bottom edge with the stages showing through below it
   (rendered at 390x600). The negative offset takes it to the edge. Not from 992px up, where the rail itself
   scrolls, has no padding, and clips: there the same offset would cut the foot off. */
@media (max-width: 991.98px) {
    .rd-rail-foot {
        bottom: calc(-1 * var(--bs-offcanvas-padding-y));
    }
}
/* #6665 review, round 2 — keyboard focus must not land under the pinned foot (WCAG 2.2 SC 2.4.11, Focus Not
   Obscured, AA, the guide's floor). Tab scrolls the focused entry just into view at the scroller's bottom edge,
   which is where the foot sits; measured, entries landed fully under it at 1366x768. Scroll padding keeps the
   foot's height plus 8px clear when the scroller scrolls for focus. The scroller is the rail from 992px up and the
   drawer's body below; on the other one the rule does nothing, because it does not scroll. --rd-rail-foot-h is the
   foot's rendered height, kept current by rdSectionRail.trackFoot (rd-interop.js). A rail with no foot never gets
   the property, so var() leaves the declaration invalid and scroll-padding-bottom at its initial auto: the other
   three rails are unchanged. */
.rd-framework-rail,
.rd-framework-rail .offcanvas-body {
    scroll-padding-bottom: calc(var(--rd-rail-foot-h) + 0.5rem);
}

/* ---- Evidence-assistant split rail (#3783) ---------------------------- */
/* P1: the assistant's "dock to side" mode. The panel becomes a fixed right rail
   (.rd-assist-rail) and the page reflows beside it — body padding-right equals the
   rail width — instead of an overlay covering the page. Width rides on the
   --rd-assist-rail dynamic var (default 420px, drag-resized in App.razor; same
   pattern as --rd-fill). Single Blazor circuit: the content pane IS the app route,
   the rail is persistent shell chrome around it — no iframe, no browser automation. */
.rd-assist-rail {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: var(--rd-assist-rail, 420px);
    z-index: 1045;
    display: flex;
    flex-direction: column;
    border: 0;
    border-left: 1px solid var(--bs-border-color);
    border-radius: 0;
}

body.rd-assist-split {
    padding-right: var(--rd-assist-rail, 420px);
}

/* Left-edge grab handle for resizing the rail (rdAssistDock wires the drag). */
.rd-assist-resize {
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    width: 6px;
    cursor: col-resize;
    z-index: 2;
}

.rd-assist-resize:hover {
    background: var(--bs-border-color);
}

/* Below lg the page is too narrow to reflow beside a rail, so the panel becomes a
   full-width overlay (like the pre-#3783 full-screen mode) and the page does not shift. */
@media (max-width: 991.98px) {
    body.rd-assist-split {
        padding-right: 0;
    }
    .rd-assist-rail {
        width: 100vw;
        border-left: 0;
    }
}

/* ---- Utilities Bootstrap 5.3 does not ship ---------------------------- */
/* min-width:0 on a flex/grid child so long content truncates instead of
   forcing the row wider (Bootstrap ships max-width but no min-width utility). */
.min-w-0 {
    min-width: 0 !important;
}

/* ---- Native <details> disclosure (obligation inventory) --------------- */
/* A flex/grid summary loses the browser's default marker, so we supply our own
   chevron and rotate it on open. Use as <details class="rd-disclose">. */
.rd-disclose > summary {
    list-style: none;
    cursor: pointer;
}
.rd-disclose > summary::-webkit-details-marker {
    display: none;
}
.rd-disclose > summary .rd-chev {
    transition: transform 0.15s ease;
}
.rd-disclose[open] > summary .rd-chev {
    transform: rotate(90deg);
}

/* ---- The ⓘ beside a page title (IsoGuidanceTag, #6559) ------------------------------------------
   A native <details>, so its content, the workflow note included, is in the DOM while it is closed
   and it opens with no script: the published working papers are DOM snapshots (#5778). The card keeps
   the look of the StandardsHelp popover it replaces and floats under the title row (RdPageHeader makes
   that row position-relative), so the page does not move when it opens. It starts at the row's left
   edge rather than at the ⓘ, so at 320px it fits the screen instead of running off its right side.
   z-index is Bootstrap's dropdown layer: an overlay anchored to a control, which the rail's drawer
   (1045) still covers. The print rules are in css/print.css. */
.rd-iso-tag {
    display: inline-block;
}
.rd-iso-tag > summary {
    list-style: none;
    cursor: pointer;
    display: inline-block;
    line-height: 1;
}
.rd-iso-tag > summary::-webkit-details-marker {
    display: none;
}
.rd-iso-tag-card {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 1000;
    width: min(30rem, calc(100vw - 2rem));
    margin-top: .25rem;
}
.rd-iso-tag-card .rd-iso-note {
    margin-top: .5rem;
    padding-top: .5rem;
    border-top: 1px solid var(--bs-border-color);
}

/* ---- Between-cards rhythm (#5313 phase 1) ------------------------------ */
/* The density guide's one between-cards value (S4: 32px), in ONE home. The markup used to carry
   it as `card mb-3` (16px) on 206 sites - a value the guide contradicted, applied one utility
   class at a time, and drifting (+29 sites in the eight days after it was measured). A Bootstrap
   spacer cannot express the guide's value (mb-4 = 24px, mb-5 = 48px), which is why it lives here
   as a rule rather than in markup as a utility: change the rhythm by changing this line.
   CardRhythmConventionTests holds the boundary - a `card` carrying `mb-3/4/5` fails the build. */
.rd-stack {
    margin-bottom: 2rem;
}

/* ---- Page-width containers -------------------------------------------- */
/* Centered page wrappers that replace the house --page-form / --page-width /
   --page-wide tokens. Use as <div class="rd-page rd-page-form|rd-page-default|rd-page-xl">.
   (rd-page-wide retired in #3113 — its three pages standardised to rd-page-default,
   so top-level section pages share one gutter; rd-page-xl stays for dense data.) */
.rd-page {
    margin-left: auto;
    margin-right: auto;
    width: 100%;
}

.rd-page-form {
    max-width: 720px;
}

.rd-page-default {
    max-width: 1100px;
}

/* Data-dense grids/tables only (assessment register, management report) — uses
   more of an FHD (1920) monitor. Not for prose/text lists, where line length matters. */
.rd-page-xl {
    max-width: 1400px;
}

/* ---- Document sheet (#6835) -------------------------------------------- */
/* A document surface reads as ONE sheet on the canvas. Until #6835 the page was the surface colour, so a
   report whose sections were partly cards and partly headings and prose read as one white page. On the
   canvas the loose parts fell onto the grey between the cards: the management report's residual position,
   decision counts and charts, and the whole of the person, audit-log and extract documents, which have no
   cards at all. The sheet goes on the element that holds the document, from its title to its end; the
   breadcrumb and the page's own export and print controls stay outside it, as chrome. Cards inside it keep
   their borders. Paper has no canvas, so print.css removes the sheet's frame. */
.rd-sheet {
    background-color: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius-lg);
    padding: 1.5rem;
}

/* The last block's own bottom margin (a card's .rd-stack, 2rem) would sit inside the sheet as dead space. */
.rd-sheet > :last-child {
    margin-bottom: 0;
}

/* ---- Skip-to-main link (WCAG 2.4.1) ----------------------------------- */
/* First focusable element on every page; visually hidden until focused. */
.skip-to-main {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 9999;
    padding: 0.5rem 0.75rem;
    background: var(--bs-primary);
    color: var(--bs-white);
    text-decoration: none;
    border-radius: 0 0 var(--bs-border-radius) 0;
    font-weight: 500;
    transform: translateY(-100%);
    transition: transform 0.15s ease;
}

.skip-to-main:focus,
.skip-to-main:focus-visible {
    transform: translateY(0);
    outline: 2px solid var(--bs-white);
    outline-offset: -4px;
}

main[id="main-content"]:focus {
    outline: none;
}

/* ---- Blazor error UI -------------------------------------------------- */
/* Survives app.css deletion. Minimal --bs-* styling for the bottom error bar
   and the unhandled-error boundary fallback. */
#blazor-error-ui {
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    z-index: 1080;
    padding: 0.6rem 1.25rem 0.7rem;
    background: var(--bs-warning-bg-subtle);
    color: var(--bs-warning-text-emphasis);
    border-top: 1px solid var(--bs-warning-border-subtle);
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    top: 0.5rem;
    right: 0.75rem;
}

.blazor-error-boundary {
    padding: 1rem 1rem 1rem 1.25rem;
    background: var(--bs-danger);
    color: var(--bs-white);
}

.blazor-error-boundary::after {
    content: "An error has occurred.";
}

/* Inline country-flag SVG (#3050) — the flag comes from the ISO 3166 registry as
   raw SVG markup, so a small global rule scales it to fit its wrapper box. */
.rd-flag {
    display: inline-block;
    line-height: 0;
}

.rd-flag svg {
    display: block;
    width: 100%;
    height: 100%;
}

/* #6329 — a country as flag + code (CountryMark), the one way a country is shown as a mark. Sized in
   em so the flag scales with the text beside it instead of the two hardcoded sizes it replaced
   (18x12px in UnitChips, 1.5x1.125em on the assessment list). */
.rd-country {
    display: inline-flex;
    align-items: center;
    gap: .45em; /* #6338 — widened from .3em: the code sat against the flag */
    white-space: nowrap;
    vertical-align: baseline;
}

.rd-country .rd-flag {
    width: 1.333em;
    height: 1em;
    flex-shrink: 0;
}

/* #6373 F10 — a control inside a badge (the intake's correspondences and confirmed gaps) is a target of at
   least 24x24 CSS px (WCAG 2.5.8). The remove cross was a btn-close at .55rem, about 13px; the glyph stays
   small and the box around it is the target. Pixels, not em: the criterion is stated in CSS pixels, and a
   badge's .75em text would shrink an em-sized box under it. */
.rd-chip-close {
    box-sizing: border-box;
    width: 24px;
    height: 24px;
    padding: 0;
    background-size: 10px;
    vertical-align: middle;
}

/* The same target for a worded control in a badge — "confirm absent", which was a .7rem link. */
.rd-chip-action {
    min-height: 24px;
    min-width: 24px;
    padding: 0 .25rem;
    font-size: .75rem;
    line-height: 24px;
    vertical-align: middle;
}

/* #6332 — a hub tile's icon square: decoration, so it carries no colour. The Framework and Catalogue hubs
   spent seven semantic colours (text-bg-success on "Control library", text-bg-danger on its neighbour) on
   navigation, while the same palette means done / attention / failed on the estate's status icons — and a
   palette that is state on one surface and decoration on another teaches the reader to stop reading it.
   One neutral, theme-aware treatment, chosen by the curator; IconConsistencyConventionTests holds it. */
.rd-tile-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: var(--bs-border-radius);
    background-color: var(--bs-secondary-bg);
    color: var(--bs-emphasis-color);
}

/* Responsibility map (#3049) — freeze the Provision column so horizontal scroll
   keeps row context. The header is already .sticky-top; the corner cell needs the
   highest z-index, and an opaque background hides cells scrolling beneath it. */
.rd-respmap th.rd-sticky-col,
.rd-respmap td.rd-sticky-col {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--bs-body-bg);
}

.rd-respmap thead th.rd-sticky-col {
    z-index: 1021;
    background: var(--bs-tertiary-bg);
}

/* Abbreviations (#4118) — a dashed underline is the long-standing convention for "hover me for the
   meaning", and it has to read in both themes, so it takes its colour from the current text rather than
   a fixed grey. text-decoration is reset because browsers default <abbr> to a dotted underline that
   disappears against small text. */
.rd-abbr {
    text-decoration: none;
    border-bottom: 1px dashed currentColor;
    cursor: help;
}

/* Inline code colour (#4661) — Bootstrap's light-mode --bs-code-color (#d63384) is 4.27:1 on white,
   under the 4.5:1 AA floor at inline-code size. The darker pink-600 clears it; the dark colour mode
   keeps Bootstrap's own (lighter) value, which already passes there. */
[data-bs-theme="light"] code,
:root:not([data-bs-theme="dark"]) code {
    --bs-code-color: #ab296a;
}

/* (A fixed-blue link class sat here, #4661: Bootstrap's blue-600 for small links on tinted card headers,
   written when every link was Bootstrap's own blue. #5755 bound the guide's link colour per theme, and from
   then on the fixed blue was the failure it had been written to prevent: 2.21:1 on the dark card header,
   where the plain link gives 5.95:1 (5.22:1 in light). #6835 removed the class and its three uses.) */

/* Register rail fractions (#4661) — text-body-secondary misses 4.5:1 on the active row's
   primary-subtle tint; the emphasis colour at 75% opacity keeps the muted look and clears AA on
   both the plain and tinted rows, in both colour modes. */
.rd-rail-fraction { color: var(--bs-emphasis-color); opacity: .75; }
/* On the ACTIVE rail row, inherit the row's text colour at full opacity. In light that row is solid
   primary with white text (5.55:1, #6835), and in dark it is the subtle form, #73aed7 on #001525 (7.73:1,
   "The current item in dark" under Palette). Any dimming spends that margin. */
.active .rd-rail-fraction { color: inherit; opacity: 1; }

/* AA muted text (#4661) — text-body-secondary (#6c757d) misses 4.5:1 on the tinted surfaces it most
   often sits on (bg-body-tertiary headers, bg-body-secondary badges). The emphasis colour dimmed by
   opacity keeps the muted look, clears AA on white and the tints, and follows the colour mode. */
.rd-text-muted-aa { color: var(--bs-emphasis-color); opacity: .78; }

/* btn-outline-warning (#4661) — Bootstrap's amber (#ffc107) is ~2:1 as text on white; the warning
   text-emphasis shade is the AA colour for exactly this. Checked/hover states keep Bootstrap's solid
   warning background with dark text, which already passes. */
.btn-outline-warning { --bs-btn-color: var(--bs-warning-text-emphasis); }

/* btn-outline-secondary, on every host (#4664 run 4, widened by #6835). Bootstrap's secondary (#6c757d)
   passes on white (4.69:1) but lands at 4.45:1 on bg-body-tertiary, a hair under the AA floor. The scope
   strip's assessment switcher sat on exactly that tint on every page, the one recurring axe offender of
   the run-4 scan, so the rule was first scoped to tinted hosts and buttons on white kept Bootstrap's
   palette. The canvas (#6835) is a tinted host too, and the page itself: #6c757d measures 4.26:1 on
   #f3f4f6 and 3.61:1 on the dark canvas, and in dark it already failed on the surface (3.29:1). So the
   rule now reaches every outline-secondary button, 223 of them in 103 files. The label is
   --bs-secondary-text-emphasis: 12.27:1 on the light canvas and 13.50:1 on white; 7.40:1 on the dark
   canvas, 6.74:1 on the dark surface and 5.03:1 on --bs-secondary-bg, its lowest ground. It follows the
   colour mode, and the hover and checked states keep Bootstrap's solid secondary fill, which passes. */
.btn-outline-secondary {
    --bs-btn-color: var(--bs-secondary-text-emphasis);
}

/* btn-outline-success and btn-outline-danger, on every host (#6835). The same mechanism as the rule above,
   found by the harness pass rather than by the issue: Bootstrap's #198754 and #dc3545 are 4.53:1 on white
   and 4.12:1 and 4.11:1 on the light canvas, so no grey canvas keeps them over AA as a label on the page. In
   dark they already failed on the surface (3.40:1 and 3.41:1) and on the canvas still do (3.74:1). The label takes the role's
   text-emphasis tone, as the warning and secondary buttons above do and as UI_STYLE_GUIDE.md's outline-button
   rule (#6008) asks of a label: light 12.20 / 12.40:1 on the canvas, dark 7.24 / 6.69:1 on the canvas and 4.92 /
   4.55:1 on --bs-secondary-bg, the lowest ground. The border keeps the role colour, which is the control's
   boundary (WCAG 1.4.11, 3:1): 4.12 and 4.11:1 on the light canvas, 3.40 and 3.41:1 on the dark surface. Hover and checked
   keep Bootstrap's solid fill under white, 4.53:1. */
.btn-outline-success {
    --bs-btn-color: var(--bs-success-text-emphasis);
}

.btn-outline-danger {
    --bs-btn-color: var(--bs-danger-text-emphasis);
}

/* ---- Two voices: document artefact vs app chrome (#4718) --------------- */
/* On cards that show BOTH an excerpt of the artefact under review AND the app
   explaining a control, the two must never dress alike (the triage card had a
   muted-small blockquote directly above a muted-small form-text — opposite
   things, one style). The pair is a scoped vocabulary so the distinction is
   enforced by class, not re-decided per card. Built on --bs-* for both themes. */
/* The artefact: quoted document content announces itself — tinted panel with a
   left accent and a micro-label ("From the document"). */
.rd-doc-excerpt {
    /* #4855 — the quote must contrast with WHAT IT SITS ON, not with the page. This was
       --bs-tertiary-bg, which is exactly the FindingReviewPanel's own bg-body-tertiary: inside the
       one surface where the two voices sit closest together, the tint cancelled out and only the
       accent showed. Verifying it against document.body rather than its container is how that
       shipped. Body-bg lifts the quote off a tertiary surface, and the 1px border keeps it
       delineated on a body-bg surface too — so it reads on either, in both colour modes. */
    background-color: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color-translucent);
    border-left: 3px solid var(--bs-secondary-border-subtle);
    border-radius: var(--bs-border-radius);
    padding: 0.5rem 0.75rem;
}

.rd-doc-excerpt .rd-voice-label {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--bs-secondary-color);
    margin-bottom: 0.15rem;
}

/* Verbatim document text keeps markdown-rendered structure compact. */
.rd-doc-excerpt blockquote p:last-child { margin-bottom: 0; }
.rd-doc-excerpt blockquote p { margin-bottom: 0.35rem; }

/* #4853 — the pattern made adoptable, via the DocumentQuote component.

   Compact: a quote nested inside an already-disclosed record, where the full panel would
   out-weigh the finding it supports. Padding drops; the voice does not change — the tint and
   the accent are what say "the document is speaking", and they stay. */
.rd-doc-excerpt-compact {
    padding: 0.35rem 0.5rem;
}

/* The locator is OUR provenance note, not part of what the document said, so it reads in the
   app's register and sits outside the quote (#4718's two voices, applied within one panel). */
.rd-doc-excerpt-source {
    font-size: 0.75rem;
    color: var(--bs-secondary-color);
    margin-top: 0.25rem;
}

/* The PANEL takes the reading measure, not just the line inside it (#4849 + #4853). Constraining
   only the line left a full-column tinted box with its text stopping at 60% — the quote read as a
   wide empty slab. The box hugs its own measure instead, plus the padding that frames it. */
.rd-doc-excerpt { max-width: calc(var(--reading-measure) + 1.5rem); }

/* #4871 — assistant-authored record prose (a rationale, a shortfall, an analysis note), rendered
   as markdown by the RecordProse component. The record grammar (#4853/#4867) means this arrives as
   headed parts and a per-document list, so the spacing has to make a list read as a list without
   the block out-weighing the finding it explains. Width comes from .rd-prose; this is rhythm only. */
.rd-record-prose > p:last-child,
.rd-record-prose > ul:last-child,
.rd-record-prose > ol:last-child { margin-bottom: 0; }

.rd-record-prose > p { margin-bottom: 0.5rem; }

.rd-record-prose > ul,
.rd-record-prose > ol {
    margin-bottom: 0.5rem;
    padding-left: 1.1rem;
}

.rd-record-prose li { margin-bottom: 0.2rem; }

/* The grammar's labelled parts ("**The duty.**") open each section — give them a little air above,
   but not on the first, or the block starts with a gap. */
.rd-record-prose > p + p > strong:first-child { display: inline-block; margin-top: 0.15rem; }

/* #4853 — the assistant's reading of a quote, in the app's register: tied to the quote it answers
   by a hanging indent rather than a second panel, so the pair reads as one unit while never
   dressing alike (#4718). The label is a word, not a colon-prefixed sentence fragment. */
.rd-shortfall {
    margin-top: 0.3rem;
    padding-left: 0.75rem;
    border-left: 2px solid var(--bs-border-color-translucent);
    font-size: 0.875rem;
}

.rd-shortfall > .text-danger-emphasis::after {
    content: " — ";
    color: var(--bs-secondary-color);
}

/* Line-height 1.4 for quoted passages — the density guide's stated allowance for blockquotes
   that already carry a left rule and indentation, which this panel does. */
.rd-doc-excerpt blockquote p { line-height: 1.4; }

/* The app's voice: a control's helper reads as chrome — tied to its control by
   a top divider and an info icon (added in markup), never as content. */
.rd-control-hint {
    border-top: 1px solid var(--bs-border-color);
    padding-top: 0.4rem;
}

/* ---- Row accent after filing (#4720) ----------------------------------- */
/* Approve & file must land visibly: the document's row flashes once (primary
   tint fading over ~2.5s) at the moment its Wiring chip changes, then rests.
   Animation-only, so no timer or cleanup — the class can stay on the row. */
.rd-row-flash > td,
.rd-row-flash > th {
    animation: rd-row-flash-fade 2.5s ease-out;
}

@keyframes rd-row-flash-fade {
    0%, 40% { background-color: var(--bs-primary-bg-subtle); }
    100% { background-color: transparent; }
}
/* #4763 — the capture-set spider diagram. Every colour is a Bootstrap variable, so the diagram
   follows the theme (including dark) rather than carrying its own palette. */

.rd-capture-graph {
    /* Centre the web in its card: at its natural size a small set no longer fills the width, and a
       left-aligned diagram in a wide card reads as though something failed to load. */
    display: flex;
    flex-direction: column;
    align-items: center;
}

/* The svg carries its own width/height in user units. max-width lets a big web shrink to the column;
   nothing may STRETCH it, because that made the diagram's apparent size inversely proportional to how
   much was in it — one node in a 180-unit box, blown up 5x to fill a 950px column. */
.rd-capture-graph svg {
    display: block;
    max-width: 100%;
    height: auto;
}

/* #4783 — arriving from an orphan node's "select its link here" must land visibly, or the scroll looks
   like nothing happened on a page of near-identical cards. Scroll margin keeps the header clear of the
   sticky chrome.
   #5556 — the management report's contents-map anchors join the same rule: a fragment landing at
   viewport Y = 0 put every section heading behind the 57px sticky navbar (measured in review —
   elementFromPoint at the heading returned the nav). One number, one home.
   #5685 — and so do a document's own headings, which its contents list links to. Markdig mints ids
   on headings and nothing else inside this container, so [id] is the heading set. */
.rd-capture-card,
[id^="report-"],
.rd-document-text [id],
.rd-regulation-article {
    scroll-margin-top: 5rem;
}

/* #6361 F7 — the contents map's two columns (RdContentsMap). They were the management report's inline
   style; one column at phone width, where two would halve an entry's line. */
.rd-contents-map { columns: 2; column-gap: 2.5rem; }
@media (max-width: 575.98px) { .rd-contents-map { columns: 1; } }

/* There is deliberately NO highlight on the arriving row, and the scroll-margin above is doing the work
   instead: it parks the card just under the sticky chrome, which is the cue that you have landed.
   Two highlights were tried against the running app and neither fires for a fragment-only navigation on
   this page — CSS :target never matches (Blazor updates the URL without setting the document's indicated
   part) and NavigationManager.LocationChanged is not raised either. Both looked correct in tests, which
   assert markup rather than navigation. Anything better than the scroll needs a click handler rather
   than a link, and a link is worth more here than a flash. */

/* ── the rings the web hangs on ── */

.rd-cg-ring {
    fill: none;
    stroke: var(--bs-border-color);
    stroke-width: 1;
    stroke-dasharray: 2 6;
    opacity: .7;
}

/* ── edges ── */

/* The web's strokes, the root's ring and its legend key are lines, so they take the link tone (#6844
   review, "two tones" under Palette): the same #006bb7 in light, and in dark #73aed7, where an edge at .6
   is 3.24:1 against the surface (#006bb7 at .6 was 1.79:1). */
.rd-cg-edge {
    fill: none;
    stroke: var(--bs-link-color);
    stroke-width: 1.75;
    opacity: .6;
}

.rd-cg-arrowhead {
    fill: var(--bs-link-color);
    opacity: .75;
}

/* An unselected link exists in the app but not in the demo: present, not a path. */
.rd-cg-edge-inert {
    fill: none;
    stroke: var(--bs-secondary-color);
    stroke-width: 1.25;
    stroke-dasharray: 3 4;
    opacity: .45;
}

.rd-cg-arrowhead-inert {
    fill: var(--bs-secondary-color);
    opacity: .5;
}

/* ── nodes ── */

.rd-cg-node circle {
    stroke-width: 2;
}

/* Lift the nodes off the web very slightly, so the arcs read as passing behind them. */
.rd-cg-node {
    filter: drop-shadow(0 1px 1px rgb(0 0 0 / .12));
    /* Each node is a link to the page it stands for (#4763 follow-up), so say so on hover — and turn
       off the underline an SVG anchor otherwise puts through its own labels. */
    cursor: pointer;
    text-decoration: none;
}

/* Keyboard focus must be visible: these are links, and the web is navigable by tab. */
.rd-cg-node:focus-visible circle {
    stroke-width: 3.5;
    outline: none;
}

/* Hovering a node dims nothing and moves nothing — it just firms up, so a long label can be read
   without the layout shifting under the pointer. */
.rd-cg-node:hover circle {
    stroke-width: 3;
}

.rd-cg-node:hover .rd-cg-label {
    font-weight: 600;
}

.rd-cg-start circle {
    fill: var(--bs-primary-bg-subtle);
    stroke: var(--bs-link-color);
}

.rd-cg-captured circle {
    fill: var(--bs-success-bg-subtle);
    stroke: var(--bs-success);
}

/* Not captured yet — the to-do state, so it reads as unfinished work. */
.rd-cg-pending circle {
    fill: var(--bs-warning-bg-subtle);
    stroke: var(--bs-warning);
    stroke-dasharray: 4 3;
}

/* Captured but unreachable: the export writes a file nobody can click to. */
.rd-cg-orphaned circle {
    fill: var(--bs-secondary-bg-subtle);
    stroke: var(--bs-secondary-color);
    stroke-dasharray: 2 3;
}

/* ── text ── */

.rd-cg-glyph,
.rd-cg-label,
.rd-cg-route {
    text-anchor: middle;
    /* Labels sit over the web; clicks belong to the list view, not to text selection here. */
    pointer-events: none;
}

/* A label knocked out of whatever it crosses. Cheaper and tidier than laying a rectangle behind each
   one, and it is what keeps an outer label legible where it passes over a ring or an arc. */
.rd-cg-label,
.rd-cg-route {
    paint-order: stroke;
    stroke: var(--bs-body-bg);
    stroke-width: 3px;
    stroke-linejoin: round;
}

.rd-cg-glyph {
    fill: var(--bs-body-color);
    font-size: 11px;
    font-weight: 600;
}

.rd-cg-label {
    fill: var(--bs-body-color);
    font-size: 12px;
}

.rd-cg-route {
    fill: var(--bs-secondary-color);
    font-size: 10.5px;
    font-family: var(--bs-font-monospace);
}

/* ── legend keys ── */

.rd-cg-key {
    display: inline-block;
    width: .75rem;
    height: .75rem;
    border-radius: 50%;
    border: 2px solid;
    vertical-align: -.1rem;
    margin-right: .25rem;
}

.rd-cg-key-start {
    background: var(--bs-primary-bg-subtle);
    border-color: var(--bs-link-color);
}

.rd-cg-key-captured {
    background: var(--bs-success-bg-subtle);
    border-color: var(--bs-success);
}

.rd-cg-key-pending {
    background: var(--bs-warning-bg-subtle);
    border-color: var(--bs-warning);
}

.rd-cg-key-orphaned {
    background: var(--bs-secondary-bg-subtle);
    border-color: var(--bs-secondary-color);
}

/* #4899 — a long recorded account, clamped to its first several lines with the whole thing one click
   away. A DISPLAY bound, never a truncation of the record: the text is all present, and the clamp is
   released by a control beside it. Used where a stored rationale frames a page whose job is something
   else (the risk-gap view, where the checks are the work). */
.rd-clamp {
  display: -webkit-box;
  -webkit-line-clamp: 6;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* #4916 — the two-line variant, for a row's summary line. Same mechanism as .rd-clamp; a different
   count because a list row is scanned, not read, and a reason that runs to six lines stops the list
   being a list. The full text is always in the row's expansion. */
/* NOT combined with Bootstrap's .d-block: that sets `display: block !important`, which beats the
   -webkit-box the clamp needs, and the line renders unclamped. The box is block-level already. */
.rd-clamp-2 {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* The clamp is a convenience, not a constraint on what is readable: anyone who has asked for less
   motion or is printing gets the whole account. */
@media print {
  .rd-clamp,
  .rd-clamp-2 { display: block; overflow: visible; }
}

/* #4908 — the near-miss bench's row grammar. Rendered, the flex version broke: candidate reasons run
   80–190 characters, and past the line length the reason claimed the full width and pushed the actions
   onto the next line, so a list of four rows had two grammars (I4.1, I4.3 — one row grammar per
   surface). Fixed columns keep the shape whatever the reason's length; the reason wraps inside its
   own column rather than displacing anything. */
.rd-candidate-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: .5rem;
}
.rd-candidate-state { align-self: start; margin-top: .15rem; white-space: nowrap; }
.rd-candidate-reason { min-width: 0; }
.rd-candidate-actions { display: flex; align-items: center; gap: .35rem; white-space: nowrap; }

/* Below the reading measure the three columns stop fitting; stack them rather than squeezing the
   reason to a few words per line (WCAG 2.2 §1.4.10 — reflow without two-dimensional scrolling). */
@media (max-width: 48rem) {
  .rd-candidate-row { grid-template-columns: 1fr; }
  .rd-candidate-actions { justify-content: flex-start; }
}

/* #6509 — the declared status column, shared by the risk gap page's rows and the near-miss bench (I4.3,
   one row grammar; I4.5 by analogy — a column's geometry is declared, not inferred from each row's word).
   Sized to the widest of the five state labels in the badge's own font (12px, 600, system-ui), measured
   29 Sep 2026 on Windows, with the tone's border: Guarded 64.4 · Falls short 72.7 · Nothing found 98.5 ·
   Not yet examined 114.7 · Shown happening 116.1px. 7.5rem is 120px. It is a MIN-width: a system font that
   sets a label wider grows that badge rather than clipping the word (I5.6). */
:root { --rd-state-col: 7.5rem; }
.rd-state-badge { min-width: var(--rd-state-col); text-align: start; }

/* #6604 — every row of count tiles is one grid (StatTileRow), and each tile spans two of its row tracks
   through subgrid: the label in the first, the figure in the second. So every figure in a row starts under
   the row's tallest label, and a figure that wraps grows its own track downwards without moving the others.
   - Columns: StatTileRow emits the count at each of Bootstrap's breakpoints as --rd-cols-*, so this carries
     no fallback chains. minmax(0, 1fr) keeps the columns equal: a long label wraps inside its column rather
     than widening it.
   - The gap: the grid keeps its row gap between rows of tiles, and each subgrid sets row-gap: 0 so the gap
     does not also open between a tile's label and its figure (css-grid-2 §9: a subgrid's own gap applies
     within it). #6604's spike measured this in Chromium 131, Firefox 132 and WebKit 18.2: figure tops level
     to 0px across a row, the label on its figure as before, rows 16px apart at g-3, no trailing space.
   - The card root may be a div, an a or a button; the spike measured subgrid through all three.
   - Without subgrid (before Chrome 117, Safari 16, Firefox 71), grid-template-rows: subgrid is dropped as
     invalid: the columns still lay out, and each tile falls back to its label stacked over its figure.
   This replaced Bootstrap's flex row (.row row-cols-*), which cannot align content across sibling columns,
   and #6418's seven-column rule for the findings band, which the grid makes unnecessary: it has no six-column
   ceiling (I16.3). */
.rd-tile-grid {
    display: grid;
    grid-template-columns: repeat(var(--rd-cols-xs, 1), minmax(0, 1fr));
    gap: var(--rd-tile-gap, 1rem);
}
@media (min-width: 576px) { .rd-tile-grid { grid-template-columns: repeat(var(--rd-cols-sm, 1), minmax(0, 1fr)); } }
@media (min-width: 768px) { .rd-tile-grid { grid-template-columns: repeat(var(--rd-cols-md, 1), minmax(0, 1fr)); } }
@media (min-width: 992px) { .rd-tile-grid { grid-template-columns: repeat(var(--rd-cols-lg, 1), minmax(0, 1fr)); } }
@media (min-width: 1200px) { .rd-tile-grid { grid-template-columns: repeat(var(--rd-cols-xl, 1), minmax(0, 1fr)); } }
.rd-tile-grid > .card,
.rd-tile-grid > .card > .card-body {
    grid-row: span 2;
    display: grid;
    grid-template-rows: subgrid;
    row-gap: 0;
}
.rd-tile-grid > .card > .card-body > * { align-self: start; }

/* #6509 — the active filter tile's ring: its 1px primary border plus a second pixel drawn INSIDE it by a
   pseudo-element's border, so the tile's box is its siblings' size. It was border-2. #6604: in the row's
   grid a thicker border grows the label track for the whole row, so every figure would move each time the
   active tile changed.
   Why a pseudo-element's BORDER, and neither of the two things tried before it:
   - not an inset box-shadow — forced-colors mode (Windows High Contrast) drops box-shadow; it keeps borders;
   - not an outline — Bootstrap's reboot sets `button:focus:not(:focus-visible) { outline: 0 }` at (0,2,1),
     so the click that presses the tile focused it and took the ring away with it: the state was then colour
     alone (WCAG 2.2 §1.4.1). Measured live in #6591's review. An outline is also the property the browser's
     focus indicator uses, so the two could not both show; they can now (§2.4.7).
   .card is already position: relative, and the pseudo-element is absolute and takes no pointer events, so
   nothing moves and a click still lands on the button.
   The ring is a line, so it takes the link tone (#6844 review, "two tones" under Palette): in dark it is
   #73aed7, 6.44:1 against the surface, where #006bb7 was 2.78:1; the tile's own .border-primary takes the
   same tone under the dark rule there. */
.rd-tile-active::after {
    content: "";
    position: absolute;
    inset: 0;
    border: 1px solid var(--bs-link-color);
    border-radius: var(--bs-card-inner-border-radius);
    pointer-events: none;
}

/* The gap page's row: state, then the title at the reading measure, then the disclosure chevron. The
   expansion starts under the title, so the reason and the sections under it line up with the words they
   open. Stacked below 48rem (the bench's breakpoint): the state above the title, every badge still starting
   on one line, and the title given the row's full width (WCAG 2.2 §1.4.10). */
.rd-state-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "state text chevron";
  align-items: baseline;
  column-gap: .5rem;
}
.rd-state-row > .rd-state-badge { grid-area: state; }
.rd-state-row-text { grid-area: text; min-width: 0; }
.rd-state-row-chevron { grid-area: chevron; }
.rd-state-row-detail { padding-left: calc(var(--rd-state-col) + .5rem); }
@media (max-width: 48rem) {
  .rd-state-row {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "state chevron" "text chevron";
    row-gap: .25rem;
  }
  .rd-state-row > .rd-state-badge { justify-self: start; }
  .rd-state-row-detail { padding-left: 0; }
}

/* #6592 — one stacked row grammar for every register table, at the width where the two row grammars above stack
   (48rem). A register's row is one record that reads on its own. So below 48rem each row becomes one block, read in
   its column order (WCAG 2.2 §1.3.2, §2.4.3):
   - the identity cell on its own line;
   - short value and status cells as labelled pairs on one wrapping line;
   - a prose cell on a line of its own;
   - the actions last.
   Nothing the desktop row shows is hidden. The rule is I4.4's, the product's own arbitration (#6592 Q6), and not
   WCAG's: 1.4.10 excepts data tables. It is held to a row-height measurement against I16.2 ("stacked blocks of text
   is a wall"): stacked rows no taller than the squeezed table rows they replace.
   - The header row is hidden the visually-hidden way, never with display: none. That would take the column headers
     out of the accessibility tree, and every value would lose its header for assistive technology. The visible
     label (.rd-stack-label) is aria-hidden, so nothing is announced twice. It is a real element because a
     generated one cannot be hidden: content: attr(…) / "" still reached the cell's name in Chromium and WebKit
     (measured, #6592).
   - The table's own semantics (table, rows, column headers, cells) survive the display change in both engines
     (measured, #6592).
   - A positional grid (I4.5) does not carry this class: its meaning is two-dimensional, so it keeps its scroll.
   - Screen only. A printed register keeps its table, whatever margins the reader picks. Chromium evaluates print
     media at the paper's full width when the stylesheet's @page margins apply (794px on A4), and at the page area
     when the print dialog supplies the margins (674px on A4 with 16mm sides, under 48rem). Both measured, #6592.
     Without 'screen', whether a register printed stacked would turn on the reader's margin setting. The design is
     about a narrow window and zoom, not paper.
   StackedRegisterConventionTests pins which tables carry it, and that this block is screen-only. */
.rd-stack-label { display: none; }
@media screen and (max-width: 48rem) {
  table.rd-stack-table > thead {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    white-space: nowrap !important;
    border: 0 !important;
  }
  table.rd-stack-table,
  table.rd-stack-table > tbody { display: block; width: 100%; }
  table.rd-stack-table > tbody > tr {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 1rem;
    row-gap: .25rem;
    padding: .5rem 0;
    border-bottom: var(--bs-border-width) solid var(--bs-border-color);
  }
  table.rd-stack-table > tbody > tr > td,
  table.rd-stack-table > tbody > tr > th {
    display: block;
    flex: 0 1 auto;
    min-width: 0;
    padding: 0;
    border: 0;
    overflow-wrap: anywhere;
  }
  /* Full-width lines: the identity, a prose cell, the actions, and any cell spanning the columns (a group header,
     a disclosure or proposal row). */
  table.rd-stack-table > tbody > tr > .rd-stack-identity,
  table.rd-stack-table > tbody > tr > .rd-stack-prose,
  table.rd-stack-table > tbody > tr > .rd-stack-actions,
  table.rd-stack-table > tbody > tr > [colspan] { flex: 1 1 100%; }
  /* A leading select box keeps its place beside the identity, in a gutter of its own, so every line below starts
     under the identity rather than under the box. */
  table.rd-stack-table > tbody > tr:has(> .rd-stack-select) { padding-left: 3rem; }
  table.rd-stack-table > tbody > tr > .rd-stack-select { flex: 0 0 2rem; margin-left: -3rem; }
  table.rd-stack-table .rd-stack-label {
    display: inline;
    margin-right: .35rem;
    font-size: .75rem;
    color: var(--bs-secondary-color);
  }
  /* Nothing is hidden (#6592 G7): what a column kept on one line, or cut short, wraps. */
  table.rd-stack-table .text-nowrap { white-space: normal !important; }
  table.rd-stack-table .text-truncate { overflow: visible; text-overflow: clip; white-space: normal; }
  /* An entity id wraps (.rd-entity-id), but as an inline the longest risk ids still ended 6px past their cell at 320,
     and the table scrolled (measured, #6592). As one box no wider than its line, it wraps inside, its chrome whole. */
  table.rd-stack-table .rd-entity-id { display: inline-block; max-width: 100%; }
  /* The actions: start-aligned, as .rd-candidate-actions is below 48rem, and wrapping, which that class is not. */
  table.rd-stack-table > tbody > tr > .rd-stack-actions { text-align: start !important; }
  table.rd-stack-table > tbody > tr > .rd-stack-actions .d-flex { flex-wrap: wrap; justify-content: flex-start !important; }
}

/* ── #5149 — the finding page ───────────────────────────────────────────────────────────────
   I16.3 — layout is Bootstrap's: row/col for the two-up, .card for the summary, .table for the
   evidence. What is left here is only what the framework has no answer for. */

/* The measure applies to the PROSE column only (density guide §2). The evidence table below it
   deliberately takes the full width — that is the fix, not an oversight. */
/* The TOP ROW is bounded, so its 8/12 column lands near the measure and the prose fills its own
   track. The per-element max-widths that used to do this left 45% of the column empty. */
.rd-finding-top { max-width: 58rem; }

.rd-finding-head,
.rd-finding-section {
    max-width: var(--reading-measure);
}

/* Title and conclusion are ONE block with one bottom margin, so the rhythm does not depend on the
   conclusion being present — a record without one used to jam its heading against the first
   section. */
.rd-finding-head { margin-bottom: 2.25rem; }

/* The type scale, followed rather than approximated (UI_STYLE_GUIDE — Type Scale). The headings
   were undersized: at 1.0625rem a section heading sits barely above 1rem body text, so the prose
   and the table rows competed with the structure meant to divide them. The row data and the group
   headers were never the problem — the style guide reserves `small` for secondary text and requires
   table cells and group headers at FULL body size, which is what they already were. */
.rd-finding-title {
    margin-bottom: 0.75rem;
    font-size: 2rem;          /* h1 */
    font-weight: 600;
    line-height: 1.2;
}

/* A lead has its own measure — 45ch, because a lighter weight runs longer lines at the same ch
   (density guide §2, the second calibrated value). */
.rd-finding-lead {
    max-width: var(--reading-measure-lead);
    margin-bottom: 0;
    font-size: 1.125rem;
    font-weight: 500;
    line-height: 1.5;
    color: var(--bs-emphasis-color);
}

.rd-finding-section { margin-bottom: 2.25rem; }

/* Section headings read as HEADINGS. They were 13px uppercase with tracking — a micro-label, which
   is the right treatment for a card's meta and the wrong one for the structure of a long narrative:
   too quiet to divide the page, and uppercase tracking costs legibility at that size. Sentence
   case, at a size that sits above 16px body text rather than under it. */
.rd-finding-section > h2,
.rd-finding-evidence-heading {
    margin-bottom: 0.5rem;
    font-size: 1.5rem;        /* h2 */
    font-weight: 600;
    line-height: 1.3;
    color: var(--bs-emphasis-color);
}

/* The jump at each level is the cue (density guide §5): h1 2rem -> h2 1.5rem -> group header 1rem
   semibold -> row data 1rem. Three steps of size and one of weight, over content that stays at
   body size throughout. */

.rd-finding-evidence-head { margin: 3rem 0 1rem; }

.rd-finding-section > ul { padding-left: 1.1rem; }

.rd-finding-section > ul > li { margin-bottom: 0.4rem; }

.rd-finding-source {
    margin-bottom: 0;
    font-size: 0.875rem;
    color: var(--bs-secondary-color);
}

/* The card's own labels stay micro-labels: they ARE meta, and there are three of them in a small
   box where a heading treatment would compete with the counts. */
.rd-finding-card-heading {
    margin-bottom: 0.5rem;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--bs-secondary-color);
}

/* The counts stay with the reader while they read the narrative beside them. Bootstrap has no
   sticky-card utility, so this is the one thing here the framework does not answer. */
@media (min-width: 992px) {
    .rd-finding-summary {
        position: sticky;
        /* #6410 — 4.5rem, the offset the sticky rails above use to clear the sticky navbar. It was 1rem,
           so the card's top — on the review page, its Accept / Edit / Reject — slid under the navbar. */
        top: 4.5rem;
    }
}

/* ── The evidence table, grouped by how the evidence misses ── */

/* #5757 — the table is the SUM OF ITS COLUMNS, not the width of the page (density guide §2).

   It ran full width under rd-page-xl on the argument that a table is data and the measure does not
   govern it. That exemption is for the structure; the prose in a cell is still read in lines. With
   a 20rem document column and a 56px words column, the whole surplus landed on the one column
   that could absorb it silently — "Why it falls short" at 1024px, 137 characters on its longest
   rendered line against a ceiling of 75. Sizing the table to its columns spends the surplus nowhere
   instead of spending it all in one place. */
.rd-finding-table {
    width: auto;
    max-width: 100%;
}

/* The prose column takes the measure; the scan columns take their content. Together they ARE the
   table's width. */
.rd-finding-table .rd-finding-cell-why { width: var(--reading-measure); }
.rd-finding-table thead th:last-child,
.rd-finding-table td:last-child { width: 4rem; }

/* The group's definition is prose too, and it sits in a colspan header — unmeasured, its longest
   sentence would set the table's width and undo everything above. */
.rd-finding-group-note { max-width: var(--reading-measure); }

.rd-finding-table thead th {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--bs-secondary-color);
}

/* #5722 / I15.8 — the group header outranks its rows.

   What shipped: header and document line both sat at the table's base size, neither setting a
   font-size, and everything else favoured the child. The header's name was pushed right by the
   chevron and its 0.5rem gap; the document line was flush at the table's left edge. So the child sat
   to the LEFT of its own parent, at equal weight, and the eye read it as the parent.

   Two declarations settle it, and they are the two halves of the clause: the rows are indented INSIDE
   the header's text column (position now agrees with structure), and the name takes a size step above
   the rows (rank now readable without comparing indents). The rows are moved rather than the header
   un-indented, because the chevron has to stay adjacent to what it toggles and a negative margin on a
   colspan header would hang outside its own cell. */
.rd-finding-table .rd-finding-cell-doc {
    width: 20rem;
    /* 1.5rem, not 1.25: the header's text starts after its chevron AND the 0.5rem gap beside it, and
       GroupHeaderRankTests measures that sum. It rejected 1.25rem, which is the guard doing its job —
       eyeballing the indent is how the original defect passed review. */
    padding-left: 1.5rem;
}

/* The group header. It carries the pattern, its count and its definition — so the headers alone
   are the overview, and the row below carries only what varies. */
.rd-finding-group > th {
    padding-top: 1.75rem;
    padding-bottom: 0.5rem;
    font-weight: 400;
    background-color: transparent;
    border-bottom-color: var(--bs-border-color);
}

/* The header is the control (I15.4). A real button, so it is keyboard-operable and exposes its
   pressed state, styled to read as the row it is rather than as a form control. */
.rd-finding-group-toggle {
    display: inline-flex;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0;
    background: none;
    border: 0;
    text-align: left;
    color: inherit;
}

.rd-finding-group-toggle:hover .rd-finding-group-name { text-decoration: underline; }

/* The focus outline is a line, so it takes the link tone (#6844 review, "two tones" under Palette). It read
   --bs-focus-ring-color, which is the primary at 25%: 1.45:1 against white and 1.23:1 against the dark
   surface, a ring that did not show in either theme (#0d6efd at 25% was 1.41 and 1.29). Now 5.55:1 and
   6.44:1. */
.rd-finding-group-toggle:focus-visible {
    outline: 2px solid var(--bs-link-color);
    outline-offset: 2px;
}

.rd-finding-group-chevron {
    font-size: 0.75rem;
    color: var(--bs-secondary-color);
}

.rd-finding-group-name {
    /* I15.8 — the size step. Weight alone was not enough: the document line beside it carries a
       title in the same size, and a bold child at equal size still competes with its parent. */
    font-size: 1.0625rem;
    font-weight: 600;
    color: var(--bs-emphasis-color);
}

.rd-finding-group-count {
    display: inline-block;
    padding: 0.05rem 0.45rem;
    font-size: 0.8125rem;
    font-variant-numeric: tabular-nums;
    color: var(--bs-secondary-color);
    background-color: var(--bs-secondary-bg);
    border-radius: 0.75rem;
}

.rd-finding-group-note {
    display: block;
    margin-top: 0.15rem;
    margin-left: 1.25rem;
    font-size: 0.875rem;
    font-weight: 400;
    color: var(--bs-secondary-color);
}

.rd-finding-quote-row > td {
    padding-top: 0;
    border-bottom-width: 0;
}

/* #5757 — inside the shortfall's column the quote takes the CELL, not its own measure. The cell is
   already at the measure, so the panel's own cap would leave it narrower than the column it belongs
   to and aligned to nothing; the text inside it then reads at the measure less the panel's padding.
   One left edge and one right edge for the shortfall and the evidence for it. */
.rd-finding-quote-row .rd-doc-excerpt { max-width: 100%; }

.rd-tabular { font-variant-numeric: tabular-nums; }

/* #5161 — the conclusion on the operating seat's panel. Quieter than the reading surface's lead
   (this is a working surface, and the panel's job is the decision) but at body strength, because
   it is the sentence being signed off. */
.rd-panel-conclusion {
    margin-bottom: 0.75rem;
    font-weight: 500;
    color: var(--bs-emphasis-color);
}

/* #5720 — the evidence matrix. Its GEOMETRY is declared inline on the table and its colgroup, the
   idiom its four sibling registers already use (#5397, ThesaurusPage / FirmVocabularyPage /
   DocumentRegisterPage / DocumentPassagesPage): headers once, geometry declared once, visible at the
   call site. What is left here is only what that idiom does not address.

   Long words in a fixed-layout cell must wrap rather than overflow: catalogue identifiers and
   document titles have no spaces to break at. */
.rd-matrix-table td,
.rd-matrix-table th {
    overflow-wrap: anywhere;
}

/* #6829 — a badge wraps inside its cell. Bootstrap's .badge is white-space: nowrap, and a badge wider
   than its fixed-layout column spills over the next one: measured at 800px with this rule removed, the
   10% State column's "Not evidenced" ran over the Evidence column's "3 citations". Below 48rem the table
   stacks (#6592) and the columns go, so there it no longer matters; between 48rem and a wide window it
   does. The matrix's columns are declared inline on its colgroup, the idiom above. */
.rd-matrix-table .badge {
    white-space: normal;
    text-align: start;
}

/* The catalogue identifier stays reachable (principle 5 — long catalogue identifiers must be visible
   where they are needed) but stops outweighing the words beside it (I4.1). It was a chip taking
   roughly 40% of the row and wrapping to a second line on 8 of 26 rows. */
.rd-matrix-id {
    font-size: 0.8125rem;
}

/* The category subheading sits between the group header and its rows, so it must not outrank the
   header (I15.8) — lighter and smaller than the header, and indented no further left than the rows
   it introduces. */
.rd-matrix-category td {
    font-size: 0.8125rem;
}

/* #6373 PR-B — a field judged wrong on submit is marked by its outline as well as by the message above
   it (I17.5; GOV.UK error message: "a red border on the input"). The mark is the attribute the field
   already carries for assistive technology, so the visual and the announced state are one fact and
   cannot disagree; Bootstrap's own invalid colour keeps it right in both themes. */
.form-control[aria-invalid="true"],
.form-select[aria-invalid="true"] {
    border-color: var(--bs-form-invalid-border-color);
    border-width: 2px;
}
.form-control[aria-invalid="true"]:focus,
.form-select[aria-invalid="true"]:focus {
    border-color: var(--bs-form-invalid-border-color);
    box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25);
}

/* #6373 F16 — a scrolling table clips its own absolutely-positioned descendants. Bootstrap's
   .visually-hidden is position:absolute, and .table-responsive is not positioned, so hidden text in a
   cell beyond the viewport (a row button's "Remove … from the inventory") escaped the wrapper's
   overflow clip and widened the DOCUMENT: measured at 320px, the systems page scrolled to 332 with no
   visible box past the edge. A positioned scroll container is the containing block, and the clip holds. */
.table-responsive {
    position: relative;
}

/* #6843 review — a scrolling table wrapper scrolls on screen and cannot on paper, so in print it lets its table out
   at the table's own width. Every wrapper, not only a markdown table's region (Platform's TableScrollRegionExtension,
   which RiskCheck turns on): both clip the same way. Measured under print media with print.css linked:
   - a markdown region on an evidence pack: a quoted four-column mapping table is 486px in a 473px quote box (the
     quote is held to the reading measure), and the region cut its last 33px at both A4 widths, 794 and 674px, where
     the bare table printed whole;
   - a plain wrapper on the board: "Status by entity" is 643px in a 599px box at 674px, and the wrapper cut its last
     44px. It fit at 794. The register export fit at both.
   Screen is unchanged: there the wrapper scrolls and the page does not (#6834). */
@media print {
    .table-responsive {
        overflow: visible;
    }
}

/* ── #6410 — the finding review page ────────────────────────────────────────────────────────
   The finding page's composition (above) with a decision in the card: a reading column and a card
   beside it. What is here is only what the finding page's rules and the type scale do not already
   answer. There is no tint on the reading column: a grey panel the width of the page, holding text
   stopped at the measure, read as an empty slab (density §2 and §5 — a tint marks one section, and on
   the whole body it marks nothing). */

/* Two-column variant (Andrew's review of the single-column slice: "a bit stingy"). The page is the wide
   container (rd-page-xl), the reading side takes 8/12 and splits into two columns at xl, each landing
   near the measure (density §2: size the container to the measure — here by the grid rather than by a
   fixed width), and the decision card takes 4/12. The single-column slice sized one column to the
   measure and bounded the row; that is the alternative this branch is compared against.

   THREE AREAS, NOT TWO COLUMNS. The first cut was a Bootstrap row of two columns with the reasoning in
   a row below both, so the reasoning could not start until the longer right-hand column ended — a hole
   under the checks on the left. Here the documents span both grid rows on the right, and the reasoning
   sits in the second row on the left, directly under the checks. Bootstrap's grid has no row span, so
   this is the one layout on the page the framework does not answer. Row 1 is sized by the checks
   (auto); the documents' extra height goes to row 2 (1fr), so the reasoning starts where the checks end.
   Below xl the areas stack in source order: checks, documents, reasoning. */
.rd-review-columns {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1.5rem;
}
@media (min-width: 1200px) {
    .rd-review-columns {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        grid-template-rows: auto 1fr;
        grid-template-areas:
            "checks documents"
            "reasoning documents";
        align-items: start;
    }
    .rd-review-area-checks { grid-area: checks; }
    /* #6508 — side by side, the two columns read across as rows ("those are not rows, those are different
       things"). A rule between them, in the theme's border token so dark mode follows, centred in a gutter
       widened from 1.5rem to 3rem (Bootstrap's spacer 5): the column gap on its left, this padding on its
       right. It stretches over both grid rows, so it divides the columns for their full height even when
       the checks and an opened reasoning run longer than the documents. Stacked, below xl, there is no rule. */
    .rd-review-area-documents {
        grid-area: documents;
        align-self: stretch;
        border-left: 1px solid var(--bs-border-color);
        padding-left: 1.5rem;
    }
    .rd-review-area-reasoning { grid-area: reasoning; }
}
.rd-review-columns > div > .rd-review-section:last-child { margin-bottom: 0; }

/* A "show the words" line wraps at the measure, like the reading it belongs to. */
.rd-review-main details > summary { max-width: var(--reading-measure); }

/* Sections divide the column by their headings; the gap between sections is larger than any gap inside
   one, so they read as parts. */
.rd-review-section { margin-bottom: 2.5rem; }
.rd-review-section > h2 { margin-bottom: 0.75rem; }

/* The conclusion is the finding's sentence, so it keeps the finding page's lead and gets room before
   the first section. */
.rd-review-conclusion { margin-bottom: 2rem; }

/* A band or pattern heading sits one step under its section (h3 in the scale) with the same rhythm. */
.rd-review-band { margin-bottom: 0.25rem; }
.rd-review-count { font-size: 1rem; }

/* The assistant's reading of a quote is content the reviewer decides on: body size, body colour
   (UI_DENSITY_GUIDE.md §6 — no micro-type for primary content).
   #6410 Addendum 3 — and NO RULE. Only quoted text wears a left rule on this page's content (the Regulation's
   words, the documents' words) — the hairline between the two columns at xl (#6508) is a column rule, not a
   rule on content; the reading carried .rd-shortfall's rule too, and with the grey gone it
   read as a third quote. #4718: the document's words and the app's explanation must never dress alike.
   The label (.rd-review-voice) is what says whose words these are. */
.rd-review-reading {
    font-size: 1rem;
    color: var(--bs-body-color);
    border-left: 0;
    padding-left: 0;
}

/* Whose words: the app's voice labelled in the same small-label grammar as DocumentQuote's "From the
   document", so the two voices are named alike and dressed differently — the quote in its panel, the
   reading as plain text under a label. */
.rd-review-voice {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--bs-secondary-color);
    margin-bottom: 0.15rem;
}

/* The failing check a passage is cited by: the check's title and its test, under the reading. */
.rd-review-fails { max-width: var(--reading-measure); }

/* The regulation's own words, set apart as quoted text by a rule, as the documents' words are. */
.rd-review-requirement {
    padding-left: 0.75rem;
    border-left: 3px solid var(--bs-border-color);
}

/* The act in progress (edit, reject, reopen) is framed, so the form reads as the thing being done. */
.rd-review-act {
    padding: 1rem;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
}

/* The second telling of the evidence, one step behind. */
.rd-review-reasoning > summary {
    cursor: pointer;
    font-weight: 600;
}

/* Label and value (density §13.2): the label small, semibold and secondary; the value full size in
   the body colour, so the pair separates by size and weight rather than by colour alone. */
.rd-review-facts > dt {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--bs-secondary-color);
}
.rd-review-facts > dd { margin-bottom: 0.75rem; }
.rd-review-facts > dd:last-child { margin-bottom: 0; }

/* #6426 — what a finding proposes for the record: the facts grammar above, held to the reading measure because its values
   include the assistant's prose (a narrative, a description) that the reviewer reads before deciding. */
.rd-review-record { max-width: var(--reading-measure); }

/* ---- Register group headers (#6412) ------------------------------------- */
/* A group header sits ABOVE its rows (density §5: "fix a flat hierarchy upwards"). It was small grey
   capitals, which read weaker than the bold column headers beneath it and the rows it heads — "POLICIES"
   under "Reference · Title · Status" on the document register. One class, worn by RegisterGroup's h2 and
   RegisterGroupRows' row-group th, so all ten registers change together: a step above body size, the
   scale's weight, the emphasis colour, sentence case. The toggle inside is a .btn, which sets its own
   size and weight; it inherits the header's instead. */
.rd-group-heading {
    font-size: 1.125rem;
    font-weight: 600;
    color: var(--bs-emphasis-color);
}
.rd-group-heading > .btn {
    font-size: inherit;
    font-weight: inherit;
}

/* ---- The (i)'s gap (#6848) ------------------------------------------------------------------------------ */
/* StandardsHelp keeps the .25rem it always had (ms-1) wherever it sits: a heading, a <dt>, a card header. Beside a
   FieldLabel's name it takes .5rem, because the label text's trailing space supplied the other .25rem until the name
   and its (i) became a flex row, which drops the space (decision 3, measured within 1px on the 52 FieldLabel
   adornments). Scoped to the row, so the hosts outside one keep today's gap (#6883 review F1). */
.rd-standards-help { margin-left: .25rem; }
.rd-field-label > .rd-standards-help { margin-left: .5rem; }
