/* =========================================================
   Ruleset Manager — application stylesheet
   DRAFT, unactivated (see GAP-REPORT.md) — not RM-WEB-001-approved production styling.

   Tokens below are reimplemented from GWMApp.com reference material (colors/fonts
   extracted from its own CSS custom properties), per Product Owner request to visually
   align Ruleset Manager with GWMApp ahead of a future add-on integration. This file does
   NOT contain any GWMApp source — it's an independent reimplementation of the same
   design tokens, consistent with this project's own GWMApp-content boundary (see
   GAP-REPORT.md's SCOPE CORRECTION entry and RM-DEC-006 §6). GWMApp's actual CSS is kept
   local-only, never committed, under /gwmapp-reference-local/ (git-ignored).

   Deliberately does NOT port GWMApp's full legacy stylesheet (mega-menu/flyout submenus,
   weld-form-specific classes, select2/print/theme-framework rules) — Ruleset Manager's
   current page set (login, admin/users) doesn't need any of that; only the token values,
   header shell shape, and a handful of component patterns (form controls, buttons,
   tables, validation) that generalize.
========================================================= */

:root {
    --rm-primary: #00007A;
    --rm-primary-hover: #1A1A96;
    --rm-accent: #3B5BDB;
    --rm-accent-hover: #4C6EF5;
    --rm-text: #1F2937;
    --rm-body-text: #565656;
    --rm-muted: #667085;
    --rm-border: #D0D5DD;
    --rm-hover-bg: #F2F4F7;
    --rm-bg: #FFFFFF;
    --rm-danger: #dc3545;
    --rm-danger-bg: #fff5f5;
    --rm-shadow: 0 18px 45px rgba(16, 24, 40, .16);
    --rm-header-height: 64px;
    --rm-font-heading: "Poppins", "Helvetica", "Arial", sans-serif;
    --rm-font-body: "Nunito", "Helvetica", "Arial", sans-serif;

    /* GAP-181 — page width. Product Owner, 2026-09-17: "The main page width shall be adjusted to
       maximum width. Apply this to all pages."

       EVERY page container in this application now reads its max-width from one of these two
       properties, so the decision lives in two lines rather than in twenty-two markup files.
       Before GAP-181 the caps were spread across `.rm-page` (1000px), `.rm-ruleset-page` (600px),
       `.rm-ruleset-page-wide` (1400px), `.rm-ruleset-list` (1100px), `.rm-page-narrow` (480px) and
       a per-page `rm-w-*` modifier on eighteen of them.

       WHY THERE ARE TWO AND NOT ONE, stated plainly because it is the one place this does not do
       what "all pages" says on its face. Content pages take `--rm-page-max` and fill the column.
       Nine single-column data-entry forms take `--rm-form-page-max` and keep a readable measure,
       because the directive's stated PURPOSE is "maximum visibility of page content" and a
       forty-character text input stretched across a 2560px monitor is less legible, not more.
       That is a judgement, it is recorded in GAP-181, and `sidebar-collapse.js` check 15 asserts
       it so that it cannot change in either direction by accident. If it is the wrong call, set
       `--rm-form-page-max: 100%` and every form widens too — one line, no markup change.

       AMENDED 2026-09-18 (GAP-193), Product Owner: "the width is too wide, which is eliminating a
       proper view of the page ... apply general good layout design principles."

       `--rm-page-max` was `100%`, so content grew without limit: measured on a 2000px viewport,
       `.rm-group-page` rendered 1780px wide with its right edge at exactly 2000 — a gutter of
       ZERO — and the explanatory paragraphs ran about 250 characters per line. "Maximum width"
       and "no maximum" are not the same instruction, and GAP-181 implemented the second.

       It is capped rather than reverted. The 1000px/600px/1400px/1100px/480px spread GAP-181
       replaced is not worth going back to, and the directive that produced it — content gets the
       space — still holds below the cap. What the cap adds is the upper bound every measure needs:
       a table wider than 1440px still scrolls inside its own `.table-responsive` wrapper, which is
       the behaviour that wrapper exists for.

       `--rm-gutter` is the other half and the more visible one. Nothing here ever set a side
       inset except `.rm-page`, so a `.rm-group-page` or `.rm-registry-page` put its content flush
       against the window edge and against the sidebar. It is applied ONCE, on `.rm-main`, rather
       than on each of the five containers: a container added later inherits the gutter instead of
       having to remember it, and `.rm-page`'s own `padding: 0 24px` was removed in the same change
       so the two do not add up to 48. */
    --rm-page-max: 1440px;
    --rm-form-page-max: 640px;
    --rm-gutter: 24px;

    /* GAP-193 — a readable measure for explanatory prose. `.rm-field-hint` is on 29 pages and had
       no cap, so every hint stretched to whatever the container was. 75ch is roughly 75 characters
       per line; the typography convention is 45-75 and the upper end suits short captions. This
       caps the LINE, not the field above it — the input keeps the column's width. */
    --rm-prose-max: 75ch;

    /* GAP-269 — Product Owner, 2026-09-21: "Adjust the page width in section above the table ...
       I want to minimize section height and increase UX."

       A PAGE INTRODUCTION is not a field hint. The register's intro was wearing .rm-field-hint,
       whose 75ch measure exists for helper text under a control, so 430 characters of introduction
       wrapped to FIVE lines inside a 500px box in a 1380px column - 116px, the largest single item
       in a 399px section, and the thing the Product Owner turned the cap off to look at.

       Still capped, not removed. GAP-193 set the cap because an uncapped hint ran 250 characters
       to a line at 2000px, which the eye cannot track back to the start of; the full 1380px column
       would be about 207. This is the widest measure that still reads as prose, and it takes the
       paragraph from five lines to three. */
    --rm-intro-max: 950px;
}

/* GAP-193 — below Bootstrap's `sm` a 24px inset on each side costs 48 of a 390px viewport, which
   is a seventh of the screen. 16px is the common floor and is what the phone layouts here need. */
@media (max-width: 575.98px) {
    :root {
        --rm-gutter: 16px;
    }
}

* {
    box-sizing: border-box;
}

/* GAP-140 — BOOTSTRAP BRIDGE. Bootstrap 5.3.8 is linked ahead of this file in _Layout.cshtml.
   5.3 is built on CSS custom properties, so its components read --bs-* at use time rather than
   baking values in at compile time. That makes the two design systems reconcilable without
   forking Bootstrap or overriding it rule by rule: point --bs-* at the --rm-* tokens above and
   Bootstrap adopts this app's palette and type instead of imposing its own.

   Why this is the whole of the integration. GAP-60 declined to link Bootstrap because "its Reboot
   and table/button components would restyle every page of this app's own design system at once",
   and that was correct at 5.1. It is avoidable at 5.3 through these variables plus load order.
   The measured collision surface is five class names the markup already carried and that were
   inert until now — .btn, .card-link, .form-control, .table-responsive and .text-danger — of
   which this file already defines .btn, .table-responsive and .text-danger and, loading second,
   still wins.

   --rm-danger is #dc3545, which IS Bootstrap's own $danger, so .text-danger renders identically
   either way. That matters because .text-danger is a utility and therefore !important: it is the
   one collision load order cannot settle, and it happens to need no settling. Checked, not assumed.

   Anything NOT listed here keeps Bootstrap's default on purpose — the grid, spacing scale and
   utilities are what TEC-007 asks the app to gain, and they are unopinionated about colour. */
:root {
    --bs-font-sans-serif: var(--rm-font-body);
    --bs-body-font-family: var(--rm-font-body);
    --bs-body-font-size: 15px;
    --bs-body-line-height: 1.6;
    --bs-body-color: var(--rm-body-text);
    --bs-body-bg: #F9FAFB;
    --bs-emphasis-color: var(--rm-text);
    --bs-secondary-color: var(--rm-muted);
    --bs-border-color: var(--rm-border);
    --bs-link-color: var(--rm-accent);
    --bs-link-hover-color: var(--rm-accent-hover);
    --bs-primary: var(--rm-primary);
    --bs-danger: var(--rm-danger);
    --bs-heading-color: var(--rm-text);
}

html, body {
    margin: 0;
    padding: 0;
}

body {
    font-family: var(--rm-font-body);
    font-size: 15px;
    line-height: 1.6;
    color: var(--rm-body-text);
    background: #F9FAFB;
}

h1, h2, h3, h4, h5, h6 {
    font-family: var(--rm-font-heading);
    color: var(--rm-text);
    margin: 0 0 .5em;
    letter-spacing: .01em;
    /* GAP-140: Bootstrap's Reboot sets `line-height: 1.2` on headings; these had been inheriting
       the body's 1.6, which put an h1 at 48px rather than 36px. `inherit` rather than a literal
       1.6 so the two stay tied if the body's value is ever changed. */
    line-height: inherit;
}

/* GAP-140 — heading size and weight pinned to what this app rendered BEFORE Bootstrap was linked.
   The rule above sets family, colour, margin and tracking but never size or weight, so every
   heading in the app was inheriting the browser's defaults. Bootstrap's Reboot sets both
   (`font-weight: 500` for all levels, and a viewport-scaled `font-size` for h1/h2/h3), so linking
   it moved h1 from 30px/700 to 40px/500 — measured, not assumed.

   Restored in `em` rather than `px` because that is the unit the browser default uses: a heading
   inside a 14px table scales from its parent exactly as it always did, which a pixel value would
   silently change. Values are the CSS2.1 defaults the app was relying on. h4 and h6 are unused in
   the markup today and are listed anyway, so adding one later does not quietly get Bootstrap's. */
/* ===========================================================================================
   GAP-140 — REBOOT COMPATIBILITY. Everything below restores a rendering this app had BEFORE
   Bootstrap was linked, and nothing below is a new design choice.

   The differences all share one cause. Bootstrap's Reboot exists to replace the browser's default
   stylesheet, and this app had been relying on that default in the places it never styled itself:
   heading size and weight, paragraph margins, checkbox and radio margins, the font of the control
   types its own input rule does not name, and the cell borders of the twelve tables that still
   carry the presentational `border="1"` attribute. Where the app DID style something, load order
   already settles it — this file is linked after Bootstrap in _Layout.cshtml.

   Found by diffing 33 computed properties of all 792 elements across eight pages, before against
   after, not by reading Bootstrap's source and guessing. `revert` is used wherever the intent is
   literally "whatever the browser would have done", because naming Chrome's internal values
   (`13.3333px Arial`, from `font: -webkit-small-control`) would hard-code one engine's defaults.
   =========================================================================================== */

/* <button> only, never `.btn`. They share every other rule in this file but not a browser default:
   the UA gives <button> `line-height: normal` and gives <a class="btn"> nothing, so the anchor
   inherits the body's 1.6. An earlier attempt at this put `line-height: normal` on `button, .btn`,
   which shrank every anchor-button from 40.8px to 35px and moved 87 elements up the page. */
button {
    line-height: normal;
}

/* Bootstrap's `.btn` COMPONENT (not Reboot) sets three properties this file does not, so they are
   the ones that leak through to anchors marked up as buttons. */
a.btn {
    line-height: inherit;
    text-align: inherit;
    vertical-align: baseline;
}

/* Reboot normalises `margin: 0` onto every control. On a checkbox or radio that is the 3px/4px gap
   between the box and its label text, which is why every .rm-nowrap label measured 7px narrower. */
input[type="checkbox"],
input[type="radio"] {
    margin: revert;
}

/* Reboot sets `font-family/font-size/line-height: inherit` on form controls. The app's own input
   rule above names text, email, password, bare-type, select and textarea — these are the rest, and
   they had been rendering in the browser's form-control font. */
input[type="checkbox"],
input[type="radio"],
input[type="number"],
input[type="file"],
input[type="submit"],
input[type="button"],
input[type="reset"],
input[type="search"] {
    font: revert;
    line-height: revert;
}

/* Reboot sets `margin-top: 0; margin-bottom: 1rem`; the browser default is `1em` top and bottom,
   which is what the card and panel heights on every page were built around. A `p` carrying an
   rm-* class keeps that class's margins — a class beats an element selector. */
p {
    margin: revert;
}

/* Same cause as `p`. Reboot sets `margin-top: 0; margin-bottom: 1rem; padding-left: 2rem` on lists,
   where the browser default is `margin: 1em 0; padding-left: 40px`. The visible effect was on the
   Login page: its validation summary renders an empty <ul>, whose 1px taller bottom margin pushed
   the whole form — labels, both fields and the button — down by one pixel. */
ul, ol {
    margin: revert;
    padding: revert;
}

/* `.form-control` is one of five Bootstrap class names this app's markup already carried while
   Bootstrap was unlinked, so it was inert and is now live. The app's own input rule wins on
   specificity for everything it names — `input[type="text"]` (0,1,1) beats `.form-control` (0,1,0)
   — but it never sets `display`, and Bootstrap's component does: `block` where the browser default
   is `inline-block`. Reverted rather than hard-coded so a <select> or <textarea> carrying the class
   gets its own correct default rather than an input's. */
.form-control {
    display: revert;
}

/* Twelve tables still use the HTML presentational attribute `border="1"` (Registry Categories,
   Form Groups and Specifications; Sources Index, Mapping and Upload; External Index and Details).
   Reboot's `border-width: 0` on table cells overrides a presentational hint, so those grids lost
   their cell borders outright. Bottom is deliberately not set here: `th, td` above owns it and
   draws the designed 1px --rm-border rule. `currentColor` is the initial value that the hint was
   resolving against before Reboot changed it to `inherit`. */
table[border] > tr > th,
table[border] > tr > td,
table[border] > thead > tr > th,
table[border] > thead > tr > td,
table[border] > tbody > tr > th,
table[border] > tbody > tr > td,
table[border] > tfoot > tr > th,
table[border] > tfoot > tr > td {
    border-top-width: 1px;
    border-right-width: 1px;
    border-left-width: 1px;
    /* Three sides, never the shorthand: `border-color` here would also override
       border-bottom-color, and this block sits further down the file than `th, td`, which draws
       the designed 1px --rm-border rule under every row. Setting the shorthand turned every grid's
       row separator from #D0D5DD to the text colour.

       `gray` because that is what the presentational hint actually resolved to, sampled from the
       rendered pixels at (330,322) on /Registry/Categories: rgb(128,128,128). The first attempt
       here used `currentColor`, which rendered rgb(102,112,133) — --rm-muted, the cell's own text
       colour. Both are plausible readings of the spec; only one matches what the browser did. */
    border-top-color: gray;
    border-right-color: gray;
    border-left-color: gray;
}

h1 { font-size: 2em;    font-weight: bold; }
h2 { font-size: 1.5em;  font-weight: bold; }
h3 { font-size: 1.17em; font-weight: bold; }
h4 { font-size: 1em;    font-weight: bold; }
h5 { font-size: .83em;  font-weight: bold; }
h6 { font-size: .67em;  font-weight: bold; }

a {
    color: var(--rm-accent);
    text-decoration: none;
}

    a:hover, a:focus {
        color: var(--rm-accent-hover);
        text-decoration: underline;
    }

/* ---------- Header shell ---------- */

.rm-header {
    position: sticky;
    top: 0;
    z-index: 1000;
    background: var(--rm-primary);
    color: #fff;
}

.rm-header-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    height: var(--rm-header-height);
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 24px;
}

.rm-brand {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    font-family: var(--rm-font-heading);
    font-size: 16px;
    font-weight: 600;
    color: #fff;
}

    .rm-brand:hover {
        color: #fff;
        text-decoration: none;
    }

.rm-brand-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 8px;
    background: rgba(255, 255, 255, .12);
    font-weight: 700;
}

.rm-nav {
    display: flex;
    align-items: center;
    gap: 4px;
    flex: 1 1 auto;
    margin-left: 24px;
}

    .rm-nav a {
        display: inline-flex;
        align-items: center;
        padding: 8px 12px;
        border-radius: 6px;
        color: rgba(255, 255, 255, .85);
        font-family: var(--rm-font-heading);
        font-size: 13px;
        font-weight: 500;
        letter-spacing: .02em;
        text-transform: uppercase;
    }

        .rm-nav a:hover, .rm-nav a:focus {
            background: rgba(255, 255, 255, .12);
            color: #fff;
            text-decoration: none;
        }

/* ---------- GAP-185: the units preference control (GWMApp header, source 125-138) ----------
   GWMApp labels this `sr-only` and styles it with `styled-select-units gwm-header-select`, which
   are its design system's names. The control is the same control; the class names are this
   application's, as everywhere else on this page. */
.rm-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.rm-units {
    display: flex;
    align-items: center;
}

.rm-units-select {
    font: inherit;
    /* `color: inherit` explicitly, NOT only `font`. GAP-178 shipped a pager whose labels were
       invisible for exactly this omission: `font: inherit` carries no colour, so the control took
       the app's global control colour against the dark header and could not be read. */
    color: inherit;
    background: rgba(255, 255, 255, .12);
    border: 1px solid rgba(255, 255, 255, .35);
    border-radius: 4px;
    padding: 4px 8px;
}

    .rm-units-select option {
        /* The dropdown list is painted by the platform on its own surface, not on the header, so
           it needs colours that work there rather than the header's white-on-blue. */
        color: #111;
        background: #fff;
    }

.rm-user {
    display: flex;
    align-items: center;
    gap: 12px;
    font-size: 13px;
    color: rgba(255, 255, 255, .85);
}

    .rm-user form {
        margin: 0;
    }

.rm-user-name {
    font-weight: 600;
    color: #fff;
}

.rm-user-roles {
    font-weight: 400;
    color: rgba(255, 255, 255, .7);
    margin-left: 4px;
}

/* GAP-285 - THE HEADER SHRINKS INSTEAD OF OVERFLOWING. Measured before this: 60px of horizontal
   overflow at 320px and 20px at 360px, with the Log out button's right edge at 380px against a
   320px viewport - i.e. off screen, which is what the reporting screenshot shows.

   The cause is that every child of .rm-header-inner was unshrinkable: a flex item's default
   min-width is auto, so the identity block could not go below its max-content width and the
   button was pushed out rather than the text giving way. min-width: 0 lets the longest string -
   the role list, "(PlatformAdministrator)" - elide, and "flex: none" on the form keeps the
   button itself at full size. Nothing is hidden; the name truncates with an ellipsis and its
   full value stays in the title attribute.

   Applied at every width rather than behind a breakpoint, because on a wide screen there is
   slack and these rules do nothing - which is preferable to a second breakpoint that can drift
   out of step with the one the shell already uses. */
.rm-header-inner > * {
    min-width: 0;
}

.rm-brand {
    flex: 0 1 auto;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

    .rm-brand-mark {
        flex: none;
    }

.rm-user {
    flex: 0 1 auto;
}

    .rm-user .rm-user-name {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* The control must never be what gives way - it is the only way out of the application. */
    .rm-user form {
        flex: none;
    }

/* GAP-285 - the mobile navigation trigger. Hidden above the breakpoint, where the rail and its
   own Collapse control do the job.

   A <button type="button"> carrying aria-label="Open navigation menu" is GWMApp's own trigger
   pattern - #mainMenu-trigger, source 470-474 of the Range Settings capture - which
   sidebar-collapse.js already records as the one citable part of this control. The bars are drawn
   rather than iconographed so the file carries no image dependency. */
.rm-nav-hamburger {
    display: none;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, .35);
    border-radius: 8px;
    background: #fff;
    cursor: pointer;
}

    .rm-nav-hamburger:hover, .rm-nav-hamburger:focus {
        background: rgba(255, 255, 255, .88);
    }

    /* Three bars, becoming an X when the panel is open - the state change GWMApp's own button
       makes, and the thing that tells a reader the same button closes it again. */
    .rm-nav-hamburger .rm-hamburger-bars {
        position: relative;
        display: block;
        width: 20px;
        height: 2px;
        background: var(--rm-primary);
    }

        .rm-nav-hamburger .rm-hamburger-bars::before,
        .rm-nav-hamburger .rm-hamburger-bars::after {
            content: "";
            position: absolute;
            left: 0;
            width: 20px;
            height: 2px;
            background: var(--rm-primary);
        }

        .rm-nav-hamburger .rm-hamburger-bars::before {
            top: -6px;
        }

        .rm-nav-hamburger .rm-hamburger-bars::after {
            top: 6px;
        }

.rm-nav-open .rm-nav-hamburger .rm-hamburger-bars {
    background: transparent;
}

    .rm-nav-open .rm-nav-hamburger .rm-hamburger-bars::before {
        top: 0;
        transform: rotate(45deg);
    }

    .rm-nav-open .rm-nav-hamburger .rm-hamburger-bars::after {
        top: 0;
        transform: rotate(-45deg);
    }

/* GAP-286 — the only control an anonymous reader is offered. Product Owner, 2026-09-25: "The
   Navigation shows 'Login' only. No other header information."

   It borrows the Log out button's shape rather than inventing a second header-control style:
   same border, radius, padding and type scale, on the same navy. The two are never on screen
   together - one is the authenticated branch of the header, the other the anonymous one - so a
   reader only ever sees one control in that position, and it looks the same either way. */
.rm-header-login {
    flex: none;
    border: 1px solid rgba(255, 255, 255, .35);
    border-radius: 6px;
    padding: 6px 12px;
    font-family: var(--rm-font-heading);
    font-size: 12px;
    font-weight: 500;
    color: #fff;
}

    .rm-header-login:hover, .rm-header-login:focus {
        background: rgba(255, 255, 255, .12);
        color: #fff;
        text-decoration: none;
    }

.rm-logout-button {
    flex: none;
    background: transparent;
    border: 1px solid rgba(255, 255, 255, .35);
    color: #fff;
    border-radius: 6px;
    padding: 6px 12px;
    font-family: var(--rm-font-heading);
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
}

    .rm-logout-button:hover {
        background: rgba(255, 255, 255, .12);
    }

/* ---------- Page shell ---------- */

.rm-page {
    max-width: var(--rm-page-max);   /* GAP-181 — was 1000px; GAP-193 capped the token at 1440px */
    margin: 2.5rem auto;
    /* GAP-193 — `padding: 0 24px` removed. `.rm-main` now carries the gutter for every page, and
       leaving this here would have made the eighteen `.rm-page` pages inset 48px while the other
       eleven were inset 24. The inconsistency is the thing being fixed, so it is removed rather
       than copied to the others. */
}

/* GAP-181 — was 480px. The login box, the four user-administration forms and the logout
   confirmation: single-column forms, and the one group that deliberately does NOT fill the
   column. See the token block for why, and for the one line that changes it. */
.rm-page-narrow {
    max-width: var(--rm-form-page-max);
}

/* GAP-181 — the same measure, for the single-column Create/Edit pages that previously carried
   `rm-w-500`, `rm-w-560`, `rm-w-600`, `rm-w-700` or `rm-w-900`. One class instead of five
   near-identical widths: the exact figure was never a decision, it was whatever looked right on
   the day each page was written.

   IT LIVES HERE, NOT IN Registry.css, and that is not tidiness. The per-area stylesheets are
   linked page by page through the `Styles` section — `Registry.css` is on the registry pages and
   nowhere else — so a class defined there and used on `RulesetGroups/Create` or `External/Create`
   would silently do nothing on exactly the pages that need it most. Written there first; caught
   by reading which pages link which sheet, before any of them was opened.

   IT REDEFINES THE VARIABLE RATHER THAN SETTING max-width, and that is the whole reason it works.
   The obvious version — `.rm-form-page { max-width: var(--rm-form-page-max); }` — LOST, silently,
   on every registry and ruleset-group form. Both `.rm-form-page` and `.rm-registry-page` are
   single-class selectors, so specificity ties and source order decides; the per-area sheets are
   linked AFTER this one, so the container's `100%` won and every form rendered full width.

   Setting the custom property instead sidesteps the contest entirely: the element redefines
   `--rm-page-max` for itself, and the container's own `max-width: var(--rm-page-max)` — whichever
   sheet that rule came from — resolves to the form measure. No specificity hack, no dependence on
   link order, and a future area stylesheet cannot break it by being added later.

   Found by `sidebar-collapse.js` check 15, which measured 1380px where it expected 640. Every
   other check passed: the pages rendered, the forms worked, nothing looked broken — they were
   just the wrong width, which is precisely the failure a screenshot shows and a test suite does
   not. */
.rm-form-page {
    --rm-page-max: var(--rm-form-page-max);
}

/* ---------- Form controls ---------- */

label {
    display: block;
    font-family: var(--rm-font-heading);
    font-size: 13px;
    font-weight: 500;
    color: var(--rm-text);
    margin-bottom: 4px;
}

/* `input:not([type])` is here because an input with no type attribute IS a text input by spec, and
   sixteen of them across this application were therefore missing every rule below — rendering at the
   browser's default ~177px instead of filling their field. Found in GAP-108 when the Ruleset Name
   box came out a third of the width of its column. Same class of defect as the textarea height. */
input[type="text"], input:not([type]), input[type="email"], input[type="password"], select, textarea {
    line-height: normal;
    width: 100%;
    height: 38px;
    padding: 6px 10px;
    font-size: 14px;
    font-family: var(--rm-font-body);
    color: var(--rm-text);
    background: #fff;
    border: 1px solid var(--rm-border);
    border-radius: 8px;
    transition: border-color .15s ease, box-shadow .15s ease;
}

/* GAP-107. The rule above sets height: 38px for inputs, selects AND textareas — so every
   multi-line textarea in this application rendered exactly one line tall, whatever its rows
   attribute said. Roughly twenty forms author rows="2".."6" and not one of them was getting it;
   the External Finding review page, where a reviewer must see the whole variable list at once, is
   where it finally became impossible to ignore.

   Height follows rows, as the author wrote it. The two grid contexts are pinned back to 38px
   because there a textarea is a table cell and its height is the row height — the shape GAP-57/59/62
   settled, which this must not disturb. */
textarea {
    height: auto;
}

.rm-lookup-grid td textarea,
.rm-resizable-table td textarea {
    height: 38px;
}

    input[type="text"]:focus, input:not([type]):focus, input[type="email"]:focus, input[type="password"]:focus, select:focus, textarea:focus {
        outline: none;
        border-color: var(--rm-accent);
        box-shadow: 0 0 0 3px rgba(59, 91, 219, .15);
    }

    input.input-validation-error, select.input-validation-error {
        border-color: var(--rm-danger);
        background-color: var(--rm-danger-bg);
    }

.text-danger, .field-validation-error {
    color: var(--rm-danger);
    font-size: 12.5px;
}

.field-validation-valid {
    display: none;
}

/* ---------- Buttons ---------- */

/* GAP-102: the `hidden` attribute must win over every display rule in this file.
   `button, .btn { display: inline-flex }` below is an AUTHOR rule, and any author rule beats the
   user-agent stylesheet's `[hidden] { display: none }` regardless of specificity — so before this,
   EVERY `<button hidden>` in the application rendered anyway. Found by the no-JavaScript check in
   specification-modal-picker.js, which saw a dead trigger button on a page whose script had not
   run. !important because the whole point is to outrank whatever a component sets. */
[hidden] {
    display: none !important;
}

button, .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 9px 18px;
    border-radius: 8px;
    border: 1px solid var(--rm-primary);
    background: var(--rm-primary);
    color: #fff;
    font-family: var(--rm-font-heading);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: .02em;
    cursor: pointer;
    transition: background-color .15s ease, border-color .15s ease;
}

    button:hover, .btn:hover {
        background: var(--rm-primary-hover);
        border-color: var(--rm-primary-hover);
    }

    button.rm-btn-outline, .btn.rm-btn-outline {
        background: transparent;
        color: var(--rm-primary);
    }

        button.rm-btn-outline:hover, .btn.rm-btn-outline:hover {
            background: var(--rm-hover-bg);
        }

/* GAP-269 — `color` is load-bearing here and its absence was a real defect.
   
   `background: transparent` removes the navy fill, and WITHOUT a colour beside it the label keeps
   the white text from the `button, .btn` rule above. White on the page's #F9FAFB, softened to 45%
   opacity, measures a contrast ratio of about 1.05:1 — not "greyed out", INVISIBLE. That is
   GAP-178's defect exactly, and Registry.css line 70 already carries a comment warning about this
   precise pairing for the sort buttons; the global disabled rule had it anyway.

   Where it showed: the register's Previous/Next pager, which is disabled whenever there is only one
   page of results. The Product Owner reported a hard-to-read Previous control on 2026-09-21 and it
   was recorded as NO DEFECT on the reasoning that a disabled control should look faint. The
   reasoning was right and the conclusion was wrong — measuring it is what settled it, which is why
   breadcrumb-pager-and-status-column.js check 12 now reads the computed colour against the
   effective background rather than merely asserting a colour exists. */
button:disabled, .btn:disabled {
    /* .45 was the original and it is the reason the label measured 1.05:1 even after the colour
       was corrected: opacity multiplies the text toward the background, so a perfectly good muted
       grey still washes out. .85 keeps the control visibly inactive — no fill, muted text,
       not-allowed cursor — while the label measures 3.58:1 against the register's #F9FAFB.
       The figure is asserted rather than asserted-about: see check 12 of
       breadcrumb-pager-and-status-column.js, which computes it from the live page. */
    opacity: .85;
    cursor: not-allowed;
    background: transparent;
    color: var(--rm-muted);
    border-color: var(--rm-border);
}

    button:disabled:hover, .btn:disabled:hover {
        background: transparent;
    }

/* ---------- Status messages ---------- */

.rm-status-message {
    padding: 10px 14px;
    border-radius: 8px;
    background: var(--rm-hover-bg);
    color: var(--rm-text);
    font-size: 14px;
    margin-bottom: 1rem;
}

/* A refused action, not a completed one — GAP-106. The neutral box above is the same one a success
   uses, and a refusal that looks like a confirmation is how a blocked approval gets read as a
   finished one. */
.rm-status-message--refused {
    background: #FEF3F2;
    border-left: 4px solid #B42318;
    color: #912018;
}

/* ---------- Tables ---------- */

table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}

th, td {
    padding: 10px 12px;
    text-align: left;
    border-bottom: 1px solid var(--rm-border);
}

thead th {
    font-family: var(--rm-font-heading);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--rm-muted);
    background: var(--rm-hover-bg);
}

/* Per-column sort links (Registry/Variables and any future grid using the same pattern) — a
   plain <a>, not a button, since it's a GET navigation, not a form action; styled to read as
   part of the header label rather than as separate link text. */
.rm-sort-link {
    color: inherit;
    text-decoration: none;
}

.rm-sort-link:hover {
    text-decoration: underline;
}

/* The per-column filter row directly under the header labels — compact controls so the row
   doesn't blow out the header's height, and normal-case/left-aligned text since these are inputs
   a user reads and types into, not label text. */
.rm-col-filter-row th {
    background: var(--rm-hover-bg);
    padding-top: 4px;
    padding-bottom: 8px;
    text-transform: none;
    letter-spacing: normal;
    vertical-align: top;
}

.rm-col-filter-row input[type="text"], .rm-col-filter-row select {
    height: 30px;
    padding: 2px 6px;
    font-size: 12px;
    min-width: 90px;
}

tbody tr:hover {
    background: var(--rm-hover-bg);
}

.rm-status-pill {
    display: inline-flex;
    align-items: center;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
}

.rm-status-active {
    background: #E6F4EA;
    color: #1E7A34;
}

.rm-status-suspended {
    background: #FDECEC;
    color: #B42318;
}

/* ---------- Cards / sectioned forms (Registry Create/Edit pages) ---------- */

.rm-form-columns {
    display: grid;
    grid-template-columns: 1.4fr 1fr;
    gap: 20px;
    align-items: start;
}

/* GAP-193 — was 720px. Bootstrap 5.3 is linked ahead of this file and seventeen pages use its
   grid, whose `md` boundary is 767.98px. A two-column form that collapsed at 720 while a
   `.row`/`.col-md-*` beside it collapsed at 768 reflowed the same page in two stages 48px apart.
   The `.98` is Bootstrap's own convention for a max-width query and avoids a 1px dead zone. */
@media (max-width: 767.98px) {
    .rm-form-columns {
        grid-template-columns: 1fr;
    }
}

.rm-card {
    border: 1px solid var(--rm-border);
    border-radius: 10px;
    background: #fff;
    margin-bottom: 20px;
    overflow: hidden;
}

.rm-card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 16px;
    background: var(--rm-hover-bg);
    border-bottom: 1px solid var(--rm-border);
    font-family: var(--rm-font-heading);
    font-size: 13px;
    font-weight: 600;
    color: var(--rm-text);
}

.rm-card-header-count {
    font-weight: 500;
    color: var(--rm-muted);
    text-transform: none;
    letter-spacing: normal;
}

.rm-card-body {
    padding: 16px;
}

.rm-field {
    margin-bottom: 14px;
}

    .rm-field:last-child {
        margin-bottom: 0;
    }

.rm-field-hint {
    font-size: 12px;
    color: var(--rm-muted);
    margin-top: 4px;
    /* GAP-193 — a hint is prose and needs a measure. Uncapped, the welding-process caption on
       RulesetGroups/Details ran the full 1780px column at 2000px: one line of about 250
       characters, which the eye cannot track back to the start of. */
    max-width: var(--rm-prose-max);
}

.rm-radio-row {
    display: flex;
    flex-wrap: wrap;
    gap: 18px;
}

.rm-radio-option, .rm-check-option {
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: var(--rm-font-body);
    font-size: 14px;
    font-weight: 400;
    color: var(--rm-text);
    margin-bottom: 0;
    cursor: pointer;
}

    .rm-radio-option input[type="radio"], .rm-check-option input[type="checkbox"] {
        width: 16px;
        height: 16px;
        accent-color: var(--rm-accent);
        cursor: pointer;
    }

.rm-check-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px 16px;
}

.rm-check-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.rm-button-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 4px;
}

.rm-button-row-start {
    display: flex;
    gap: 10px;
}

button.rm-btn-danger, .btn.rm-btn-danger {
    background: var(--rm-danger);
    border-color: var(--rm-danger);
}

    button.rm-btn-danger:hover, .btn.rm-btn-danger:hover {
        background: #b02a37;
        border-color: #b02a37;
    }

/* ---------- Sidebar shell ---------- */

.rm-shell {
    display: flex;
    align-items: flex-start;
    min-height: calc(100vh - var(--rm-header-height));
}

.rm-sidebar {
    flex: 0 0 220px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 20px 12px;
    background: #fff;
    border-right: 1px solid var(--rm-border);
    min-height: calc(100vh - var(--rm-header-height));
    /* `.rm-shell` sets `align-items: flex-start` deliberately (see `.rm-main`'s note, GAP-60), which
       also stops the sidebar growing past its own min-height. On a short page nobody notices; on a
       tall one — the rule editor card is now well past 2000px — the sidebar's white background and
       its border stop partway down and the nav appears to float in the middle of the page (seen in
       GAP-118's browser capture). Stretching THIS child only fixes that without touching the main
       column, which is the one GAP-60's note is actually about. */
    align-self: stretch;
}

    .rm-sidebar a {
        display: block;
        padding: 9px 14px;
        border-radius: 8px;
        font-family: var(--rm-font-heading);
        font-size: 13.5px;
        font-weight: 500;
        color: var(--rm-text);
    }

        .rm-sidebar a:hover, .rm-sidebar a:focus {
            background: var(--rm-hover-bg);
            text-decoration: none;
        }

        .rm-sidebar a.active {
            background: var(--rm-primary);
            color: #fff;
        }

/* GAP-218 — the navigation section headings (Library, Development, Governance,
   Administration). Product Owner, 2026-09-19: "Define Library items and put them in a
   navigation section that is called library."

   A <p>, not a heading element, deliberately: the sidebar sits inside the page's own heading
   outline, and an <h2> here would announce itself to a screen reader as a peer of the page
   title. The grouping is visual and the links are what matter, so it carries no role.

   No top margin on the first one — it follows Home directly and a gap there reads as a
   separator rather than a label. */
.rm-nav-section {
    margin: 18px 0 4px;
    padding: 0 14px;
    font-family: var(--rm-font-heading);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--rm-muted, #6b7280);
}

    .rm-nav-section:first-of-type {
        margin-top: 12px;
    }

.rm-main {
    flex: 1 1 auto;
    min-width: 0;
    /* `.rm-shell` sets `align-items: flex-start` so the sidebar keeps its content height in the
       side-by-side layout. Below 860px the shell turns into a column, and there flex-start means
       neither child is stretched across the shell — so a main region wider than the viewport takes
       its max-content width and drags the whole page sideways with it. `.rm-sidebar` already
       compensates with `width: 100%`; this is the same compensation for the content column, and it
       is inert in the side-by-side layout where the main region is narrower than the shell anyway.
       Without it a wide grid scrolls the PAGE instead of its own `.table-responsive` wrapper, which
       defeats the wrapper entirely (GAP-60). */
    max-width: 100%;

    /* GAP-193 — the side gutter for EVERY page, set here rather than on the five page containers.
       Before this, only `.rm-page` had one; `.rm-group-page`, `.rm-registry-page`,
       `.rm-ruleset-page-wide` and `.rm-ruleset-list` had none, so their content sat flush against
       the window edge and against the sidebar's border. Measured at 2000px: right gutter 0.
       `padding-inline` rather than `padding: 0 X`, so the vertical padding of a future rule here
       is not silently zeroed by this one. */
    padding-inline: var(--rm-gutter);
}

/* ---------- The collapsible navigation (GAP-181) ----------
   Product Owner directive, 2026-09-17. The state class sits on <html> and is put there by an
   inline script in _Layout.cshtml's <head> before first paint, then maintained by
   sidebar-collapse.js — see both for why it is not on the shell.

   COLLAPSING HIDES THE LINKS, NOT THE RAIL. The toggle has to stay reachable or the nav cannot be
   brought back, so the sidebar keeps a narrow column holding just the button. Hiding the links
   rather than shrinking them to icons is deliberate: this application has no icon set, and
   inventing one to label thirteen destinations would be originating UI. */

.rm-nav-toggle {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    margin-bottom: 8px;
    padding: 9px 12px;
    border: 1px solid var(--rm-border);
    border-radius: 8px;
    background: var(--rm-hover-bg);
    font-family: var(--rm-font-heading);
    font-size: 13px;
    font-weight: 500;
    /* GAP-178's lesson, one screen later: this application styles bare `button`, and that rule
       sets white text for a dark fill. A pale button inheriting it renders its label invisible —
       present and correct in the served HTML, unreadable on screen, and passing every check that
       is not a screenshot. `sidebar-collapse.js` check 12 measures the contrast. */
    color: var(--rm-text);
    cursor: pointer;
    white-space: nowrap;
}

    .rm-nav-toggle:hover, .rm-nav-toggle:focus {
        background: #E4E7EC;
        color: var(--rm-text);
    }

    .rm-nav-toggle [data-rm-nav-toggle-chevron] {
        font-size: 16px;
        line-height: 1;
    }

.rm-nav-collapsed .rm-sidebar {
    flex: 0 0 auto;
    padding: 20px 8px;
}

    /* The links, and nothing else. `> a` rather than `a` so that a future control inside the
       sidebar has to opt into disappearing rather than doing it by accident. */
    .rm-nav-collapsed .rm-sidebar > a {
        display: none;
    }

    /* GAP-219 — the section headings opt in, exactly as the comment above says a new control must.
       They did not, and it cost a real defect that only a MEASUREMENT caught.

       Collapsing hid the links and left "LIBRARY", "DEVELOPMENT", "GOVERNANCE", "ADMINISTRATION"
       standing in the rail — uppercase, letter-spaced, and wider than the rail is meant to be. The
       collapse still worked, the toggle still worked, and sidebar-collapse.js check 7 ("no sidebar
       link is visible when collapsed") still PASSED, because a heading is not a link. Only check 9,
       which measures the main column, saw it: the content gained 68px where it should gain over
       100. */
    .rm-nav-collapsed .rm-sidebar > .rm-nav-section {
        display: none;
    }

    .rm-nav-collapsed .rm-nav-toggle {
        justify-content: center;
        gap: 0;
        margin-bottom: 0;
        padding: 9px 10px;
    }

        /* The word goes, the chevron stays — that is what makes the rail narrow. It is removed
           from the box rather than from the accessibility tree: the button's own `aria-label`
           carries "Expand navigation" in this state, which is what a screen reader announces. */
        .rm-nav-collapsed .rm-nav-toggle [data-rm-nav-toggle-text] {
            display: none;
        }

/* Below 860px the shell is already a stacked column and the sidebar is a horizontal strip of
   wrapped links — there is no width to win back, so the collapse has nothing to do. Leaving it
   active would hide the whole navigation behind a button on exactly the screens where the nav is
   hardest to reach. */
/* GAP-193 — was 860px, now Bootstrap's `lg` boundary. Same reason as the `md` query above, and
   it also helps the content: at 900px the old rule kept the 220px rail, leaving a 680px column
   for tables. Stacking through to 991.98px gives those pages the whole viewport. */
@media (max-width: 991.98px) {
    .rm-nav-collapsed .rm-sidebar > a {
        display: block;
    }

    /* GAP-219 — the mirror. Below this breakpoint the collapse does nothing, so the headings come
       back with the links; without this they would stay hidden on exactly the screens where the
       navigation is hardest to read. */
    .rm-nav-collapsed .rm-sidebar > .rm-nav-section {
        display: block;
    }

    .rm-nav-toggle {
        display: none;
    }

    .rm-shell {
        flex-direction: column;
    }

    /* GAP-285 - THE MOBILE NAVIGATION IS A HAMBURGER AND A STACKED PANEL, per GWMApp's own
       mobile header, supplied by the Product Owner 2026-09-25: "This is what I expect from the
       header and nav for RM too."

       WHAT THE CAPTURES SHOW. A one-row navy header: the brand block on the left, and on the
       right a white rounded-square button of three bars beside the language pill. Tapping it
       turns the bars into an X and drops a navy panel across the full width, its links stacked
       ONE PER LINE in caps with generous vertical padding, overlaying the page rather than
       pushing it down.

       THIS REVERSES A RECORDED DECISION, which is worth saying plainly rather than quietly
       overwriting. The comment above this block argued the collapse should be switched OFF below
       this breakpoint because it "would hide the whole navigation behind a button on exactly the
       screens where the nav is hardest to reach". The Product Owner has now decided the opposite
       and supplied the source. What the old reading cost, measured before this change: the
       sidebar became a wrapping ROW in which the section headings were flex items like the links,
       so at 768px NINE of the ten links rendered under the wrong heading, and at 412px "Rulesets"
       sat between the DEVELOPMENT and GOVERNANCE labels.

       SEPARATE STATE FROM THE DESKTOP COLLAPSE, deliberately. rm-nav-collapsed means "shrink the
       rail" and is remembered; rm-nav-open means "the menu is showing" and is NOT, because a menu
       that is open when you arrive is not a menu. Sharing one class would have made a desktop
       collapse decide whether a phone's menu was open. */
    .rm-sidebar {
        flex: none;
        flex-direction: column;
        flex-wrap: nowrap;
        width: 100%;
        border-right: none;
        border-bottom: 1px solid var(--rm-border);
        min-height: 0;
        align-self: auto;

        /* Overlays the content instead of displacing it - GWMApp's panel covers the paragraph
           beneath it in the supplied capture. Fixed to the viewport under the sticky header, and
           scrollable in its own right so a long menu on a short phone stays reachable. */
        position: fixed;
        top: var(--rm-header-height);
        left: 0;
        right: 0;
        max-height: calc(100vh - var(--rm-header-height));
        overflow-y: auto;
        z-index: 900;
        box-shadow: 0 10px 24px rgba(0, 0, 0, .18);
        padding: 8px 12px 16px;

        /* Closed by default. display:none rather than a transform so the links are genuinely out
           of the tab order while the menu is shut, not merely off screen. */
        display: none;
    }

    .rm-nav-open .rm-sidebar {
        display: flex;
    }

        /* One per line, which a column already gives; this is the tap target. GWMApp's own rows
           are tall and full width. */
        .rm-sidebar a {
            padding: 12px 14px;
        }

    /* The header hamburger is the only nav control below this breakpoint. */
    .rm-nav-hamburger {
        display: inline-flex;
    }

    /* The header carries the brand, the units picker and the hamburger - GWMApp's mobile shape
       exactly. Who is signed in, and the way out, move into the panel below. Measured before
       this: at 320px the Log out button's right edge was 60px past the viewport. */
    .rm-header .rm-user {
        display: none;
    }

    .rm-nav-user {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 8px;
        margin: 4px 0 10px;
        padding: 10px 14px;
        border-bottom: 1px solid var(--rm-border);
    }

        .rm-nav-user .rm-logout-button {
            border-color: var(--rm-border);
            color: var(--rm-text);
        }

        /* Its own class rather than reusing .rm-user-name, because that one is hidden with the
           header block above and would take this with it. RmClassResolutionTests caught it
           missing: a class a partial uses with no rule behind it is a rule that silently does
           nothing (GAP-127, GAP-250). */
        .rm-nav-user-name {
            font-weight: 600;
            color: var(--rm-text);
        }

        .rm-nav-user .rm-user-roles {
            color: var(--rm-muted, #6b7280);
        }
}

/* Above the breakpoint the panel's copy is dead weight: the header's own block is showing.

   A min-width QUERY, not a bare rule, and the difference is a defect this change actually
   shipped for one measurement. Written bare, this sits LATER in the file than the
   max-width: 991.98px block that sets `display: flex`, carries the identical specificity, and so
   won on every phone — the panel's Log out button rendered at zero width while every other check
   passed, because the element was present, carried its text and pointed at the right form. Only
   reading its bounding box found it. Scoping it to the widths it is actually for makes the two
   rules mutually exclusive instead of order-dependent. */
@media (min-width: 992px) {
    .rm-nav-user {
        display: none;
    }
}

/* ---------- List-page filter rows / wide tables (Registry grids) ---------- */

.rm-filter-row {
    display: flex;
    gap: 16px;
    align-items: flex-end;
    flex-wrap: wrap;
    margin-bottom: 16px;
}

/* GAP-60: every table in the app scrolls horizontally rather than forcing the page to.
   `.table-responsive` is Bootstrap 5's own class name for exactly this, defined here rather than
   pulled from Bootstrap: Bootstrap 5.1.0 is vendored in wwwroot/lib/ but deliberately not linked,
   because its Reboot and table/button components would restyle every page of this app's own design
   system at once. Using its name keeps the markup Bootstrap-compatible, so linking Bootstrap later
   is a straight swap rather than a rename of every table. `.rm-table-wrap` is the pre-existing
   name for the same thing and stays as an alias so no existing page had to change. */
.rm-table-wrap,
.table-responsive {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* GAP-60: drag-to-resize column headers, ported from GWMApp's own resizable Lookup Table
   (Instructions/Source_Inputs/GWMApp_Source/GetRuleset.cshtml — `.resize-handle`,
   `.resizable-table`, `body.resizing-column`), restyled onto this app's own tokens instead of
   GWMApp's hard-coded blues. Behaviour is driven by ~/js/resizable-columns.js; without it the
   handles are never inserted and every table renders exactly as it does today.

   Deliberately NOT `table-layout: fixed` here. Every table in this app is `width: 100%`, so a fixed
   layout with no per-column widths divides the container into equal fractions and ignores content
   entirely — which crushed the 19-column Range Settings Variables grid to 20px a column at a phone
   width (headers rendered as "P V N C D F ..."), and, because the table then always fit its
   container exactly, meant the responsive wrapper never had anything to scroll. Auto layout gives
   every column at least its minimum content width, so a wide grid overflows the wrapper and the
   wrapper scrolls, which is the whole point of `.table-responsive`. The fixed layout belongs with
   the pinned pixel widths and nowhere else: resizable-columns.js sets `table-layout: fixed` on the
   element itself in `pinCurrentWidths()`, in the same step that gives every column an explicit
   width. Loading the module must not, on its own, change how any table lays out. */
.rm-resizable-table th {
    position: relative;
    user-select: none;
}

/* A condition column's heading is a VARIABLE CODE plus an operator — "t1>", "BM1PT CONTAINSIN" —
   and case is part of the code. This app's own `thead th` rule uppercases every heading, so a rule
   drafted against the variable `t` displayed its columns as `T>=`, `T<=`, `T>`: a reader checking
   the grid against the Registry sees a code that does not exist there. Found by reading GAP-116's
   browser capture, invisible to every assertion in the suite. GWMApp renders the same heading in
   an <input> holding the text verbatim (GetRuleset source 1441-1444), so verbatim is also what it
   should look like here. */
.rm-lookup-grid thead th,
.rm-resizable-table thead th {
    text-transform: none;
    letter-spacing: normal;
    font-size: 13px;
}

/* Clipping only once the columns actually have fixed widths to be clipped to. Before the first
   drag the table is still auto-laid-out, where a cell is never narrower than its content and there
   is nothing to clip — scoping these to the pinned state keeps "module loaded" and "module never
   loaded" pixel-identical. `data-rm-widths-pinned` is set by pinCurrentWidths(). */
.rm-resizable-table[data-rm-widths-pinned="true"] th,
.rm-resizable-table[data-rm-widths-pinned="true"] td {
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Controls inside a resized cell must shrink with it, not overflow it. */
.rm-resizable-table[data-rm-widths-pinned="true"] td input,
.rm-resizable-table[data-rm-widths-pinned="true"] td select,
.rm-resizable-table[data-rm-widths-pinned="true"] td textarea {
    box-sizing: border-box;
    max-width: 100%;
}

.rm-resize-handle {
    position: absolute;
    top: 0;
    right: -3px;
    width: 8px;
    height: 100%;
    cursor: col-resize;
    z-index: 10;
    background: transparent;
}

.rm-resize-handle:hover,
.rm-resize-handle:focus-visible {
    background: var(--rm-accent, rgba(0, 0, 255, .35));
    opacity: .55;
    outline: none;
}

/* While dragging, keep the col-resize cursor everywhere and stop the drag selecting text. */
body.rm-resizing-column,
body.rm-resizing-column * {
    cursor: col-resize !important;
    user-select: none !important;
}

.rm-actions-inline details {
    display: inline-block;
    margin-left: 8px;
}

/* ---------- Lookup Table grid (Rulesets/Details) ----------

   Matches real GWMApp's own `.resizable-table` layout model (its Lookup Table declares
   `table-layout: fixed` on the element and `width`/`min-width` on every `th`, inside an
   `overflow-x: auto; max-width: 100%` wrapper). A fixed layout is what keeps thirteen columns at
   the widths they were given instead of the browser dividing the container between them; the
   wrapper then scrolls, which is what `.table-responsive` is for.

   The cell rules are the other half of it, and the half that actually stops the clipping: a value
   longer than its column is clipped *at the column edge with an ellipsis*, rather than spilling
   over the next cell or dragging the column wider. Same two declarations GWMApp uses
   (`.resizable-table td { overflow: hidden; text-overflow: ellipsis; }`). */
.rm-lookup-grid {
    table-layout: fixed;
}

.rm-lookup-grid th,
.rm-lookup-grid td {
    overflow: hidden;
    text-overflow: ellipsis;
}

/* A form control inside a cell must not push the column wider than the fixed layout allows —
   without this the inputs keep their own intrinsic width and the ellipsis above never applies. */
.rm-lookup-grid td input,
.rm-lookup-grid td select,
.rm-lookup-grid td textarea {
    max-width: 100%;
    box-sizing: border-box;
}

/* ---------------------------------------------------------------------------
   Print — GAP-87, the counterpart of GWMApp's PRINT button on its grids.

   No JavaScript and no separate print route: the browser's own Ctrl+P renders
   the page the reader is already looking at, with their filters, sort, page
   size and chosen columns all still applied. A "print view" route would have
   to reproduce that state and could drift from it; this cannot, because it is
   the same DOM.

   What is suppressed is everything that is a control rather than content. A
   printed grid is a record of what the registry held, so navigation, action
   menus and the filter controls themselves are noise on paper — the filter
   *results* are the point, not the boxes that produced them.
   --------------------------------------------------------------------------- */
@media print {
    /* Chrome and controls: not content.

       Deliberately NOT a blanket `form` or `details` rule. On Rulesets/Details a Draft revision's
       entire Lookup Table renders as form controls (GAP-57), so hiding forms wholesale would print
       an empty grid — the opposite of the point. Anything hidden here is named, and `rm-print-hide`
       is the opt-in for a control that has no other distinguishing class. */
    .rm-header,
    .rm-sidebar,
    .rm-button-row,
    .rm-col-filter-row,
    .rm-actions-inline,
    .rm-print-hide,
    /* GAP-91: .rm-pager is the shared class every grid's pager now carries. The
       Variables-specific selector is kept alongside it so that page cannot regress
       if its markup is edited without the class. */
    .rm-pager,
    nav[aria-label="Variable list pages"] {
        display: none !important;
    }

    /* The shell exists to place a sidebar beside a main column; on paper there
       is only the main column. */
    .rm-shell,
    .rm-main,
    .rm-page,
    .rm-page-narrow {
        display: block !important;
        max-width: none !important;
        margin: 0 !important;
        padding: 0 !important;
    }

    /* A horizontally scrolling wrapper cannot scroll on paper — let the table
       take the width it needs and wrap rather than clipping columns away. */
    .rm-table-wrap,
    .table-responsive {
        overflow: visible !important;
    }

    table {
        width: 100% !important;
        white-space: normal !important;
        border-collapse: collapse;
    }

    th,
    td {
        border: 1px solid #999;
        padding: 2pt 4pt;
        font-size: 8pt;
    }

    /* Repeat the header on every sheet: a multi-page grid whose columns are
       named only on sheet 1 is unreadable by sheet 3. */
    thead {
        display: table-header-group;
    }

    tr {
        page-break-inside: avoid;
    }

    /* Ink, and legibility on a monochrome printer. */
    body {
        background: #fff !important;
        color: #000 !important;
    }

    a {
        text-decoration: none;
        color: #000 !important;
    }
}

/* GWMApp's Lookup Table guidance block — the grammar note it prints directly above its own grid
   (GetRuleset.cshtml, "Condition Columns - The following operators are currently supported"),
   mirrored here on Product Owner instruction 2026-09-11. Presentation only; the operators and
   conventions it names are each implemented server-side, checked before the text was written. */
.rm-lookup-guidance {
    margin: 0 0 16px;
}

.rm-lookup-guidance p {
    margin: 0 0 6px;
    color: #1a1a1a;
    font-size: 13px;
    line-height: 1.5;
}

/* GWMApp pairs its Lookup Table grammar note and its "Enter notes to explain rule" box as two
   col-md-6 columns in one row, directly above the grid (GetRuleset.cshtml). Mirrored here.
   Stacks below 900px: the notes box is a full-width textarea and the guidance is prose, so side
   by side stops helping long before a phone. */
.rm-lookup-row {
    display: flex;
    flex-wrap: wrap;
    gap: 24px;
    align-items: flex-start;
    margin-bottom: 16px;
}

.rm-lookup-row > .rm-lookup-guidance,
.rm-lookup-row > .rm-lookup-notes {
    flex: 1 1 380px;
    min-width: 0;
}

.rm-lookup-notes-heading {
    margin: 0 0 8px;
    font-size: 15px;
}

.rm-lookup-notes textarea {
    width: 100%;
    box-sizing: border-box;
    min-height: 100px;
}

/* Modal pickers — GWMApp's "Select Specification" dialog (_selectSpecModal.cshtml), rendered with
   a native <dialog> rather than Bootstrap's modal, since this app carries no Bootstrap JS.
   Permitted presentation under RM-DEC-007 §4. */
dialog[data-rm-modal],
dialog[data-rm-welding-picker] {
    border: 1px solid var(--rm-border, #d0d0d8);
    border-radius: 6px;
    padding: 0;
    max-width: 640px;
    width: calc(100% - 32px);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25);
}

/* GAP-183: the welding picker alone gets more room. It went from four columns to five when the
   ASME/AWS Description column went in, and at 640px the new column was CLIPPED OFF THE RIGHT EDGE
   — present in the DOM, correct in the served HTML, and invisible on screen. Every check passed;
   only the screenshot showed it. That is GAP-178's invisible pager labels and GAP-173's
   hidden-versus-absent in a third costume, and it is the reason a browser run is not optional.

   The other dialogs keep 640px: they carry one list each and would only gain whitespace. */
dialog[data-rm-welding-picker] {
    max-width: 1100px;
}

dialog[data-rm-modal]::backdrop,
dialog[data-rm-welding-picker]::backdrop {
    background: rgba(0, 0, 32, 0.45);
}

/* Open in the flow when JavaScript has not run — see modal-picker.js's header. */
dialog[data-rm-modal][open]:not(:modal) {
    position: static;
    box-shadow: none;
    max-width: 100%;
    margin: 4px 0;
}

.rm-modal-header {
    padding: 12px 16px;
    border-bottom: 1px solid var(--rm-border, #d0d0d8);
    font-weight: 600;
}

.rm-modal-body {
    padding: 12px 16px;
    max-height: 60vh;
    overflow-y: auto;
}

.rm-modal-footer {
    padding: 12px 16px;
    border-top: 1px solid var(--rm-border, #d0d0d8);
    text-align: right;
}

/* ---------- GAP-176: RULESET HELP (source 2498-2590) ---------- */

/* The "(help)" trigger sits inside a sentence of prose, where GWMApp has an <a> (source 1414).
   It is a <button> here because it opens a dialog rather than navigating, and this rule is what
   stops a button in running text looking like a form control. No inline CSS — Product Owner,
   2026-09-16: "No inline CSS — use rm-* classes". */
.rm-linklike {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    color: var(--rm-accent);
    text-decoration: underline;
    cursor: pointer;
}

.rm-help-operator {
    margin: 0 0 0.25rem;
    font-size: 1rem;
    font-weight: 700;
}

/* The worked examples are three short lines GWMApp separates by line breaks (e.g. 2514-2516).
   Monospace because two of the three lines are literal rule text the reader retypes, and the
   commas in "70,50,HPS 70W" carry meaning that proportional spacing hides. */
.rm-help-example {
    margin: 0 0 0.75rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    white-space: normal;
}

.rm-picker-summary {
    margin: 4px 0 0;
    color: #444;
    font-size: 13px;
}

/* ---------- Rule cards on the ruleset page (GAP-108) ----------
   GWMApp's Rules section: one collapsible card per rule, its name and operations in the header bar.
   <details>/<summary> rather than script — RM-DEC-007 §4 keeps JS to presentation, and a collapse
   that needs none is the better answer. */

.rm-rule-card {
    border: 1px solid var(--rm-border);
    border-radius: 8px;
    background: #fff;
}

/* GAP-133: the three card operations stay visible when the card is shut, as they do in GWMApp.
   GetRuleset line 1290 puts .card-header-buttons (CSS line 4274: display:flex; margin-left:auto)
   inside .card-header beside the collapse link; only #collapseOne (line 1294) hides. Here the
   forms cannot live inside <summary> — a button there toggles the card instead of submitting —
   so the card and the operations share one grid cell and the operations are drawn over the
   header bar. */
.rm-rule-card-row {
    display: grid;
    grid-template-columns: 1fr;
}

    .rm-rule-card-row > details,
    .rm-rule-card-row > .rm-rule-card-ops {
        grid-area: 1 / 1;
    }

.rm-rule-card-ops {
    justify-self: end;
    align-self: start;
    display: flex;
    align-items: center;
    gap: 5px;
    /* The header bar's own height, so the operations centre on it: 10px + 21px line + 10px. */
    height: 41px;
    padding-right: 14px;
    white-space: nowrap;
}

.rm-rule-card-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    cursor: pointer;
    list-style: none;
    /* GAP-133: the right padding reserves the strip .rm-rule-card-ops is drawn over, so the
       revision/status meta is never covered by the buttons. min-height holds the bar to the same
       41px the operations are centred on, so a short rule name cannot make it shorter. */
    padding: 10px 170px 10px 14px;
    min-height: 41px;
    font-weight: 600;
}

    /* The disclosure marker is drawn by the rule below, so the UA's own triangle goes. */
    .rm-rule-card-header::-webkit-details-marker {
        display: none;
    }

    .rm-rule-card-header::before {
        content: "\25B8";
        color: var(--rm-muted, #667085);
        transition: transform .12s ease;
    }

details[open] > .rm-rule-card-header::before {
    transform: rotate(90deg);
}

/* A faint "|" between inline controls. Lived in RulesetGroups.css until GAP-135 gave the Registry
   grid a client-side pager that needs one too — a second page group makes it global, which is what
   Tools/StyleChecks/check-class-resolution.py flagged the moment the second use appeared. */
.rm-separator {
    color: #bbb;
    margin: 0 0.5rem;
}

.rm-rule-card-title {
    flex-grow: 1;
}

.rm-rule-card-meta {
    font-weight: 400;
    font-size: 13px;
    color: var(--rm-muted, #667085);
}

/* A row of fields that wraps — GWMApp's Bootstrap col-md-* layout without Bootstrap (GAP-108).
   Each field carries its own flex basis so the row collapses to one column on a narrow window
   instead of squeezing five controls into it. */
.rm-field-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0 1rem;
    align-items: flex-start;
}

    .rm-field-row .rm-field {
        min-width: 0;
    }

/* ---------------------------------------------------------------------------
   Rule editor card — laid out from the real GWMApp source (GAP-118).
   Instructions/Source_Inputs/20260827_RangeSettings_GetRuleset_Browser_Source_Information.txt,
   lines cited beside each rule. Presentation only, per RM-DEC-007 §4.
   --------------------------------------------------------------------------- */

/* Source 1299/1358/1411/1419: every row of the card body is col-md-6 + col-md-6. */
.rm-lookup-row > .rm-card-col {
    flex: 1 1 380px;
    min-width: 0;
}

/* Source 1346-1356: <ul class="inputlist"> — name 40%, variable code 15%, test value 35%,
   remove button floated right. The remove button is absent here, not disabled: this list is
   derived from the grid rather than authored, so there is nothing to remove. */
.rm-inputlist {
    list-style-type: none;
    margin: 0 0 16px 15px;
    padding: 0;
    width: 100%;
}

.rm-inputlistrow {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 15px;
    width: 100%;
}

.rm-inputlist-name { flex: 0 1 40%; min-width: 0; }
.rm-inputlist-code { flex: 0 0 15%; color: #555; font-family: monospace; }
.rm-inputlist-value { flex: 1 1 35%; min-width: 0; }

/* Source 1377-1401: #pcontable, background #FAFAFA, border-spacing 15px, cells padded .75rem. */
.rm-pcontable {
    background-color: #FAFAFA;
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 12px;
}

.rm-pcontable th {
    text-align: left;
    padding: 8px;
    font-weight: 600;
    /* This app's own `th` rule uppercases every heading. GWMApp writes "Name", "Formula" and
       "Value (True/False)" in sentence case (source 1379-1381), and "VALUE (TRUE/FALSE)" is not
       the same label. */
    text-transform: none;
    letter-spacing: normal;
}

.rm-pcontable td {
    padding: .75rem 8px;
}

.rm-pcontable td input[type="text"] {
    width: 100%;
    box-sizing: border-box;
}

/* Source 3390-3440: a PCON row that evaluated True is #00FF00, False is #FAFAFA. */
.rm-pcontable tr.rm-pcon-true { background-color: #00FF00; }
.rm-pcontable tr.rm-pcon-false { background-color: #FAFAFA; }

/* Source 1426-1427: Test Rule floats left, Add Condition floats right, directly above the grid. */
.rm-table-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    margin: 12px 0;
}

.rm-table-actions-right { margin-left: auto; }

.rm-inline-form {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    margin: 0;
}

/* Source 1474-1480: Add row on the left, Save Rule / clone / Import CSV / Export CSV on the right,
   directly below the grid. */
.rm-table-footer {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    margin: 12px 0 20px;
}

.rm-table-footer-right {
    margin-left: auto;
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: center;
}

/* Source 1351: the per-input remove button — GWMApp's btn-danger btn-sm "X", floated right. */
.rm-inputlist-remove {
    background: #B42318;
    border-color: #B42318;
    padding: 2px 10px;
    font-size: 13px;
    line-height: 1.4;
    height: auto;
    min-height: 0;
}

/* GAP-121 (REQ-09): a rule row produced mechanically and not yet confirmed by a person. Amber
   rather than red — it is not an error, it is unfinished work, and it blocks approval until done.
   Deliberately weaker than the #00FF00 a Test Rule match paints, so a test run stays readable
   over the top of it. */
.rm-lookup-grid tr.rm-row-needs-review > td:first-child {
    box-shadow: inset 3px 0 0 0 #B54708;
}

.rm-lookup-grid tr.rm-row-needs-review {
    background: #FFFAEB;
}

/* GAP-121: the per-row review actions, listed in the banner above the grid rather than inside it.
   The Lookup Table scrolls horizontally and is already wide, so an action in its own actions cell
   is not reliably reachable — found by a browser run that could not click one. */
.rm-review-list {
    list-style: none;
    margin: 8px 0 0;
    padding: 0;
}

.rm-review-list > li {
    margin-bottom: 8px;
}

.rm-review-formula {
    font-family: monospace;
    min-width: 22rem;
    display: inline-block;
}

/* ---------------------------------------------------------------------------
   Shared partials (Pages/Shared) — GAP-125

   The Product Owner, 2026-09-13: "CSS shall not be inline, unless absolutely
   necessary. Move all style to a dedicated CSS per form group."

   These rules come from inline `style=` attributes that were sitting in the
   shared partials. They live in the app-wide file rather than a page-group one
   because a Shared partial renders on many page groups at once — _GridControls
   and _GridPager appear on every Registry grid, _StandardsAndRulesetNotice
   wherever RM-REQ-001 §21.1 requires it. A page-group file would have to repeat
   them, which is the duplication this move exists to remove.
   --------------------------------------------------------------------------- */

/* ---------- Grid controls: column picker, export, page size (_GridControls) ---------- */

.rm-grid-controls {
    margin-bottom: 0.75rem;
}

.rm-column-picker {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 1.25rem;
    margin: 0.6rem 0 0.2rem;
}

/* A checkbox and its label must not be split across a line break. */
.rm-column-picker-option {
    white-space: nowrap;
}

/* The <details> wrapper around the picker. Global, not per-group: both the
   Rulesets list and the Registry grids use it, and a page-group file would
   have to repeat it (GAP-127 found it defined only in Rulesets.css while
   Registry used it — Registry never loads that file, so the margin vanished). */
.rm-column-picker-details {
    margin-top: 12px;
}

.rm-grid-controls-actions {
    margin-top: 10px;
    display: flex;
    gap: 0.75rem;
    align-items: center;
    flex-wrap: wrap;
}

.rm-page-size {
    margin-bottom: 0.5rem;
}

/* GWMApp's "Show [25] entries" — plain links, so the sizes need separating from
   the words around them and from each other. */
.rm-page-size-option {
    margin: 0 0.15rem;
}

.rm-page-size-summary {
    margin-left: 0.75rem;
}

/* GAP-269 — Product Owner, 2026-09-21: the Previous button "does not belong in this row".

   `margin-left: auto` inside the flex row that holds "Show [n] entries" and the summary pushes the
   whole pager to the far right, so the summary reads as a sentence and the controls sit apart from
   it. That is the shape GWMApp has, where DataTables renders the information block (`i`) and the
   pagination block (`p`) as separate regions — "dom": 'lBfrtip', Form Fields capture line 1067.

   The parent is `.rm-field-hint`, which `display: flex` is NOT set on; it is a paragraph whose
   children are inline. So this group is an inline-flex box that keeps its own three children on one
   line, and it is pushed right by the parent's `text-align` rather than by flex alignment — see the
   rule below, which is why the parent gets `display: flex` here rather than a float. */
.rm-field-hint:has(> .rm-grid-pager-group) {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.25rem;
}

.rm-grid-pager-group {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

/* A utility for the several places a paragraph inside a <details> or a card must
   not add its own margin. */
.rm-flush {
    margin: 0;
}

/* ---------- Pager (_GridPager) ----------
   .rm-pager already existed, but only as the hook the @media print block hides
   (GAP-91); its layout was inline. Both live here now. */

.rm-pager {
    margin-top: 16px;
    display: flex;
    gap: 0.4rem;
    align-items: center;
    flex-wrap: wrap;
}

/* Previous/Next at the ends of the range: present, so the control does not jump
   about as pages change, but visibly not a link. */
.rm-pager-disabled {
    color: #999;
}

/* ---------- Standards and Ruleset Notice (_StandardsAndRulesetNotice) ----------
   RM-REQ-001 §21.1. Deliberately NOT print-hidden — the notice is content, and a
   printed ruleset that drops its own disclaimer is the case the requirement
   exists to prevent. */

.rm-standards-notice {
    border: 1px solid #ccc;
    background: #fbfbfb;
    padding: 0.75rem 1rem;
    margin: 1.25rem 0;
    font-size: 0.9em;
    line-height: 1.45;
}

.rm-standards-notice-heading {
    font-weight: bold;
}

.rm-standards-notice p {
    margin: 0 0 0.5rem;
}

/* ---------- Rule editor card (_RuleEditorCard) — GAP-125 ----------
   66 inline styles lifted out of the partial. The layout itself is unchanged:
   these are the same declarations, addressed by class. Geometry that GWMApp's
   own Range Settings page fixes (column widths) stays pixel-exact, because the
   grid's drag-to-resize behaviour (GAP-60) measures against it. */

/* Small shared utilities. Kept deliberately few — a class per one-off margin
   would just be inline styles wearing a different hat. */

.rm-nowrap    { white-space: nowrap; }
.rm-mono      { font-family: monospace; }

/* ---------- The specificity trap these classes have to clear ----------
   GAP-125. The form-control rule above is
       input[type="text"], input:not([type]), … { font-family: …; width: 100%; }
   whose specificity is (0,1,1) — an attribute selector plus an element. A bare
   class is (0,1,0), so it LOSES to it. These declarations used to be inline
   `style=` attributes, which beat everything, so lifting them into plain classes
   silently reverted them to the global control styling.

   Caught by a computed-style diff, NOT by the 388-check acceptance suite, which
   reads DOM and text and cannot see a font change. Any future class that restyles
   an <input> must clear (0,1,1) the same way. Selects and textareas need no such
   care: the global rule matches those as a bare element, at (0,0,1). */

input[type="text"].rm-mono,
input:not([type]).rm-mono,
textarea.rm-mono {
    font-family: monospace;
}
.rm-flush-top { margin-top: 0; }
.rm-block-gap { margin-bottom: 0.75rem; }
.rm-muted       { color: #555; }
.rm-muted-light { color: #777; }
.rm-guidance    { color: #333; }

/* ---------- Row 1: rule name and pass number (source 1304-1316) ---------- */

.rm-rule-identity {
    margin-bottom: 1rem;
}

/* GWMApp gives Rule Name the width and Pass Number the remainder. */
.rm-field-wide   { flex: 4 1 22rem; }
.rm-field-narrow { flex: 1 1 12rem; }

/* A text input with its own submit button beside it, not beneath it. */
.rm-input-with-button {
    display: flex;
    gap: 0.5rem;
}

/* ---------- Inputs list (source 1329-1356) ---------- */

.rm-add-input-form {
    margin-bottom: 12px;
}

/* width:auto overrides the global control width; the min-width keeps a variable
   code and its description readable rather than truncated to the box. */
.rm-variable-picker {
    width: auto;
    min-width: 18rem;
}

/* ---------- Lookup table heading and notes ---------- */

.rm-revision-badge {
    font-weight: normal;
    font-size: 0.85em;
    color: #555;
}

/* Notes are entered in a textarea, so their line breaks are content. */
.rm-notes-text {
    white-space: pre-wrap;
}

/* ---------- Grid analysis findings (GAP-123) ---------- */

.rm-finding-list {
    margin: 0;
    padding-left: 1.2rem;
}

.rm-finding {
    margin-bottom: 4px;
}

/* <input type="text">: without the attribute here the global `width: 100%` wins
   and the box stretches to its container. */
input[type="text"].rm-correction-input {
    width: 16rem;
}

/* ---------- Add Condition controls ---------- */

.rm-condition-variable {
    width: auto;
    min-width: 14rem;
}

.rm-condition-operator {
    width: auto;
}

/* ---------- The Lookup Table grid itself ----------
   white-space: nowrap keeps every row one line; the grid scrolls horizontally
   rather than letting the page do it (GAP-60). */

.rm-lookup-grid {
    border-collapse: collapse;
    margin-bottom: 1.5rem;
    white-space: nowrap;
}

/* Tighter than the app-wide `th, td { padding: 10px 12px }` — this grid carries
   far more columns than any other table in the app. */
.rm-lookup-grid th,
.rm-lookup-grid td {
    padding: 0.4rem;
}

.rm-lookup-grid thead tr {
    text-align: left;
    border-bottom: 2px solid #ccc;
}

.rm-lookup-grid tbody tr {
    border-bottom: 1px solid #eee;
}

/* Column geometry. Pixel values, not rem, because the resize handles (GAP-60)
   write pixel widths back onto these same cells. */
.rm-col-output         { width: 300px; min-width: 300px; }
.rm-col-result-formula { width: 200px; min-width: 150px; }
.rm-col-test-result    { width: 150px; min-width: 100px; }
.rm-col-notes          { width: 220px; min-width: 160px; }
.rm-col-pcon           { width:  90px; min-width:  90px; }

/* GAP-197 — Save / Remove. It borrowed .rm-col-pcon's 90px and clipped its last control off the
   grid's right edge; a control whose label cannot be read is indistinguishable from one that is
   absent (GAP-173, GAP-178, GAP-183). Sized for both buttons side by side; it held three until the
   Product Owner removed the Edit link, which is why it is not narrower still — Remove sits in a
   form of its own and the two are not flush. */
.rm-col-row-actions    { width: 180px; min-width: 180px; }

.rm-col-condition {
    font-size: 14px;
    width: 110px;
    min-width: 110px;
}

.rm-remove-column-form {
    display: inline;
    margin-left: 0.3rem;
}

.rm-remove-column-button {
    padding: 0 0.35rem;
    font-size: 12px;
    line-height: 1.4;
}

/* GAP-202 — the session-timeout warning. A native <dialog>, like the RULESET HELP screen
   (GAP-176), so the browser supplies the modal behaviour, the focus trap and Escape. */
.rm-session-dialog {
    max-width: 32rem;
    border: 1px solid var(--rm-border);
    border-radius: 8px;
    padding: 24px;
    /* --rm-bg, not an invented --rm-surface: the token block above defines the former and has no
       token by the latter name, and a var() naming nothing falls back silently to whatever the
       second argument says — a colour that would then never track a theme change. */
    background: var(--rm-bg);
    color: var(--rm-text);
}

.rm-session-dialog::backdrop {
    background: rgba(0, 0, 0, .45);
}

.rm-session-dialog h2 {
    margin-top: 0;
}

/* The countdown is the one thing a reader looks at, and it changes every second — tabular figures
   stop the line reflowing as the digits change width. */
.rm-session-dialog [data-rm-session-countdown] {
    font-variant-numeric: tabular-nums;
}

/* GAP-202 — GWMApp's back-to-top control (source 4409-4410), fixed to the bottom-right corner.

   Bottom-right rather than bottom-left because the sidebar occupies the left edge at every width
   above the stacking breakpoint, and a control that sits over the navigation is a control that
   covers a link. */
.rm-scroll-top {
    position: fixed;
    right: 24px;
    bottom: 24px;
    z-index: 40;
    width: 44px;
    height: 44px;
    min-height: 0;
    padding: 0;
    border-radius: 50%;
    font-size: 20px;
    line-height: 1;
    /* The app's primary button colours, stated rather than inherited: GAP-178's pager labels went
       invisible precisely by inheriting a colour without the background that made it readable. */
    background: var(--rm-primary);
    color: #FFFFFF;
    border: none;
    box-shadow: 0 2px 8px rgba(0, 0, 0, .25);
    cursor: pointer;
}

.rm-scroll-top:hover,
.rm-scroll-top:focus-visible {
    background: var(--rm-primary-hover);
}

/* 44px square is the minimum comfortable touch target, and at phone width the page gutter is
   16px — so the control is pulled in to match rather than sitting proud of the content. */
@media (max-width: 575.98px) {
    .rm-scroll-top {
        right: 16px;
        bottom: 16px;
    }
}

/* GAP-197 — the blank row at the bottom of the Lookup Table, which is where a rule row is added
   now that "Add row" no longer navigates to a page of its own.

   Tinted, so a reader can tell at a glance which row is stored content and which is the one
   waiting to be filled in. GWMApp has no equivalent because its clone is only ever created by a
   click; this one is rendered by the server so that adding a row needs no script at all, and a
   permanently-present empty row that looked identical to a saved one would be a worse answer than
   the page it replaces. Source 3606 paints a freshly cloned row #FFFFFF against the grid's own
   #FAFAFA for the same reason — telling the new row apart from the stored ones. */
.rm-lookup-grid tbody tr.rm-new-rule-row {
    background: #F8FAFF;
}

/* Source 1459: GWMApp's Test Result cell is a <label class="ruletestresult">---</label>, never an
   input. A row that has never been tested has no result; "---" is the token GWMApp writes. */
.rm-lookup-grid tbody tr.rm-new-rule-row .rm-new-row-test-result {
    color: #667085;
}

/* Source 1474: `btn btn-link btn-sm` — GWMApp renders Add row as a button styled as a link, which
   is why it reads as one and behaves as a button. */
.rm-add-row-button {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    /* `color: inherit` alone would take the app's white button text — GAP-178's invisible pager
       labels, which were exactly this mistake. Stated against the same token `a` uses (line 290),
       so this button reads as the link it was until today. */
    color: var(--rm-accent);
    text-decoration: underline;
    cursor: pointer;
    min-height: 0;
    height: auto;
}

.rm-add-row-button:hover,
.rm-add-row-button:focus-visible {
    text-decoration: none;
}

/* A row the last Test Rule run matched. GWMApp paints it #00FF00 (source
   3384-3386); previously a computed inline style, now a class toggle, the same
   shape as .rm-pcon-true/.rm-pcon-false above. */
.rm-lookup-grid tbody tr.rm-rule-row-matched {
    background: #00FF00;
}

/* ---------- Editable cells inside a row ---------- */

/* A <select>, so a bare class suffices (see the specificity note above). */
.rm-output-select { min-width: 12rem; }

/* All three are <input type="text">, so each must clear (0,1,1). */
input[type="text"].rm-result-formula-input {
    width: 100%;
    min-width: 12rem;
    font-family: monospace;
}

input[type="text"].rm-row-notes-input {
    width: 100%;
    min-width: 10rem;
}

input[type="text"].rm-condition-input {
    width: 100%;
    min-width: 5rem;
    font-family: monospace;
}

.rm-evaluation-error {
    color: #B42318;
}

.rm-row-action {
    margin-left: 0.4rem;
}

.rm-row-action-form {
    display: inline;
    margin-left: 0.4rem;
}

/* ---------- Import CSV ---------- */

.rm-import-details { margin-bottom: 1.5rem; }
.rm-import-summary { cursor: pointer; }
.rm-import-form    { margin-top: 0.75rem; }

.rm-import-help {
    color: #555;
    max-width: 800px;
}

/* ---------- Cross-cutting utilities lifted in GAP-126 ----------
   These came out of Pages/Rulesets but are not specific to it: the same spacing
   and muted-text patterns recur across every page group, so a per-group file
   would just repeat them. Group-specific rules live in the page-group files. */

.rm-field-block    { margin-bottom: 1rem; }
.rm-field-block-sm { margin-bottom: 0.5rem; }
/* GAP-269 — the register's page introduction. Wider measure than a field hint, same muted
   treatment. See --rm-intro-max for why it is wider and why it is still bounded. */
.rm-page-intro {
    font-size: 12px;
    color: var(--rm-muted);
    max-width: var(--rm-intro-max);
}

.rm-field-block-md { margin-bottom: 1.25rem; }
.rm-field-block-lg { margin-bottom: 1.5rem; }
.rm-flush-bottom   { margin-bottom: 0; }
.rm-inline-gap     { margin-left: 1rem; }
.rm-inline-gap-sm  { margin-left: 0.5rem; }
.rm-bold           { font-weight: bold; }
.rm-small          { font-size: 0.85em; }

/* A form that must sit on the same line as the text beside it — a Clone or
   Disable button inline in a paragraph. */
.rm-inline-form-plain { display: inline; }

/* Boxed notices. The neutral one carries a status message; the two variants are
   an error and a harder alert, kept visually distinct from each other. */
.rm-notice {
    border: 1px solid #999;
    background: #f5f5f5;
    padding: 0.75rem;
    margin-bottom: 1rem;
}

.rm-notice-error {
    border-color: #a33;
    background: #fdf4f4;
}

.rm-notice-alert {
    border-color: #c00;
    background: #fee;
    padding: 1rem;
}

.rm-notice-error-text { color: #a00; }

/* ---------- Data grids and cells — shared (GAP-128) ----------
   .rm-cell-tight started life in Rulesets.css (GAP-126) and was then needed by
   RulesetGroups, which never loads that file. Same failure as
   .rm-column-picker-details in GAP-127: a page-group stylesheet is the wrong
   home for anything two groups use. Global from here.

   NOTE, recorded rather than silently left: .rm-ruleset-grid (Rulesets.css) and
   .rm-registry-grid (Registry.css) duplicate .rm-data-grid exactly. They are not
   consolidated here only because doing so would churn two open PRs whose
   descriptions already name them; consolidate once the stack has merged. */

.rm-data-grid {
    width: 100%;
    border-collapse: collapse;
}

.rm-grid-head-row {
    text-align: left;
    border-bottom: 2px solid #ccc;
}

.rm-grid-body-row {
    border-bottom: 1px solid #eee;
}

.rm-cell-tight   { padding: 0.5rem; }
.rm-cell-tighter { padding: 0.25rem; }

/* ---------- Width modifiers — global (GAP-128) ----------
   A max-width is not specific to any page group. Registry and RulesetGroups had
   each defined .rm-w-700 and .rm-w-900 in their own file, which is both
   duplication and a leak waiting to happen: the class-resolution check caught
   each group using the other's copy. One home for all of them. */

.rm-w-500   { max-width:  500px; }
.rm-w-560   { max-width:  560px; }
.rm-w-600   { max-width:  600px; }
.rm-w-620   { max-width:  620px; }
.rm-w-700   { max-width:  700px; }
.rm-w-900   { max-width:  900px; }
.rm-w-1000  { max-width: 1000px; }
.rm-w-1600  { max-width: 1600px; }
.rm-w-44rem { max-width:  44rem; }
.rm-w-60rem { max-width:  60rem; }

.rm-mb-xs { margin-bottom: 0.35rem; }

/* ---------- Status text colours — global (GAP-129) ----------
   Four groups now use these. .rm-ok-text started in Registry.css and was needed
   by External and Sources, which never load it — the third time a shared rule
   has been stranded in a page-group file (after .rm-column-picker-details and
   .rm-cell-tight). Kept together here so the palette is one decision.

   NOTE: .rm-alert-text and .rm-evaluation-error are the same #B42318. The latter
   is the rule card's own name for it (GAP-125) and is left alone rather than
   churned; consolidate alongside the .rm-data-grid duplication. */

.rm-alert-text      { color: #B42318; }
.rm-warn-text       { color: #8a6d00; }
.rm-warn-text-deep  { color: #8a6d3b; }
.rm-ok-text         { color: #067647; }
.rm-subtle          { color: #667085; }

.rm-semibold  { font-weight: 600; }
.rm-prewrap   { white-space: pre-wrap; }
.rm-break-all { word-break: break-all; }
.rm-text-left  { text-align: left; }
.rm-text-right { text-align: right; }
.rm-cell-top   { vertical-align: top; }
.rm-w-auto     { width: auto; }

/* ---------- Notice variants (GAP-129) ----------
   .rm-notice and .rm-notice-alert already existed. External and Sources add a
   warn, a success and an info box, all on the same base. Each restates padding
   because the base is 0.75rem and these are 1rem. */

.rm-notice-warn {
    border-color: #8a6d3b;
    background: #FFF8E1;
    padding: 1rem;
}

.rm-notice-success {
    border-color: #2e7d32;
    background: #E6F4EA;
    padding: 1rem;
}

.rm-notice-info {
    border-color: #00007A;
    background: #EEF;
    padding: 1rem;
    margin-bottom: 1.25rem;
}

/* A left-barred callout — lighter than a full notice box. */
.rm-callout-alert {
    padding: 0.75rem;
    border-left: 4px solid #B42318;
    background: #FEF3F2;
    margin-top: 0;
    margin-right: 0;
    margin-left: 0;
}

/* ---------- Spacing and width utilities added in GAP-129 ---------- */

.rm-mb-xxs      { margin-bottom: 0.25rem; }
.rm-mt-xs       { margin-top: 0.5rem; }
.rm-mt-xs-flush { margin: 0.5rem 0 0; }
.rm-my-xs       { margin: 0.5rem 0; }
.rm-my-sm       { margin: 1rem 0; }
.rm-my-md       { margin: 1.25rem 0; }

/* A horizontal rule drawn by border-top rather than the browser's default. */
.rm-rule-line {
    margin: 1rem 0;
    border: none;
    border-top: 1px solid #e5e5e5;
}

.rm-indent-list { padding-left: 1.25rem; }

.rm-w-1100  { max-width: 1100px; }
.rm-w-1200  { max-width: 1200px; }
.rm-w-1400  { max-width: 1400px; }
.rm-w-20rem { max-width: 20rem; }
.rm-w-22rem { max-width: 22rem; }
.rm-w-24rem { max-width: 24rem; }
.rm-w-26rem { max-width: 26rem; }
.rm-w-28rem { max-width: 28rem; }
.rm-w-30rem { max-width: 30rem; }
.rm-w-34rem { max-width: 34rem; }

/* A list that scrolls inside itself rather than pushing the page down. */
.rm-scroll-32 {
    max-height: 32rem;
    overflow-y: auto;
}

/* Fixed-width text inputs. Each must clear the global form-control rule's
   (0,1,1) — see the specificity note above (GAP-125). */
input[type="text"].rm-input-6,
input[type="text"].rm-input-10,
input[type="text"].rm-input-12,
input[type="text"].rm-input-14,
input[type="text"].rm-input-18 { width: auto; }

input[type="text"].rm-input-16 { width: 16rem; }
input[type="text"].rm-input-6  { width:  6rem; }
.rm-input-16 { width: 16rem; }
input[type="text"].rm-input-10 { width: 10rem; }
input[type="text"].rm-input-12 { width: 12rem; }
input[type="text"].rm-input-14 { width: 14rem; }
input[type="text"].rm-input-18 { width: 18rem; }

.rm-input-6  { width:  6rem; }
.rm-input-10 { width: 10rem; }
.rm-input-12 { width: 12rem; }
.rm-input-14 { width: 14rem; }
.rm-input-18 { width: 18rem; }

/* A boxed panel around a block of controls. Registry, Sources — global from
   GAP-129 rather than repeated. */
.rm-panel {
    padding: 1rem;
    background: #f6f6f6;
}

/* A cell indented to show it belongs under the row above it. External, Sources. */
.rm-cell-indent {
    padding: 12px 12px 12px 32px;
}

/* ---------- Shorthand-preserving block spacings (GAP-129) ----------
   These replace inline `margin: 0 0 <x> 0` shorthands. They must set the WHOLE
   shorthand, not just margin-bottom: a <p> has a browser default margin-top of
   1em, so a class that sets only the bottom lets that default back in. Caught by
   the computed-style diff as a 0px -> 15px margin-top on Sources/Details; the
   acceptance suite could not see it. */

.rm-block-gap-flush { margin: 0 0 0.75rem; }
.rm-block-sm-flush  { margin: 0 0 0.5rem; }


/* GAP-172 — "Find Rules by Output" (GWMApp GetRuleset, source lines 1226-1250 and 4000-4017).

   GWMApp adds .output-search-highlight to the matched card's header and .has-output-match to the
   card. One class does both here: the whole card row is the thing a reader is looking for, and the
   header is inside it.

   The badge follows GWMApp's own two states — badge-success normally, badge-warning on zero
   matches — using this application's existing notice colours rather than Bootstrap's. */
.rm-output-match > .rm-card {
    outline: 2px solid var(--rm-color-primary, #00007a);
    outline-offset: 2px;
}

.rm-output-match > .rm-card > .rm-card-header {
    background: #eef2ff;
}

.rm-search-result-badge {
    display: inline-block;
    padding: 2px 10px;
    border-radius: 10px;
    background: #e6f4ea;
    color: #1e4620;
    font-size: 0.85rem;
    /* "0 rules found" wrapped to two lines inside the button row and read as two badges. */
    white-space: nowrap;
}

.rm-search-result-badge .rm-badge-warning {
    color: #7a5600;
}

.rm-search-result-badge:has(.rm-badge-warning) {
    background: #fff4e5;
}

.rm-inline-label {
    font-weight: 600;
    white-space: nowrap;
}

/* GAP-172 #5 — GWMApp's Metric/Imperial radios (GetRuleset source 915-921). They sit in their own
   column with no group label, so the column needs the same vertical alignment the labelled fields
   around it get from their <label>. */
.rm-radio-field {
    display: flex;
    align-items: flex-end;
    gap: 0.75rem;
    padding-bottom: 0.35rem;
}

.rm-radio-pair {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    white-space: nowrap;
}

.rm-radio-label { margin: 0; font-weight: normal; }

/* GAP-172 #9 — GWMApp's Delete (GetRuleset source 952) is a btn-danger. Here the red is on the
   <summary> that reveals the confirmation and on the button that actually deletes, so the colour
   means the same thing in both places: this one destroys something. */
.rm-danger-confirm { max-width: 42rem; }

.rm-danger-summary {
    display: inline-block;
    cursor: pointer;
    padding: 0.35rem 1rem;
    min-width: 110px;
    border-radius: 4px;
    background: #b3261e;
    color: #fff;
    font-weight: 600;
    text-align: center;
    list-style: none;
}

.rm-danger-summary::-webkit-details-marker { display: none; }

.rm-danger-body {
    margin-top: 0.5rem;
    padding: 0.75rem 1rem;
    border: 1px solid #b3261e;
    border-radius: 4px;
    background: #fff4f3;
}

.rm-danger-button {
    background: #b3261e;
    border-color: #b3261e;
    color: #fff;
}


/* GAP-172 #10 — the Welding Processes controls: the header `+` (source 969), the per-row magnifier
   (976) and the per-row trash (991). GWMApp draws all three as Font Awesome glyphs in small
   Bootstrap buttons; this application carries neither, so they are characters in a plain button of
   the same size and the same two colours. */
.rm-icon-button {
    padding: 1px 6px;
    min-width: 1.9rem;
    line-height: 1.4;
    font-size: 0.9rem;
    vertical-align: middle;
}

.rm-icon-danger {
    background: #b3261e;
    border-color: #b3261e;
    color: #fff;
}

/* GAP-178: was a full-width block, back when the picker was a <ul> of one button per line. The
   picker is now GWMApp's table (source 2076) and this is the per-row Select button in its first
   column, so the block/full-width rules are gone with the list they were for. */
.rm-welding-option {
    background: none;
    border: 1px solid var(--rm-border, #d0d0d8);
    border-radius: 3px;
    padding: 2px 10px;
    color: inherit;
    font: inherit;
    cursor: pointer;
}

.rm-welding-option:hover,
.rm-welding-option:focus { background: #eef2ff; }

/* GAP-172 #10 — GWMApp puts the magnifier and the value side by side in one cell, separated by a
   space (source 976: button, &nbsp;, label). Without this the input's block width pushes the
   button onto its own line above it, which reads as two rows. */
[data-rm-welding-row] > td:first-child {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

[data-rm-welding-row] > td:first-child > input { flex: 1 1 auto; min-width: 0; }

/* GAP-172 #11/#12 — GWMApp's `<ol class="pl-3">` wrapper and its `selectionDisplay` list
   (GetRuleset source 1144-1157, 1172-1180). The <ol> is numbered and indented; the <ul> inside it
   carries no bullets (GWMApp sets list-style-type:none inline). */
.rm-picker-ol {
    margin: 0.25rem 0 0.5rem;
    padding-left: 1.5rem;
}

.rm-selection-display {
    list-style-type: none;
    margin: 0.35rem 0 0;
    padding: 0;
}

.rm-selection-display > li {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 2px 0;
}

/* GAP-173 — the Copy button's confirmation. A clipboard write gives the reader no feedback of its
   own, and a Copy that looks identical whether it worked or was blocked is worse than none. */
.rm-copy-note {
    margin-left: 0.5rem;
    font-size: 0.85rem;
    color: var(--rm-muted, #5a5a68);
}

/* GAP-174 — GWMApp's drag handle (GetRuleset source 1278-1280). It draws a Font Awesome
   grip-vertical; this application carries no icon font, so the same shape is two vertical
   ellipses. `grab` is the cursor the gesture expects, and it is the only affordance the control
   has — copying GWMApp exactly means there is no keyboard equivalent. */
.rm-drag-handle {
    cursor: grab;
    padding: 0 0.35rem 0 0;
    color: var(--rm-muted, #5a5a68);
    letter-spacing: -0.25em;
    user-select: none;
}

.rm-drag-handle:active { cursor: grabbing; }

/* The card being dragged. Dimmed rather than hidden, so the reader can still see what is moving. */
.rm-rule-card-row.rm-dragging { opacity: 0.45; }

/* ---------- GAP-178: the welding catalogue table inside the picker (source 2076) ---------- */

/* The dialog body already scrolls (.rm-modal-body), so the table sizes to its content and the
   111 rows scroll within it. No inline CSS — Product Owner, 2026-09-16. */
.rm-welding-catalogue {
    width: 100%;
}

/* The Select button is GWMApp's per-row action (source, columnDefs targets [0]). Sized down so a
   column of 111 of them does not dominate the dialog. */
.rm-welding-catalogue .rm-welding-option {
    white-space: nowrap;
}

/* The search row sits in <tfoot>, where GWMApp puts it, and is revealed by the script. */
.rm-welding-catalogue tfoot input {
    width: 100%;
    box-sizing: border-box;
}

/* GAP-178: the picker's pager (GWMApp pages this table, source 2369-2370). */
.rm-welding-pager {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin-top: 8px;
    font-size: 0.9rem;
}

/* `color: inherit` is NOT optional here, and leaving it out is how the first version shipped
   invisible. The app's global button style is a dark fill with WHITE text; `background: none`
   removes the fill and, without this line, leaves white text on white — measured at
   rgb(255,255,255) on a transparent background. The buttons rendered as empty boxes and only the
   screenshot showed it, because every label was present and correct in the served HTML. */
.rm-welding-pager button {
    background: none;
    border: 1px solid var(--rm-border, #d0d0d8);
    border-radius: 3px;
    padding: 2px 8px;
    font: inherit;
    color: inherit;
    cursor: pointer;
}

.rm-welding-pager button:disabled {
    opacity: 0.45;
    cursor: default;
}

.rm-welding-pager .rm-page-size-current {
    background: var(--rm-primary);
    border-color: var(--rm-primary);
    color: #fff;
}

/* ---------------------------------------------------------------------------
   Breadcrumb — GAP-269. Product Owner, 2026-09-21: "Breadcrumb must be installed."

   Deliberately low-profile. The same document asks to "minimize section height and increase UX",
   so this adds one compact row: muted text, no box, no background, no border. A breadcrumb that
   announced itself would be taking vertical space from the table the reader came for.
   --------------------------------------------------------------------------- */
.rm-breadcrumb {
    margin: 0 0 12px;
    font-family: var(--rm-font-body);
    font-size: 13px;
    line-height: 1.4;
    color: var(--rm-muted);
}

.rm-breadcrumb ol {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin: 0;
    padding: 0;
    list-style: none;
}

/* The separator is a CSS ::before rather than a character in the markup, so it is not read out as
   content by a screen reader and cannot be selected when a reader copies the trail. */
.rm-breadcrumb li + li::before {
    content: "/";
    margin-right: 6px;
    color: var(--rm-border);
}

.rm-breadcrumb a {
    color: var(--rm-muted);
    text-decoration: none;
}

.rm-breadcrumb a:hover,
.rm-breadcrumb a:focus-visible {
    color: var(--rm-accent);
    text-decoration: underline;
}

/* The page the reader is already on, and any ancestor with no page behind it. Both render as plain
   text; the current page is the one carrying aria-current, which is what distinguishes them to a
   screen reader rather than the weight here. */
.rm-breadcrumb [aria-current="page"] {
    color: var(--rm-text);
    font-weight: 600;
}
