/* ==================================================================================================
   AIMS — CLASSIC CONTENT PRESENTATION
   The "how the Classic page CONTENT looks" sheet. Product-owner direction, 2026-08-12.

   ---------------------------------------------------------------------------------------------
   WHY THIS FILE EXISTS
   ---------------------------------------------------------------------------------------------
   CLASSIC restores the legacy page-body DOM (table.LayoutTable, table.BareTable.PageTitleImage,
   div.sectionHeader9, hr.horizline, .button-split-N, .form-control, div.radio, the .row-fluid >
   .col-* column) so a migrating customer's stylesheet still binds. wwwroot/css/aims-legacy-chrome.css
   then reproduces the LEGACY BASELINE for those class names — verbatim values, copied from
   AIMSBase.css / avatier_bootstrap.css / bootstrap.min.css / PasswordStation.css, so a customer
   override has the same thing to sit on that it did in ASPX.

   That baseline is faithful and it is also, on a 1600px desktop, ugly: 493px letter-spaced button
   slabs, 45px step icons hanging below their own divider, password fields marooned in one table
   column while their policy text is exiled into another, browser-default 13px checkboxes, and
   500px of dead white below every page.

   The product owner's ruling is that CLASSIC keeps the legacy CHROME (header/footer) and the
   legacy HOOKS (ids, classes, element types), but the CONTENT CONTROLS must LOOK LIKE THE MODERN
   INTERFACE. This file is that layer: it is the only place that re-presents the legacy content
   contract. Nothing in here changes what the DOM *is* — only what it LOOKS like.

   The reference implementation for the whole approach is
   AIMS.WebHost/Pages/PasswordStation/PsAuthSelection.razor: keep `div.radio`, keep
   `#ButtonTextCancel` / `#btnTextReEnroll`, drop the redundant legacy step, and give the control
   itself a uniform modern presentation (there: a fixed 24x24 object-fit:contain logo box).

   ---------------------------------------------------------------------------------------------
   CASCADE POSITION — THIS IS LOAD-BEARING, READ BEFORE MOVING THE <link>
   ---------------------------------------------------------------------------------------------
   App.razor links stylesheets in this order:

        1. css/aims-app.css              product/MODERN styling
        2. css/aims-legacy-auth.css      legacy SIGN-IN chrome baseline      :where(.aims-legacy-auth)
        3. css/aims-legacy-chrome.css    legacy POST-LOGIN chrome + content baseline
                                                                             :where(.aims-legacy-chrome)
        4. css/aims-legacy-content.css   >>> THIS FILE <<<                   :where(.aims-legacy-chrome)
        5. /branding/css/global          THE CUSTOMER'S OWN Custom.css
        6. /branding/css/{module}        the customer's per-module sheet

   This file MUST sit strictly between (3) and (5), and it is a separate sheet for exactly that
   reason:

     * It cannot live in aims-app.css. aims-app.css is loaded at (1), BEFORE the legacy baseline at
       (3). Every rule here overrides a legacy baseline rule at the SAME specificity, so from
       position (1) it would silently lose the tie on source order and do nothing. (Tried it; the
       button rows stay 493px wide.) The alternative — winning from (1) by inflating specificity —
       would then also outrank the customer's own class-level rules at (5), which is the one thing
       this layer must never do.

     * It must stay ABOVE (5). Every selector below is written at the legacy baseline's OWN
       specificity and wrapped in `:where(...)`, which contributes ZERO. So against (3) this file
       ties and wins on source order, and against the customer at (5) it ties and LOSES on source
       order. That is the intended relationship in both directions: we beat the legacy default, the
       customer beats us. It is the same discipline aims-legacy-chrome.css documents at its own
       "LEGACY CONTENT CONTRACT" header.

   NO `!important` AGAINST A LEGACY OR CUSTOMER-REACHABLE SELECTOR, and no inline styles in the
   Razor that would do the same thing. A customer sheet that declares the same property must always
   win. There is exactly ONE `!important` in the file, in §R, and it is aimed at a MudBlazor PRODUCT
   default (`.mud-secondary-text`, which MudBlazor itself declares `!important`) on an element no
   legacy markup and no customer selector touches. Any other one is a bug.

   ---------------------------------------------------------------------------------------------
   THE SPECIFICITY GATE — added 2026-08-12 after the pass that removed nine title-strip rules,
   three field-width caps, two control-sizing blocks and the whole flex button row.

   A CLASSIC-SCOPED RULE MAY NEVER CARRY MORE SPECIFICITY THAN THE LEGACY DECLARATION IT REPRODUCES.
   `:where()` contributes zero, so a faithful port ties the customer and correctly loses on source
   order. Every defect this file was blamed for came from the EXTRA class in a compensating
   selector — `.LayoutTable .form-control` (0,2,0), `.LayoutTable .radio > label > input[type=radio]`
   (0,3,2), `.LayoutTable .table input[type=checkbox]` (0,3,1) — climbing above the customer's own
   (0,1,1)/(0,2,0) rules and silently voiding them. A `max-width` of ours also beats a `width` of
   theirs no matter how they declare it, `!important` included; treat `max-*` on a legacy-named
   selector as an override, because it is one.

   ---------------------------------------------------------------------------------------------
   SCOPING — HOW THIS FILE IS KEPT OUT OF MODERN
   ---------------------------------------------------------------------------------------------
   Every rule is scoped to a legacy root class, so the sheet is INERT unless the install is CLASSIC:

        .aims-legacy-chrome   emitted by Shared/PSLayout.razor (_pageRootClass),
                              Shared/ModuleLayout.razor and Shared/PsLegacyLandingLayout.razor
                              ONLY when LegacyAppearance.IsClassic. All 25 /ps/* routes and every
                              post-login module page land under it.
        .aims-legacy-auth     emitted by Pages/Authenticate.razor's Classic arm (the sign-in pages).

   MODERN emits NEITHER class, so architectural rule A holds: MODERN cannot change, because no
   selector in this file can match a MODERN page.

   THE SIGN-IN SHELL IS DELIBERATELY NOT IN THE LEGACY-DOM SCOPE. Rules that re-present a legacy
   construct (table.LayoutTable, .PageTitleImage, .button-split-N, .form-control) are scoped to
   `.aims-legacy-chrome` ALONE. The Classic sign-in page already has its own approved presentation
   (MudBlazor outlined fields inside the legacy chrome, equal-width footer buttons) and its own
   baseline in aims-legacy-auth.css; sweeping it into these rules would restyle a screen the product
   owner has already signed off. Only the NEW product classes this layer introduces
   (`.aims-legacy-field*`, `.aims-legacy-actions`, `.aims-legacy-progress`, …) are scoped to BOTH
   roots, because those classes exist nowhere until a page opts in by emitting them.

   ---------------------------------------------------------------------------------------------
   CACHE BUSTING
   ---------------------------------------------------------------------------------------------
   Linked as `css/aims-legacy-content.css?v=1` in AIMS.WebHost/App.razor. BUMP THAT `?v=` ON EVERY
   EDIT TO THIS FILE or browsers keep the old copy.
   ================================================================================================== */


/* ==================================================================================================
   0. TOKENS
   One place for the geometry the whole Classic content surface shares, so six page fixers cannot
   each pick a different field width or a different gutter. Declared on the legacy roots and
   inherited from there.

   A customer can retune the whole surface in one rule — `.aims-legacy-chrome { --aims-lc-row-gap:
   24px }` from their own sheet is (0,1,0) at position (5) and beats the (0,1,0) here at (4).

   TOKENS ARE FOR BLAZOR-ONLY CONSTRUCTS NOW. After the 2026-08-12 pass, no token is applied to a
   LEGACY-named control (`.form-control`, `.button-split-N`, `.PageTitleImage img`, `.radio input`) —
   those take their metrics from the faithful ports in aims-legacy-chrome.css and from the customer's
   own sheet. Adding a token back onto a legacy selector is how the last three defect classes were
   introduced; don't.
   ================================================================================================== */
:where(.aims-legacy-chrome),
:where(.aims-legacy-auth) {
    /* THERE IS NO `--aims-lc-field-max` ANY MORE, and no field-width cap of any kind. It used to be
       420px and it was applied to `.aims-legacy-field`, `.LayoutTable .form-control` and
       `.aims-password-toggle-host`. Deleted 2026-08-12: legacy's field width is
       `.form-control { width: 100% }` of its table cell inside the
       `col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2` content column (Styles/hdStandardLayout.html)
       — ~772px at a 1280 viewport, NOT 420px — and a `max-width` beats a `width` regardless of
       `!important`, so the cap was silently discarding CVS's own
       `@media(max-width:760px){ input.form-control { width:100% !important } }` and
       `.input-group .form-control { width:70% }`. Measured at 600px wide: the input stayed 420px in a
       568px cell. It is also what made the title rule (legacy's correct full-column width) look like
       it "spanned 938px over a 420px form" — the rule was right, the form was clamped.

       If a field ever genuinely needs a ceiling again, it belongs on a PRODUCT class that no legacy
       markup and no customer selector can reach, never on `.form-control`. */

    /* Wider variant, still used — but only by the BLAZOR-ONLY blocks that have no legacy counterpart
       (`.aims-legacy-notice`, `.ps-policy-panel`, `.ps-transient-error`). It is not applied to any
       legacy-named control. */
    --aims-lc-field-max-wide: 640px;

    /* Vertical gap between two stacked form rows, and between a label and its own control. */
    --aims-lc-row-gap: 18px;
    --aims-lc-label-gap: 6px;

    /* Horizontal gap between an inline caption and its control, and inside the Blazor-only
       transient title strip (§P3). `--aims-lc-action-gap` went with §5's flex button row. */
    --aims-lc-title-gap: 12px;

    /* Footer buttons keep legacy's OWN sizing now (`calc(50% - 7px)` / `width:100%` /
       `calc(33% - 7px)`, avatier_bootstrap.css:219-263) — the `--aims-lc-btn-min: 150px` floor that
       replaced it was deleted 2026-08-12 along with the flex conversion that made CVS's
       `.button-split-N { text-align: center }` inoperative. This token survives only for
       `.largebutton` in §9c, a PasswordStation.css control legacy sizes but never gives a height. */
    --aims-lc-btn-height: 40px;

    /* The step icon's box. Square, so a 45x45 GIF and a 512x512 SVG land the same size — the
       fix PsAuthSelection.razor already applies to the MFA logo column. */
    --aims-lc-step-icon: 40px;

    --aims-lc-border: #d8dde3;
    --aims-lc-muted: #6b7280;
}


/* ==================================================================================================
   1. THE TITLE STRIP — `table.BareTable.PageTitleImage`
   Emitted by Shared/LegacyPageTitle.razor. Legacy shape (unchanged by this file):

       table.BareTable.PageTitleImage > tbody > tr[valign=top]
         > td.hidden-xs[width=45px] > img#ImageStep|#Image2|#imageStep1|#ImageSuccess|…
         > td                       > div.sectionHeader9[.hide-cox] > [prefix] span#LabelAction
                                                                    > hr.horizline

   THIS SECTION IS DELIBERATELY EMPTY. IT USED TO HOLD NINE RULES; ALL NINE WERE DELETED
   2026-08-12 UNDER THE CORRECTED GOVERNING RULE, AND NOTHING REPLACED THEM.

   The rules were: `table.PageTitleImage { width:100%; margin:0 0 4px 0 }`, a `display:flex` +
   `align-items:center` row with `display:block` cells, `td.hidden-xs { flex:0 0 auto }`,
   `td:last-child { flex:1 1 auto }`, `.PageTitleImage img { box-sizing:border-box; width:40px;
   max-width:100%; max-height:56px; object-fit:contain }`, `.sectionHeader9 { padding:0;
   line-height:1.3 }` and `.horizline { margin:10px 0 0 0 }`.

   WHY THEY WENT. The step icon that hangs below its own rule is not our layout — it is the
   customer's own `.PageTitleImage img { margin-top:18px; width:60px !important }` plus
   `img#Image2 { padding-top:25px !important }` landing on legacy's metrics, which is exactly the
   geometry CVS shipped to production. Worse, `max-width` / `max-height` are properties the customer
   never declares, so ours applied unopposed and CLAMPED their `!important` width down to 45px (the
   constraint solver applies max-* after !important resolution). We were overriding a customer
   `!important` — the one thing this layer exists never to do. The flex row was what made
   `max-width:100%` resolve against a 45px cell in the first place, and it also shifted the title
   3px off legacy's own position.

   MEASURED, on a control that reproduced legacy's exact DOM under legacy declarations plus CVS's
   verbatim rules and NO product sheet at all: img 60x81, icon cell widened to 60px by the table
   algorithm, `hr.horizline` at y=81.7 with the icon bottom at y=107 — a 24.3px straddle, and the
   title 76px right of the form. Our live page with these nine rules ablated reproduces 24.3px and
   76px exactly. So the straddle and the indent ARE the customer's design; there is nothing of ours
   left to fix.

   WHAT NOW CARRIES THE STRIP, all already ported at legacy's own weight in aims-legacy-chrome.css:
       table.BareTable { padding:0; border-spacing:0; border-collapse:collapse }   AIMSBase.css:97-101
       table.BareTable td { padding:0 }                                           AIMSBase.css:102-104
       table.PageTitleImage img { padding-right:4px }  (no width, no margin)      AIMSBase.css:106-109
       .sectionHeader9 { font-size:20px; color:#333; padding:2px 0 6px 0 }        AIMSBase.css:1235-1241
       .sectionHeader9 span { font-size:20px }                              avatier_bootstrap.css:328-330
       .horizline { margin:0; height:1px; background:#CCCCCC; border:0 }          AIMSBase.css:1243-1249
   plus the row's own `valign="top"` PRESENTATIONAL ATTRIBUTE, which is now the only thing aligning
   the cells — as in legacy.

   DO NOT RE-ADD, in any spelling: `display:flex` / `align-items:center` on the row,
   `vertical-align:middle` on the cells, a negative `margin-top` on the icon, a `max-width` /
   `max-height` / `object-fit` / `box-sizing` on `.PageTitleImage img`, or a `margin-left:0` /
   `padding-left:0` on `.sectionHeader9` to pull the title onto the form's left edge. Every one of
   those defeats a declaration CVS wrote on purpose. If the icon should be centred on the rule, that
   is a change to the customer's Custom.css, made by the customer.

   THE ONE GEOMETRY COMPLAINT THAT WAS OURS — "the rule spans ~938px over a ~420px form" — is fixed
   at the OTHER end, in §3b and §0: the rule is legacy's correct full-column width and the FORM was
   the thing we had capped at 420px. Removing that cap makes the two share one measure again.

   `--aims-lc-step-icon` and `--aims-lc-title-gap` survive in §0 — §P3's Blazor-only transient strip
   still consumes them.

   ---------------------------------------------------------------------------------------------
   1a. THE ONE SANCTIONED, DELIBERATE CLASSIC DEVIATION FROM BYTE-FAITHFUL ASPX
       (product-owner approved, 2026-08-12 — the ONLY rule in this section)
   ---------------------------------------------------------------------------------------------
   WHAT LEGACY DOES, AND WHY IT IS AN ACCIDENT. Legacy stacks the icon in cell 1 and the title +
   `hr.horizline` in cell 2 of ONE table row (PB70ChangePassword.aspx:278-299). The icon column is a
   FIXED stack — CVS's `.PageTitleImage img { margin-top:18px; width:60px !important }` plus the
   per-id `img#Image2 { padding-top:25px !important }` / `img#ImageSuccess { padding-top:10px }` —
   so it measures 74px (`#ImageStep`, no padding), 84px (`#ImageSuccess`) or 99px (`#Image2`). The
   title band beside it is 29-107px tall depending on how many lines the title text wraps to, and
   `hr.horizline` sits at the BOTTOM of that band. So whether the rule clears the icon or is
   straddled by it depends on the LENGTH OF THE TITLE STRING — measured live on 2026-08-12, the SAME
   route (/ps/security-question) straddled by 41px with a one-line title and cleared by 85px with a
   wrapped one. /ps/change-password and /ps/reset-password straddled by 24.3px, /ps/enroll-instructions
   by 25.3px.

   That 24.3px IS faithful — a control built from legacy DOM + legacy declarations + CVS's sheet
   verbatim reproduces it exactly (see the note above). It is faithful and it is an accident, and the
   product owner has approved fixing it here rather than in the customer's Custom.css.

   THE FIX, PRECISELY. The two cells of a table row are ALWAYS laid out to the same height, so the
   title cell is ALREADY exactly as tall as the icon column — measured 99px on /ps/change-password
   with the band only 70.8px of it. Nothing needs to know the icon's size: we give the band a
   MINIMUM HEIGHT of `100%` of that cell and let `margin-top:auto` carry the rule — and ONLY the
   rule — to the band's bottom edge. `height:1px` on the cells is the standard idiom that makes a
   table cell's used height DEFINITE so a percentage inside it resolves; a table cell can never be
   shorter than its content, so it changes no measured geometry on its own (verified live: with only
   that rule applied, every rect in the strip is unchanged).

   PROPERTIES SET, AND WHY NONE OF THEM CAN DEFEAT A CUSTOMER RULE:
     * `height` on `table.PageTitleImage td`      — CVS declares no height on any cell. Selector is
                                                    plain `td`, (0,0,1); every customer selector that
                                                    could reach these cells outranks it.
     * `display` / `flex-direction` / `min-height` on `.sectionHeader9` — CVS declares `margin` on
                                                    `.sectionHeader9.hide-cox` and font/`display` on
                                                    the SPANS inside; it declares no `display`, no
                                                    `min-height` and no `height` on the band itself.
                                                    Selector weight is `.sectionHeader9`, (0,1,0) —
                                                    IDENTICAL to legacy's own AIMSBase.css:1235 rule,
                                                    and this sheet loads BEFORE /branding/css/global,
                                                    so a customer rule of EQUAL weight beats it on
                                                    source order.
     * `margin-top` on `.horizline`               — our `<hr>`, and the only thing being moved.
                                                    Selector weight is `.horizline`, (0,1,0) —
                                                    identical to legacy's AIMSBase.css:1243 — so the
                                                    same source-order rule applies. It beats the
                                                    `.horizline { margin:0 }` port in
                                                    aims-legacy-chrome.css only because THIS sheet is
                                                    linked after that one. `margin-top:auto` computes
                                                    to 0 outside a flex container, so it is inert
                                                    anywhere the band is not the flex parent.
   NO `!important` is added, no `width`, no `margin-top`, no `padding` and no colour is set on the
   icon: the customer's `width:60px !important` still sizes the icon, their `margin-top:18px` still
   offsets it, their `padding-top` per image id still applies, and because the band measures the CELL
   the icon produced, a customer who changes the icon size still governs where the rule lands.

   MEASURED, live, all four short-title routes: overhang +24.3 -> -4.0 (/ps/change-password),
   +24.3 -> -4.0 (/ps/reset-password), +41.0 -> -4.0 (/ps/security-question),
   +25.3 -> -4.0 (/ps/enroll-instructions). The strip's own height (`tr` top/bottom) is UNCHANGED on
   every route — only the rule moved.

   KNOWN, ACCEPTED SIDE EFFECT. Where CVS's `.sectionHeader9.hide-cox { margin:10px }` applies AND
   the band (not the icon) is what makes the row tall, the cell height already contains the band's
   own 10px+10px margins, so `100%` overshoots the band's natural height by 20px and the rule lands
   20px lower than legacy (/ps/configure, /ps/auth-selection, /ps/enroll). It stays inside the
   enclosing `LayoutTable` cell's padding on every measured route — no collision with the content
   below — and CSS cannot subtract a margin it is not allowed to read. Documented debt, not a defect.

   MODERN IS UNREACHABLE FROM HERE, twice over: the selectors need BOTH `.aims-legacy-chrome` on an
   ancestor AND the legacy `PageTitleImage` / `sectionHeader9` / `horizline` class names, and
   LegacyPageTitle.razor's MODERN arm emits neither the root nor any of those classes.
   ================================================================================================== */

/* Makes the two title-strip cells' used height DEFINITE. Table cells cannot be shorter than their
   content, so this is a no-op on the rendered geometry; it exists only so the `min-height:100%`
   below has something to resolve against. */
:where(.aims-legacy-chrome) :where(table.PageTitleImage) td {
    height: 1px;
}

/* The title band, given a floor equal to the icon column (= its own table row) and turned into a
   column so the rule can be carried to that floor. Both spans keep their own boxes: they are
   block-level already (`#LabelAction { display:block }`, `.sectionHeader9.hide-cox span
   { display:table }` — both CVS), and blockification leaves both untouched. Verified live: the
   title span's rect is byte-identical before and after (t=125, h=35.8, w=710, l=315). */
:where(.aims-legacy-chrome) :where(table.PageTitleImage) .sectionHeader9 {
    display: flex;
    flex-direction: column;
    min-height: 100%;
}

/* THE DEVIATION ITSELF — the only thing that moves. */
:where(.aims-legacy-chrome) :where(table.PageTitleImage .sectionHeader9) > .horizline {
    margin-top: auto;
}

/* THE LAST 6px. `margin-top: auto` carries the rule to the band's CONTENT-box bottom; the band's
   bottom edge — the one that coincides with the icon column — is its BORDER-box bottom. Between the
   two sits `.sectionHeader9 { padding: 2px 0 6px 0 }` (AIMSBase.css:1235-1241, ported verbatim in
   aims-legacy-chrome.css) and `box-sizing: border-box`, so `min-height:100%` sizes the BORDER box to
   the cell and the padding is subtracted from the inside. The rule therefore stops exactly 6px short
   of where the icon ends. Measured live 2026-08-13: /ps/quick-reset icon bottom 206, band bottom 206,
   rule bottom 200; /ps/test-password 181 / 181 / 175. Both +6.0.

   A negative bottom margin on the last flex item hands those 6px back to the free space
   `margin-top: auto` is distributing, so the rule — and only the rule — moves down onto the border
   edge. The band's own box is untouched: `min-height` still pins it to the cell, so no row changes
   height (verified: `tr` height 99 and 74 respectively, before and after).

   WHY IT IS SCOPED TO `:not(.hide-cox)`, AND NOT APPLIED EVERYWHERE. The 6px is only the WHOLE story
   where the band has no bottom margin. Where CVS's `.sectionHeader9.hide-cox { margin: 10px }`
   (branding rule 434) applies, the band's border box already starts 10px below the cell top and ends
   10px below the cell bottom, so the padding leaves the rule 4px PAST the icon rather than 6px short
   — the -4.0 that §1a's note already records as accepted debt. Correcting the padding there would
   deepen that to -10.0, i.e. change six working routes to fix two. `hide-cox` is legacy MARKUP
   (LegacyPageTitle.razor's `HideCox`, placed only where legacy places it), so this is a stable,
   per-route condition and not a customer-sheet sniff. Every route measured 2026-08-13 carries the
   class except /ps/quick-reset and /ps/test-password — precisely the two with the overhang.

   WEIGHT: `.horizline`, (0,1,0) — identical to legacy's own AIMSBase.css:1243 and to the
   `margin-top` rule above it; the `:where()` chain, `:not()` included, contributes nothing. CVS
   declares nothing whatsoever on `.horizline`, and any customer rule that reaches it at (0,1,0) or
   above wins on source order, since this sheet is linked before /branding/css/global. No
   `!important`, no new property on the band, no change to the icon. */
:where(.aims-legacy-chrome) :where(table.PageTitleImage .sectionHeader9:not(.hide-cox)) > .horizline {
    margin-bottom: -6px;
}


/* ==================================================================================================
   2. PAGE RHYTHM — `table.LayoutTable`
   The legacy baseline is `border-spacing: 4px` + `td { padding: 4px }`, which is why legacy's empty
   spacer `<tr>`s are real content. Measured consequence on the live pages: gaps of 0px, 8px, 20px,
   32px and 55px inside a single form, because the rhythm comes from however many spacer rows the
   ASPX happened to have rather than from a scale.

   This replaces it with ONE vertical step and a single left edge. Horizontal border-spacing goes to
   zero so the title strip, the fields and the button row all start at the same x — the "heading
   block is offset ~76px right of the form it heads" defect.

   THE SPACER ROWS ARE NOT DELETED. They stay in the DOM (positional customer selectors count them);
   they just stop contributing 4px of horizontal indent and stop being the only source of vertical
   space.
   ================================================================================================== */
:where(.aims-legacy-chrome) table.LayoutTable {
    width: 100%;
    border-spacing: 0;
    margin-top: 8px;
}

:where(.aims-legacy-chrome) table.LayoutTable > tbody > tr > td {
    padding: calc(var(--aims-lc-row-gap) / 2) 0;
    vertical-align: top;
}

/* COLLAPSE THE ORPHANED FIRST COLUMN. Legacy's two-cell rows put an EMPTY `<td>` in column 1 and
   the real content in column 2 (PsResetPassword.razor's strength + policy-rules rows are the
   documented case). The table's auto layout then sizes column 1 to the widest thing in it — the
   316px field group — and hands the rest to column 2, which is what marooned the strength meter
   684px wide, 316px to the right of the 256px field it grades, above a large empty rectangle.

   Zero-width collapse fixes the rendering without a page edit. It is a stopgap: the RIGHT fix is
   for the page to stop emitting the empty cell at all, which is what <LegacyFieldRow> does.

   `:not(:last-child)` keeps a genuine one-cell spacer row (`<tr><td></td></tr>`) intact. */
:where(.aims-legacy-chrome) table.LayoutTable > tbody > tr > td:empty:first-child:not(:last-child) {
    padding: 0;
    width: 0;
}

/* A spacer row should be a small, CONSISTENT step — not a full content row's worth of padding. */
:where(.aims-legacy-chrome) table.LayoutTable > tbody > tr > td:empty {
    padding-top: 0;
    padding-bottom: 0;
    height: 6px;
}


/* ==================================================================================================
   3. FIELD ROWS
   Two halves:
     3a. the NEW product classes that Shared/LegacyFieldRow.razor emits — the shape page fixers
         should be reaching for;
     3b. a safety net for the legacy label/field cells that have not been converted yet, so an
         unconverted page still reads.
   ================================================================================================== */

/* --- 3a. <LegacyFieldRow> ----------------------------------------------------------------------
   Stacked label-over-control, capped at the shared field width, which is what MODERN's card does.
   These classes exist ONLY inside the Classic arm of LegacyFieldRow, so scoping them to both legacy
   roots costs nothing and keeps them out of MODERN by construction. */
:where(.aims-legacy-chrome) .aims-legacy-field,
:where(.aims-legacy-auth) .aims-legacy-field {
    display: flex;
    flex-direction: column;
    gap: var(--aims-lc-label-gap);
}

/* NO WIDTH CAP HERE, and none on the `-wide` / `-full` modifiers either — see the §0 note. The
   wrapper is a STACK (caption over control), nothing more. `LegacyFieldRow` still emits
   `aims-legacy-field-wide` / `-full` so page markup and any customer selector keyed on them are
   unchanged; they are simply inert now that the base has no ceiling to opt out of. */

/* Side-by-side variant, for a short control that reads better beside its caption
   (a PIN, a 4-digit code, a domain picker). */
:where(.aims-legacy-chrome) .aims-legacy-field-inline,
:where(.aims-legacy-auth) .aims-legacy-field-inline {
    flex-direction: row;
    align-items: center;
    gap: var(--aims-lc-title-gap);
}

:where(.aims-legacy-chrome) .aims-legacy-field-inline > .aims-legacy-field-control,
:where(.aims-legacy-auth) .aims-legacy-field-inline > .aims-legacy-field-control {
    flex: 1 1 auto;
    min-width: 0;
}

/* THE LABEL. This is the wrapper, not the legacy caption element — `span#LabelNewPassword`,
   `label#labelQuestion1`, `span#LabelDomain` etc. stay exactly as they are INSIDE it, with their
   ids, their tag names and their `.control-label` class, so every customer selector still binds.
   Three of these captions are deliberately `display:none` in the deployed CVS sheet
   (`span#LabelInstructions`, `span#LabelQ1`, `span#Answer1`, `span#LabelUserID`,
   `span#LabelPrimaryEmail`); when that happens the wrapper collapses on its own because it has no
   in-flow content.

   `display: contents` IS LOAD-BEARING AND REPLACED `display: flex` ON 2026-08-12. A flex wrapper
   generates a BOX, and a box with a `display:none` child is still a 0px-tall flex item that the
   stack's `gap` charges for — measured 6px of orphan air above `input#txtUserName` on /ps/enroll
   (whose caption CVS hides with `span#LabelUser { display: none }`) and above the Primary Email
   field on /ps/configure-questions (`span#LabelPrimaryEmail { display: none }`). With
   `display: contents` the wrapper generates no box at all, so a hidden caption costs exactly
   nothing and a visible one is unaffected (verified live: field height 50->44 and 40->34 on the two
   hidden captions, unchanged at 60px on the visible PIN caption).

   THE FONT PROPERTIES STAY. They are all INHERITED properties, so they still reach the caption
   through a `display:contents` box — and they still lose to any customer rule on the caption's own
   id, which is how every caption CVS styles is reached.

   DO NOT "fix" an unlabelled field by un-hiding its caption. `span#LabelUser`, `span#Answer1/2/3`
   and `span#LabelPrimaryEmail` are `display:none` in the deployed customer sheet on purpose.

   THERE IS NO `font-weight` HERE ANY MORE (deleted 2026-08-12; it was 600). Legacy declares exactly
   two weights for a field caption and 600 is neither of them:
     * `label { font-weight: 700 }`                             Styles/bootstrap.min.css
     * a `<span>` caption declares nothing at all, so it inherits 400 from the page.
   The wrapper is `display:contents`, so an INHERITED property like `font-weight` passes straight
   through to whatever caption element the row wrapped — which meant every `<label>` came out lighter
   than legacy's 700 and every `<span>` came out heavier than legacy's 400. Measured case:
   `span#Question1` on /ps/enroll step 2. CVS styles that span by id
   (`span#Answer1, …, span#Question1, …` — family, size, letter-spacing, margins) and deliberately
   declares NO weight, so the customer's own answer is 400; we were rendering it at 600. With the
   declaration gone, `<label>` captions take bootstrap's 700 and `<span>` captions inherit 400, which
   is what legacy renders. Do not re-add it, and do not "split the difference" at 500. */
:where(.aims-legacy-chrome) .aims-legacy-field-label,
:where(.aims-legacy-auth) .aims-legacy-field-label {
    display: contents;
    font-size: 13px;
    line-height: 1.3;
    color: #374151;
}

:where(.aims-legacy-chrome) .aims-legacy-field-control,
:where(.aims-legacy-auth) .aims-legacy-field-control {
    display: block;
    min-width: 0;
}

:where(.aims-legacy-chrome) .aims-legacy-field-control > .form-control,
:where(.aims-legacy-auth) .aims-legacy-field-control > .form-control {
    width: 100%;
}

/* Helper / hint / validator line under a control. Replaces the pattern of dropping a bare
   `<span style="color:indianred">` into the next table cell, which is what put
   `span#LabelPINLength` in a 6px OVERLAP with the PIN input it describes. */
:where(.aims-legacy-chrome) .aims-legacy-field-hint,
:where(.aims-legacy-auth) .aims-legacy-field-hint {
    font-size: 12px;
    line-height: 1.4;
    color: var(--aims-lc-muted);
}

/* A group of related field rows (the three security questions, the three password fields), so a
   page can separate its sections without inventing a spacer row per gap. */
:where(.aims-legacy-chrome) .aims-legacy-fieldgroup,
:where(.aims-legacy-auth) .aims-legacy-fieldgroup {
    display: flex;
    flex-direction: column;
    gap: var(--aims-lc-row-gap);
}

:where(.aims-legacy-chrome) .aims-legacy-section-title,
:where(.aims-legacy-auth) .aims-legacy-section-title {
    display: block;
    margin: 4px 0 2px 0;
    font-size: 15px;
    font-weight: 600;
    color: #1f2937;
}

/* --- 3b. UNCONVERTED LEGACY CELLS ---------------------------------------------------------------
   THE THREE `max-width` RULES THAT USED TO LIVE HERE ARE GONE (2026-08-12):
       .LayoutTable .form-control                      { max-width: 420px }   (0,2,0)
       .LayoutTable .aims-legacy-field-wide  .form-control { max-width: 100% }
       .LayoutTable .aims-legacy-field-full  .form-control { max-width: 100% }

   `.form-control` is a LEGACY class the customer writes against, and a `max-width` of ours defeats
   a `width` of theirs no matter how they declare it. CVS ship
   `@media(max-width:760px){ input.form-control { width:100% !important } }` and
   `.input-group .form-control { float:right; width:70% }`; both were being clamped to 420px.
   Legacy's own width for these controls is `width:100%` of the cell — reproduced faithfully at
   aims-legacy-chrome.css `:where(.aims-legacy-chrome) .form-control { width: 100% }`
   (Styles/bootstrap.min.css / stylesheets/bootstrap-ms.css) — and that is now what applies.

   The `.control-label` rule below stays: legacy scatters that class on bare spans where Bootstrap's
   own `.form-horizontal .control-label` never fires, so it has always been an inert hook with no
   legacy declaration to defeat, and this states it at (0,2,0) — under any customer id rule. */

/* Legacy scatters `.control-label` on bare spans where Bootstrap's own rule (scoped to
   `.form-horizontal`) never fired, so it has always been an inert hook. Give it the field label's
   look so an unconverted `<span class="control-label">` matches a converted one instead of
   rendering as 14px body text. */
:where(.aims-legacy-chrome) .LayoutTable .control-label {
    display: inline-block;
    margin-bottom: 0;
    font-size: 13px;
    font-weight: 600;
    line-height: 1.3;
    color: #374151;
}


/* ==================================================================================================
   4. THE PASSWORD REVEAL CONTROL

   CORRECTION, 2026-08-12. The premise this section was written on — "`.aims-password-toggle*` are
   PRODUCT classes with no legacy counterpart, so they are ours to restyle outright" — was FALSE.
   Legacy ships this control: PS/PB70ChangePassword.aspx:345-351 (and :436-442, :453-459),
   PB70ResetPassword.aspx:350/:381/:398, SecurityQuestion.aspx:152, Login.aspx:121, OTPEmail.aspx:114,
   SMS.aspx:110 all render

       div.input-group > input.form-control.pwd
                       + span.input-group-btn > button.btn.btn-default.btn-group
                                                > i.glyphicon.glyphicon-eye-close

   What was missing was never a product treatment: it was three groups of Bootstrap-3 rules
   (stylesheets/bootstrap-ms.css:3418-3426, :3431-3439, :3450-3459) and the Glyphicons @font-face
   (:254-266, :588-592). All of those are now ported at legacy's own weight in
   aims-legacy-chrome.css, so a legacy-shaped reveal button joins its field the way it always did
   and CVS's `span.input-group-btn { padding-top: 89px !important }` /
   `.input-group-btn:last-child > .btn { margin-top: -89px !important }` phone rules can fire again.

   WHAT WE STILL SUBSTITUTE. wwwroot/js/password-visibility.js rewrites every eligible
   `input[type=password]`, inserting `div.aims-password-toggle-host` INSIDE legacy's `div.input-group`
   and appending a bare `button.aims-password-toggle` labelled "Show" — no `span.input-group-btn`, no
   `.btn`, no `<i class="glyphicon">`. Because that host is `display:flex`, the input becomes a flex
   ITEM: `float` is dropped and `flex-grow` overrides the used width, so CVS's
   `.input-group .form-control { float:right; width:70% }` was being discarded outright (measured:
   361px instead of their 294px; setting `flex:none` on the element snapped it to exactly 294px).

   THE FIX HERE IS TO GET OUT OF THE WAY, not to restyle harder. Inside a legacy `.input-group` the
   host generates no box at all (`display:contents`) so the input-group's own table layout — and
   therefore the customer's width — applies to the real `input.form-control`; and the substituted
   button is given legacy's `.input-group-btn` metrics (`display:table-cell; width:1%;
   white-space:nowrap; vertical-align:middle`, stylesheets/bootstrap-ms.css:3386-3390) so it
   shrink-wraps into the addon cell exactly as legacy's `span.input-group-btn` does.

   `max-width: var(--aims-lc-field-max)` on the host is DELETED with the rest of the 420px cap (§0).

   FOLLOW-UP, NOT DONE HERE (it is page markup, not shared metrics): make the two password pages
   emit legacy's `span.input-group-btn > button.btn.btn-default.btn-group > i.glyphicon` in their
   CLASSIC arm and let password-visibility.js skip them — `hasExistingReveal()` already honours a
   `.reveal` inside the input-group and a `data-aims-password-toggle-ignore` wrapper. And on
   /ps/test-password legacy has NO reveal at all (TestPassword.aspx:83-86 is a bare
   `asp:textbox.form-control`), so that page's chip should be suppressed in CLASSIC rather than
   restyled.
   ================================================================================================== */
:where(.aims-legacy-chrome) .aims-password-toggle-host {
    gap: 0;
    align-items: stretch;
}

/* Legacy's own input-group is in charge here — dissolve our wrapper so it can be. */
:where(.aims-legacy-chrome) .input-group > .aims-password-toggle-host {
    display: contents;
}

/* NOTHING IS ADDED FOR THE BUTTON. Measured after the `display: contents` above: the input-group's
   own table fixup wraps the substituted button in an anonymous table cell, which puts it exactly
   where legacy's `span.input-group-btn` sits, and CVS's `.input-group .form-control { float:right;
   width:70% }` then lands verbatim — input 561px (70% of the 802px group) where it was being
   clamped to 361px before. Reproducing `.input-group-btn`'s `display:table-cell; width:1%` on our
   product class was tried and changes nothing, so it is not shipped. */

:where(.aims-legacy-chrome) .aims-password-toggle-host .aims-password-toggle-input {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
    max-width: none;
}

:where(.aims-legacy-chrome) .aims-password-toggle {
    min-width: 60px;
    min-height: 34px;
    margin-left: -1px;
    border-radius: 0 4px 4px 0;
    font-size: 12px;
    font-weight: 500;
}


/* ==================================================================================================
   5. FOOTER BUTTON ROWS — `.button-split-1` / `-2` / `-3`
   The single loudest defect in the whole baseline. Legacy (avatier_bootstrap.css, reproduced in
   aims-legacy-chrome.css:1158-1212) sizes these as `width: calc(50% - 7px)` at `font-size: 18px`,
   which at a 1600px viewport is 493-623px PER BUTTON. With the customer's 2px letter-spacing on
   top, the pages render "C a n c e l" floating in a half-page slab, a lone 999px "Continue" bar on
   /ps/success, and a three-slab 2+1 wrap on /ps/security-question and /ps/unlock-account.

   THE WHOLE OF THAT REPLACEMENT WAS DELETED 2026-08-12. Five rules went: the `display:flex` cell
   with `gap` + `padding-top:24px; padding-bottom:8px`; the `width:auto; min-width:150px;
   height:40px; padding:0 22px; font-size:15px` button sizing; the `margin-right:0` that cancelled
   legacy's 9px gutter; and the `@media(max-width:639px)` `justify-content` / `flex:1 1 100%` block.

   TWO REASONS, both of them the corrected governing rule.

   (1) THE "DEFECT" IS THE LEGACY METRIC. `.button-split-2 input[type=submit] { width: calc(50% - 7px) }`
       and `.button-split-1 input[type=submit] { width: 100% }` are avatier_bootstrap.css:231-238,
       inside `@media (min-width: 640px)`, at `height:40px; font-size:18px; line-height:27px`
       (:52-56). Two half-width slabs and a full-width Continue on /ps/success ARE what legacy
       renders. All of it is already ported faithfully in aims-legacy-chrome.css and now applies.

   (2) MAKING THE CELL A FLEX CONTAINER SILENTLY VOIDED A CUSTOMER DECLARATION. CVS ship
       `.button-split-1, .button-split-2, .button-split-3 { text-align: center }` in BOTH their
       `@media(max-width:640px)` and `@media(max-width:639px)` blocks. `text-align` does not place
       flex items, so their rule computed and did nothing — the old comment in this very section
       admitted as much ("does not silently discard the customer's intent"; it did). With the cell a
       table-cell again their centring works, unaided.

   EXPECT THE FOOTERS TO LOOK "WORSE" BY MODERN STANDARDS — wide slabs, 18px letter-spaced type.
   That is CVS's production rendering, reproduced. Do not re-open it as a defect.

   Legacy's phone half (`@media(max-width:639px)`: 20px padding, `margin-bottom:12px; width:100%`,
   avatier_bootstrap.css:281-292) had never been ported at all; it is now in aims-legacy-chrome.css
   beside the desktop block.
   ================================================================================================== */

/* The product class <LegacyButtonRow> adds alongside `.button-split-N`, for the pages that want the
   pair pushed to the right (MODERN's card footer) rather than left-anchored under the form.
   Expressed as `text-align` — the cell is a table-cell again, and `text-align` is also the property
   the customer's own small-screen centring uses, so the two are directly comparable and the
   customer's (0,1,0) at position (5) still beats this (0,1,0) at position (4). */
:where(.aims-legacy-chrome) .aims-legacy-actions-end,
:where(.aims-legacy-auth) .aims-legacy-actions-end {
    text-align: right;
}

:where(.aims-legacy-chrome) .aims-legacy-actions-center,
:where(.aims-legacy-auth) .aims-legacy-actions-center {
    text-align: center;
}


/* ==================================================================================================
   6. RADIO / CHECKBOX ROWS

   THE 16x16 SIZING WENT ON 2026-08-12, both copies:
       .LayoutTable .radio > label > input[type=radio]        (0,3,2)  width/height 16px, margin 0,
       .LayoutTable .checkbox > label > input[type=checkbox]           position: static
       .LayoutTable .table input[type=checkbox] | [type=radio] (0,3,1) width/height 16px, margin 0
   and with them the `min-height: 0` + `padding-left: 0` that had cancelled legacy's own
   `.radio label { min-height:20px; padding-left:20px }`.

   THERE IS NO LEGACY WIDTH OR HEIGHT FOR A RADIO OR A CHECKBOX — not in AIMSBase.css, not in
   Styles/avatier_bootstrap.css, not in stylesheets/bootstrap-ms.css. Legacy renders the browser
   default, which in this engine measures exactly 13x13. So "13px radios on /ps/quick-reset" and
   "raw 13px checkboxes on /ps/select-systems" were LEGACY PARITY and our 16x16 was the deviation.

   And the deviation cost a live customer declaration: CVS ship
   `input[type="radio"], input[type="checkbox"] { margin: 9px }` at (0,1,1), which our (0,3,2) /
   (0,3,1) `margin: 0` out-specified into oblivion (measured 0px; an injected bare radio on the same
   page computed their 9px). That is the structural failure this whole pass exists to end: our
   Classic rules were carrying MORE specificity than the legacy declarations they reproduced, and so
   climbed above the customer. RULE FOR ANYTHING ADDED BELOW: a Classic-scoped rule may never carry
   more specificity than the legacy declaration it reproduces.

   The other half of the radio defect was a MISSING port, not an override — legacy's
   `.radio input[type="radio"] { position: relative; margin-left: 0 }` (avatier_bootstrap.css:344-349)
   cancels the Bootstrap-3 `position:absolute; margin-left:-20px` we HAD ported, and its absence is
   why a legacy-shaped radio rendered 20px outside its own table cell. It is now ported beside the
   rule it cancels in aims-legacy-chrome.css.

   All that is left here is the row rhythm, which has no legacy or customer counterpart on
   `.LayoutTable .radio` and only replaces Bootstrap's own `margin-top: 10px`.
   ================================================================================================== */
:where(.aims-legacy-chrome) .LayoutTable .radio,
:where(.aims-legacy-chrome) .LayoutTable .checkbox {
    margin-top: 0;
    margin-bottom: 10px;
}

/* THE OPTION-ROW GUTTER — /ps/quick-reset's reset-password options.
   QuickPasswordReset.aspx:106-152 lays each option out as legacy's TWO-CELL row: the radio in
   `td[width=30px] > div.radio`, that option's value box in the SIBLING `<td>`. §2 above zeroes the
   table's horizontal border-spacing and cell padding so every row on a PS page starts on ONE left
   edge — which also removes the 4px border-spacing + 4px cell padding legacy leaves BETWEEN those
   two columns, so the radio caption would butt straight against the value box. One horizontal step
   is restored, on the RADIO cell only, so the content column's left edge is unchanged for every
   other row on the page.

   `aims-qpr-option-radio` is a PRODUCT class carried only by the CLASSIC arm of
   PsQuickPasswordReset.razor. It appears in no legacy stylesheet and nowhere in the deployed
   customer sheet — which declares no `.LayoutTable` rule and no `.radio` rule at all — so this
   competes with nothing of the customer's. It is anchored because it has to out-specify §2's own
   `table.LayoutTable > tbody > tr > td { padding: … 0 }` (0,1,4), which is OUR rule, not legacy's
   and not the customer's; (0,2,4) clears that and nothing else. No `!important`. */
:where(.aims-legacy-chrome) table.LayoutTable > tbody > tr > td.aims-qpr-option-radio {
    padding-right: var(--aims-lc-row-gap);
}

/* The caption. `min-height: 0` and `padding-left: 0` were removed with the sizing block — they
   cancelled legacy's own `.radio label { min-height: 20px; padding-left: 20px }`
   (stylesheets/bootstrap-ms.css:2527-2533, ported in aims-legacy-chrome.css), which is now back.
   What remains is only the caption/control alignment our NESTED DOM needs: legacy emits
   `div.radio > input + label` (ASP.NET RadioButton), we emit `div.radio > label > input + text`, so
   without an inline-flex row the control and its words have nothing placing them relative to each
   other. It declares no width, no height and no margin, so CVS's
   `input[type="radio"] { margin: 9px }` lands untouched. */
:where(.aims-legacy-chrome) .LayoutTable .radio > label,
:where(.aims-legacy-chrome) .LayoutTable .checkbox > label {
    display: inline-flex;
    align-items: center;
    font-size: 14px;
    line-height: 1.4;
}


/* ==================================================================================================
   7. LEGACY DATA GRIDS — `table.table` inside the layout table
   Deliberately MINIMAL. The grids on /ps/select-systems and /ps/results carry per-page defects
   (phantom expander column, orphaned Status column, group rows that read as corrupt, a second
   sub-table whose columns do not line up) that are page-shape problems, not baseline-CSS problems,
   and they are left to the page fixers. All that is set here is the row rhythm every grid shares.
   ================================================================================================== */
:where(.aims-legacy-chrome) .LayoutTable .table > tbody > tr > td {
    padding: 10px 8px;
    vertical-align: middle;
    border-top: 1px solid #eceff3;
}

:where(.aims-legacy-chrome) .LayoutTable .table > tbody > tr:first-child > td {
    border-top: 0;
}

/* Row artwork (system connector logos, status glyphs) at mixed intrinsic sizes — the same uniform
   box PsAuthSelection.razor applies to the MFA logo column. */
:where(.aims-legacy-chrome) .LayoutTable .table > tbody > tr > td > img {
    width: 24px;
    height: 24px;
    object-fit: contain;
    vertical-align: middle;
}


/* ==================================================================================================
   8. IN-FLIGHT / STATUS BLOCKS  (new product classes — no legacy counterpart)
   /ps/account-info and /ps/unlock-account render a COMPLETELY BLANK white viewport in CLASSIC for
   the 2-4s a multi-system operation takes, because their Classic arm emits an empty <div> while
   MODERN shows an icon, a title and a spinner. /ps/error, /ps/lockout-warning and /ps/disagree
   have no Classic arm at all and hand-roll an inline-styled table.

   These classes are the shared shape for all of that. A page fixer emits them INSIDE the legacy
   shell, so the legacy hooks still surround them.

   NOTE FOR THE FIXERS: introducing a Classic arm on the three status pages necessarily changes
   what MODERN renders there too, because those pages currently have no arms at all. That is a
   product-owner decision, not a CSS one — surface it before you make it. Nothing in this section
   changes any page on its own.
   ================================================================================================== */
:where(.aims-legacy-chrome) .aims-legacy-progress,
:where(.aims-legacy-auth) .aims-legacy-progress {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 28px 0;
    font-size: 15px;
    color: var(--aims-lc-muted);
}

:where(.aims-legacy-chrome) .aims-legacy-spinner,
:where(.aims-legacy-auth) .aims-legacy-spinner {
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    border: 3px solid var(--aims-lc-border);
    border-top-color: #6b7280;
    border-radius: 50%;
    animation: aims-legacy-spin 0.9s linear infinite;
}

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

@media (prefers-reduced-motion: reduce) {
    :where(.aims-legacy-chrome) .aims-legacy-spinner,
    :where(.aims-legacy-auth) .aims-legacy-spinner {
        animation-duration: 3s;
    }
}

/* A bordered notice block — the shape MODERN uses for MudAlert. Severity comes from a modifier so
   the three status pages stop each inventing their own colour (three different reds and three
   different blues across /ps/error, /ps/lockout-warning and /ps/disagree).

   THERE IS NO WIDTH CAP HERE ANY MORE. It used to be `max-width: var(--aims-lc-field-max-wide)`
   (640px), and it was an invention: this block stands in for legacy's `div.alert.alert-danger`,
   which DisplayError.aspx:36-45 drops straight into `table.BareTable.PageTitleImage > tr > td` of a
   `width="100%"` table inside the `col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2` content
   column (Styles/psStandardLayout.html). Bootstrap's `.alert` (bootstrap.min.css) declares padding,
   margin, border and colour — no width and no `max-width` — so legacy's card is exactly as wide as
   the column, which is the SAME measure as the `td.button-split-2` button row directly beneath it
   (DisplayError.aspx:49-53). The 640px cap is what produced the measured /ps/error defect: a 640px
   card sitting above a 1015px button row, overhanging it by ~375px. Legacy's measure governs now.
   If a notice ever genuinely needs a ceiling, it belongs on a per-page product class, not here. */
:where(.aims-legacy-chrome) .aims-legacy-notice,
:where(.aims-legacy-auth) .aims-legacy-notice {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    margin: 4px 0 12px 0;
    padding: 14px 16px;
    border: 1px solid var(--aims-lc-border);
    border-left-width: 4px;
    border-radius: 6px;
    background-color: #f8fafc;
    font-size: 14px;
    line-height: 1.5;
    color: #374151;
}

:where(.aims-legacy-chrome) .aims-legacy-notice-error,
:where(.aims-legacy-auth) .aims-legacy-notice-error {
    border-left-color: #c0392b;
    background-color: #fdf3f2;
}

:where(.aims-legacy-chrome) .aims-legacy-notice-warning,
:where(.aims-legacy-auth) .aims-legacy-notice-warning {
    border-left-color: #d68910;
    background-color: #fdf8ef;
}

:where(.aims-legacy-chrome) .aims-legacy-notice-success,
:where(.aims-legacy-auth) .aims-legacy-notice-success {
    border-left-color: #1e8449;
    background-color: #f2faf5;
}

:where(.aims-legacy-chrome) .aims-legacy-notice-info,
:where(.aims-legacy-auth) .aims-legacy-notice-info {
    border-left-color: #2471a3;
    background-color: #f2f7fc;
}

/* Uniform status/step artwork box — the PsAuthSelection logo fix, generalised. Stops the
   36px / 24px / 64px icon-size anarchy measured across the status pages. */
:where(.aims-legacy-chrome) .aims-legacy-notice-icon,
:where(.aims-legacy-auth) .aims-legacy-notice-icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    line-height: 0;
}

:where(.aims-legacy-chrome) .aims-legacy-notice-icon img,
:where(.aims-legacy-chrome) .aims-legacy-notice-icon svg,
:where(.aims-legacy-auth) .aims-legacy-notice-icon img,
:where(.aims-legacy-auth) .aims-legacy-notice-icon svg {
    width: 100%;
    height: 100%;
    object-fit: contain;
}


/* ==================================================================================================
   9. THE LANDING / CHALLENGE PAGES
   /ps/security-question, /ps/language-select, /ps/domain-user, /ps/welcome.
   (/ps/default is a pure redirect shim and emits no DOM, so it has nothing here.)

   These four are the pages whose CONTENT sits outside — or beside — the shared field/button
   primitives above, so their remaining defects could not be fixed by §1-§8 alone:

       * /ps/domain-user does not use table.LayoutTable at all. It is the ONE PS inner page that
         goes straight from #content into a fixed-width table.facecenter, so §2/§3/§5 never reach
         it. It was measured as: a 591x16 SOLID BLACK SLAB under the title, a completely INVISIBLE
         username field (borderless white on white), a 42x15 browser-default domain dropdown and a
         16px-tall Continue sliver — the worst-rendering page in the whole Classic walk.
       * /ps/language-select renders table#DataGrid1 directly inside its column div, not inside a
         LayoutTable, so §6/§7 never reach it either. Its flags were being forced into 42x42
         squares (the US flag came out with VERTICAL stripes) and hung below their own captions,
         and the rows gave no hover feedback on a page whose entire purpose is picking a row.
       * /ps/security-question needed the reveal button's missing glyph and a couple of type
         details; its layout is fixed by §3a now that the page emits <LegacyFieldRow>.
       * /ps/welcome already renders MODERN's card in CLASSIC; only its two title lines needed the
         MODERN heading/sub-heading hierarchy.

   SPECIFICITY DISCIPLINE IS THE SAME AS THE REST OF THE FILE. Everything here is written AT OR
   BELOW the specificity of the deployed customer rule that touches the same element, so the
   customer still wins every property they declare:
       CVS `#editUserID`        (1,0,0)  >  `table.facecenter input[type=text]`   (0,2,2)
       CVS `select#listDomains` (1,0,1)  >  `table.facecenter select`             (0,2,1)
       CVS `#LabelAction`       (1,0,0)  >  `.Large`                              (0,1,0)
       CVS `label#labelQuestion1` (1,0,1) >  `#labelQuestion1`                    (1,0,0)
   No !important, no inline styles in the Razor doing the same job.
   ================================================================================================== */

/* --- 9a. /ps/security-question ------------------------------------------------------------------
   The page emits <LegacyFieldRow>, so §3a already stacks the caption over the control. The three
   rules that used to prop up the reveal button — `.input-group-btn > .btn.reveal { display:inline-flex;
   padding: 0 10px }`, `… > .glyphicon { display:inline-flex; line-height:0 }` and
   `.aims-lc-reveal-icon { width:18px; height:18px }` — are GONE (2026-08-12). They existed only
   because the Glyphicons FONT was not being served, so legacy's own
   `i.glyphicon.glyphicon-eye-close` measured 0x0 and the button rendered as an empty white square.
   The @font-face and the two `:before` content rules are now ported from
   stylesheets/bootstrap-ms.css into aims-legacy-chrome.css, the font files were already on disk at
   wwwroot/fonts/bootstrap/, and the glyph draws itself exactly as it does in ASPX. The MudBlazor
   stand-in nested inside the `<i>` is correspondingly rendered in MODERN only, so MODERN is
   unchanged and CLASSIC does not draw two eyes.

   `#labelQuestion1` is set at (1,0,0) ON PURPOSE — CVS ships `label#labelQuestion1` at (1,0,1) with
   its own font-family/size/weight and `text-align: right`, so on a branded install the customer
   still wins outright and only an UNBRANDED install picks up MODERN's subtitle look. */
:where(.aims-legacy-chrome) #labelQuestion1 {
    display: block;
    font-size: 15px;
    font-weight: 600;
    line-height: 1.4;
    color: #1f2937;
}

/* The answer field's placeholder is the modern-interface stand-in for MODERN's outlined "Answer"
   label, because the deployed sheet hides `span#LabelAnswer1`. Keep it clearly secondary to a real
   typed answer. */
:where(.aims-legacy-chrome) #textAnswer1::placeholder {
    color: #9aa1ab;
    opacity: 1;
}

/* THE DISABLED PRIMARY. #ButtonTextContinue is disabled until an answer is typed, but the customer
   paints it the same pale mint either way, so it read as disabled even when it was live — and as
   live when it was not. Opacity is a property CVS does not declare, and this is scoped to THIS
   page's action row so it cannot surprise a sibling page owned by someone else. */
:where(.aims-legacy-chrome) .aims-lc-sq-actions input[disabled],
:where(.aims-legacy-chrome) .aims-lc-sq-actions .btn[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
}

/* --- 9b. /ps/language-select ---------------------------------------------------------------------
   MODERN is a 720px MudPaper: a coloured band with the product name, then 50px rows of
   flag / welcome sentence / native name, the flag boxed at `height:32px` with a 1px #ccc border, the
   native name right-aligned and muted, and a lemonchiffon hover. CLASSIC now reads the same way.

   Scoped to `#DataGrid1` (1,0,x) rather than to `.table` so these beat the Bootstrap-3 baseline in
   aims-legacy-chrome.css (`.table > tbody > tr > td { vertical-align: top; border-top: 1px solid
   #ddd }`, which is what made every flag hang ~26px below its own caption) while a customer rule of
   the same `#DataGrid1 …` shape still wins on source order. The table's inline
   `border-style:None` is legacy's own, so the card edge is drawn with box-shadow rather than a
   border it would defeat. */
:where(.aims-legacy-chrome) .aims-lc-lang-head {
    max-width: 760px;
    margin: 24px auto 12px auto;
    padding: 0 4px;
}

:where(.aims-legacy-chrome) .aims-lc-lang-title {
    display: block;
    font-size: 20px;
    font-weight: 600;
    line-height: 1.3;
    color: #1f2937;
}

:where(.aims-legacy-chrome) #DataGrid1 {
    max-width: 760px;
    margin: 0 auto 24px auto;
    border-radius: 8px;
    background-color: #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 0 0 1px rgba(0, 0, 0, .06);
}

:where(.aims-legacy-chrome) #DataGrid1 td {
    padding: 10px 14px;
    vertical-align: middle;
    border-top: 1px solid #eceff3;
    font-size: 14px;
    line-height: 1.4;
}

:where(.aims-legacy-chrome) #DataGrid1 tr:first-child td {
    border-top: 0;
}

/* The click affordance MODERN paints inline. Here it is a real rule, so a customer can retune or
   remove it — legacy offered no hover feedback at all on a page that is nothing but a row picker. */
:where(.aims-legacy-chrome) #DataGrid1 tr:hover td {
    background-color: lemonchiffon;
}

/* THE FLAGS. `input[type=image]` with no width/height renders at its intrinsic file size, and these
   files are square-ish thumbnails, so every flag was squashed into a 42x42 box — the US flag came
   out with vertical stripes. Same fix, same reason, as the 24x24 object-fit box PsAuthSelection.razor
   applies to the MFA logo column; the geometry (32px tall, 1px #ccc frame) is MODERN's. */
:where(.aims-legacy-chrome) #DataGrid1 input[type="image"] {
    display: block;
    box-sizing: content-box;
    width: auto;
    height: 32px;
    max-width: 48px;
    object-fit: contain;
    border: 1px solid #ccc;
}

:where(.aims-legacy-chrome) #DataGrid1 td:first-child {
    width: 64px;
}

:where(.aims-legacy-chrome) #DataGrid1 td.hidden-xs {
    text-align: right;
    color: #555;
    white-space: nowrap;
}

/* --- 9c. /ps/domain-user -------------------------------------------------------------------------
   `table.facecenter` is unique to this page (Success uses td.face8, DataSetResult uses a nested
   table.face), so scoping the card treatment to it cannot reach another page. MODERN is
   `MudContainer MaxWidth.Small > MudPaper Elevation=2 Class="pa-6"`. */
:where(.aims-legacy-chrome) table.facecenter {
    max-width: 100%;
    margin-top: 24px;
    margin-bottom: 24px;
    padding: 24px;
    border-spacing: 0;
    border-radius: 8px;
    background-color: #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 0 0 1px rgba(0, 0, 0, .06);
    font-size: 14px;
    font-family: inherit;
}

/* AIMSBase.css gives `.face` / `.facecenter` an 11px Verdana, which is why every caption on this
   page rendered smaller than the same caption on its sibling pages. */
:where(.aims-legacy-chrome) table.facecenter .face {
    font-size: 14px;
    font-family: inherit;
}

/* `vertical-align: middle` WAS REMOVED FROM THIS RULE (2026-08-13). It was the cause of the last
   label/field defect on /ps/domain-user: DomainUser.aspx:57 and :63 are `<tr valign="top">`, and the
   `valign` attribute maps to a presentational hint that ANY author declaration beats — including
   this one. So the caption cell centred its caption in a row whose height is set by the FIELD cell,
   and the customer's own `#editUserID { margin-bottom: 30px !important }` makes that row 111px tall.
   Measured: `label#LabelUserID` at y=374 against an input occupying y=334-383 — the caption sitting
   BELOW the bottom edge of the field it names. With the declaration gone, `valign="top"` reaches the
   cell again and the pair share the row's top band: label y=334, input y=334, exact.

   NOTHING REPLACES IT — no `vertical-align: top` of ours. Legacy states the alignment per row, in
   the markup, and the initial value for a cell without `valign` is `middle` anyway, so deleting the
   declaration is what reproduces legacy: `top` where the .aspx writes `valign="top"` (the title row
   and the two field rows), `middle` everywhere else. The `padding` is unrelated and stays. */
:where(.aims-legacy-chrome) table.facecenter td {
    padding: 6px 0;
}

/* `span#LabelAction.Large` is this page's title. `.Large` has no legacy baseline rule at all, so it
   rendered at the inherited 11px; CVS's `#LabelAction` (1,0,0) still outranks this (0,1,0). */
:where(.aims-legacy-chrome) table.facecenter .Large {
    display: inline-block;
    font-size: 20px;
    font-weight: 600;
    line-height: 1.3;
    color: #1f2937;
}

/* THE BLACK SLAB. Legacy draws its title rule as `td[bgcolor="#000000"] > img.spacer[height=1]`; the
   1px GIF is correct but the CELL never collapses under today's line-height, so it painted as a
   591x16 solid black bar — the single most jarring element on any of these four routes. Collapsed
   back to the hairline legacy intended, tinted to match `hr.horizline` (#ccc), which is the divider
   every sibling page draws under its title. The `bgcolor` ATTRIBUTE stays on the element:
   attribute-mapped presentation ranks below author CSS, so nothing had to be removed from the DOM. */
:where(.aims-legacy-chrome) table.facecenter td[bgcolor] {
    height: 1px;
    padding: 0;
    font-size: 0;
    line-height: 0;
    background-color: #ccc;
}

:where(.aims-legacy-chrome) table.facecenter img.spacer {
    display: block;
    width: 100%;
    height: 1px;
}

/* THE INVISIBLE FIELD AND THE 42x15 DROPDOWN. `#editUserID` carries no class, so the Bootstrap-3
   `.form-control` baseline never applied and the input rendered borderless white on white — present,
   focusable, and completely undetectable. Adding `.form-control` to the markup was rejected: it
   would newly activate every `.form-control` rule a customer wrote for OTHER pages on this one.
   These give MODERN's control geometry from CSS instead, at (0,2,2)/(0,2,1) — BELOW CVS's own
   `#editUserID` (1,0,0) and `select#listDomains` (1,0,1), so their width, border-radius, text-align
   and font-size still win and simply now sit on a control that can be seen. */
:where(.aims-legacy-chrome) table.facecenter input[type="text"],
:where(.aims-legacy-chrome) table.facecenter select {
    box-sizing: border-box;
    min-height: 40px;
    padding: 8px 12px;
    border: 1px solid #ccc;
    border-radius: 4px;
    background-color: #fff;
    color: #333;
    font-size: 15px;
    line-height: 1.4;
}

:where(.aims-legacy-chrome) table.facecenter select {
    min-width: 200px;
}

:where(.aims-legacy-chrome) table.facecenter input[type="text"]:focus,
:where(.aims-legacy-chrome) table.facecenter select:focus {
    border-color: #66afe9;
    outline: 0;
    box-shadow: 0 0 6px rgba(102, 175, 233, .5);
}

:where(.aims-legacy-chrome) table.facecenter input[type="text"]::placeholder {
    color: #9aa1ab;
    opacity: 1;
}

/* `.largebutton` is PasswordStation.css's fixed-width text button and is used on this route only.
   The baseline sets `width: 175px` and nothing else, so with the page's inherited 11px type the
   Continue button rendered 16px TALL. Height/padding/type are added; the 175px width is deliberately
   left to legacy. */
:where(.aims-legacy-chrome) .largebutton {
    box-sizing: border-box;
    min-height: var(--aims-lc-btn-height);
    padding: 0 22px;
    font-size: 15px;
    line-height: 1.4;
}

/* --- 9d. /ps/welcome -----------------------------------------------------------------------------
   This page already renders MODERN's MudPaper card in CLASSIC — only the title block is inside the
   `@if (IsClassic)` arm, where MODERN's `Typo.h3 Color.Primary` + `Typo.h6` pair becomes
   `span#LabelWelcomeToPS` + `span#LabelSelfServicePWResetAndSync.font15`. Both computed to the SAME
   grey, so the heading hierarchy MODERN has was flat here. Colour only: the size, weight and
   letter-spacing of these two spans are the customer's own (`#LabelWelcomeToPS { font-size: 23pt;
   font-weight: 100 !important; letter-spacing: 5px }`) and are left alone. `--mud-palette-primary`
   is emitted by MudThemeProvider, which PSLayout renders in both appearances, so this is literally
   the colour MODERN's `Color.Primary` resolves to. */
:where(.aims-legacy-chrome) #LabelWelcomeToPS {
    color: var(--mud-palette-primary, #1976d2);
}

:where(.aims-legacy-chrome) #LabelSelfServicePWResetAndSync {
    color: #616161;
}

:where(.aims-legacy-chrome) #welcome {
    margin-bottom: 8px;
}


/* ==================================================================================================
   P. PAGE ADJUSTMENTS — /ps/test-password, /ps/link-accounts, /ps/account-info, /ps/unlock-account

   Lettered, not numbered, on purpose: several page fixers append to this file at once and the
   numeric sequence above is contested. §P is page-scoped and can never collide.

   Everything in §0-§8 is shared surface. This section is the small remainder that only these four
   routes need, and it is here rather than in aims-app.css for the reason the file header gives:
   aims-app.css is linked at App.razor slot 1, BEFORE aims-legacy-chrome.css at slot 3, so a rule
   written there at the legacy baseline's own specificity loses the tie on source order. The two
   grid rules below override exactly such a baseline rule (aims-legacy-chrome.css:1319-1326), and
   winning from slot 1 would have meant inflating specificity ABOVE the migrating customer's own
   class-level rules — the one thing this layer must never do. Same `:where()` scoping, same zero
   !important, same "customer at slot 5 still wins the tie" contract as every section above.
   ================================================================================================== */

/* --- P1. THE PASSWORD-POLICY PANEL (/ps/test-password) --------------------------------------------
   Components/PasswordPolicyRulesPanel.razor is SHARED markup: the same component instance renders in
   MODERN and in CLASSIC, and every one of its rule rows carries a Blazor-authored inline
   `style="display:flex;align-items:center;gap:4px;margin-top:6px"` plus an inline `font-size` on the
   bullet icon. An inline style outranks every non-!important rule, so its interior is not reachable
   from a stylesheet at all — and editing the component would change MODERN, which is forbidden.

   What IS reachable is the CLASSIC-only wrapper the page puts around it. Giving that wrapper a panel
   treatment is what stops the 7-line requirement list reading as loose body text bolted to the
   bottom of the form (measured: a solid y=428..602 wall aligned to the LABEL column, 300px away from
   the password field it describes).

   `:empty` is the no-policy case: the component renders literally nothing when PB is unlicensed or
   the rules-display attribute is off, and an empty bordered box would be worse than no box. The page
   writes the wrapper on ONE line precisely so no whitespace text node defeats this. */
:where(.aims-legacy-chrome) .ps-policy-panel {
    max-width: var(--aims-lc-field-max-wide);
    margin-top: 4px;
    padding: 2px 16px;
    border: 1px solid var(--aims-lc-border);
    border-radius: 6px;
    background-color: #f8fafc;
}

:where(.aims-legacy-chrome) .ps-policy-panel:empty {
    display: none;
}


/* --- P2. THE LINKED-ACCOUNTS GRID (/ps/link-accounts) ---------------------------------------------
   `table#LinkAccountsASCX_datagrid.table.table-bordered` is a WebForms DataGrid, so it ships
   `border="1" rules="all"` and an inline `border-collapse:collapse`. Those are PRESENTATIONAL
   ATTRIBUTES (attribute-mapped presentation sits below author CSS in the cascade), which is why they
   can stay on the element untouched while these rules decide what is actually painted — a hairline
   header rule and one separator per row, the same rhythm §7 gives every other legacy grid, instead
   of the hard 1px inset box that made this "the ugliest block on the page" the moment a user has a
   linked account.

   Stated at `.LayoutTable .table-bordered` (0,2,0) / `… > tbody > tr > th` (0,2,3) — the same weight
   as §7's own grid rules, and this section is later in the same file, so §7 keeps its padding and
   its `border-top` and only the border sides declared here change. A customer rule qualified by the
   grid's id still outranks all of it. */
:where(.aims-legacy-chrome) .LayoutTable .table-bordered {
    border: 0;
    margin-bottom: 0;
}

:where(.aims-legacy-chrome) .LayoutTable .table-bordered > tbody > tr > th {
    padding: 10px 8px;
    border: 0;
    border-bottom: 1px solid var(--aims-lc-border);
    font-size: 13px;
    font-weight: 600;
    line-height: 1.3;
    color: var(--aims-lc-muted);
    text-align: left;
    vertical-align: middle;
}

:where(.aims-legacy-chrome) .LayoutTable .table-bordered > tbody > tr > td {
    border-left: 0;
    border-right: 0;
    border-bottom: 0;
}


/* --- P3. THE TRANSIENT STATUS SCREENS (/ps/account-info, /ps/unlock-account) ----------------------
   These two routes have NO legacy content DOM to reproduce (AccountInfo.aspx:15-20 and
   UnlockAccount.aspx:17-21 render an empty <form> and Response.Redirect), so both appearances render
   ONE copy of the same Blazor-only title strip + spinner. That is deliberate and must stay that way:
   MODERN MUST NOT CHANGE, and the block is shared markup.

   The page therefore adds `ps-transient-classic` ALONGSIDE its existing wrapper class in CLASSIC
   only, and everything below hangs off that. It is the §1 title-strip treatment applied to a strip
   that has no legacy classes for §1 to bind to: a centred flex row instead of `valign="top"`, and a
   square `object-fit: contain` icon box instead of a 45px GIF top-aligned against an 18px title.
   Without it these two screens are the only place in CLASSIC where the step icon still hangs below
   its own rule.

   `table tr`, NOT `table > tbody > tr`: Blazor patches the DOM through createElement/appendChild, so
   no implicit <tbody> is inserted and the page's `<table><tr>` really is `table > tr` in the live
   DOM (the same asymmetry LegacyPageTitle.razor documents). */
:where(.aims-legacy-chrome) .ps-transient-classic table tr {
    display: flex;
    align-items: center;
    gap: var(--aims-lc-title-gap);
}

:where(.aims-legacy-chrome) .ps-transient-classic table tr > td {
    display: block;
    padding: 0;
}

/* The icon cell. Legacy's `width="45px"` attribute stays on the element; `flex: 0 0 auto` is what
   stops flex-shrink collapsing it, exactly as §1 does through `td.hidden-xs`. */
:where(.aims-legacy-chrome) .ps-transient-classic table tr > td:first-child {
    flex: 0 0 auto;
    line-height: 0;
}

:where(.aims-legacy-chrome) .ps-transient-classic table tr > td:last-child {
    flex: 1 1 auto;
    min-width: 0;
}

:where(.aims-legacy-chrome) .ps-transient-classic .ps-transient-icon {
    box-sizing: border-box;
    width: var(--aims-lc-step-icon);
    height: auto;
    max-height: calc(var(--aims-lc-step-icon) * 1.4);
    object-fit: contain;
    vertical-align: middle;
}

:where(.aims-legacy-chrome) .ps-transient-classic .ps-transient-rule {
    margin: 10px 0 0 0;
}

:where(.aims-legacy-chrome) .ps-transient-classic .ps-transient-progress {
    gap: 14px;
    padding: 22px 0;
}

/* The error branch is the one place either transient screen ever shows a message. aims-app.css
   paints it 12px bold pure-red bare text, which is the "unstyled sentence floating on white" shape
   §8 exists to replace. Same look as `.aims-legacy-notice-error`, applied to the existing hook so no
   class has to be added to markup MODERN also renders. */
:where(.aims-legacy-chrome) .ps-transient-classic .ps-transient-error {
    max-width: var(--aims-lc-field-max-wide);
    margin: 16px 0 0 0;
    padding: 14px 16px;
    border: 1px solid var(--aims-lc-border);
    border-left-width: 4px;
    border-left-color: #c0392b;
    border-radius: 6px;
    background-color: #fdf3f2;
    font-size: 14px;
    font-weight: 600;
    line-height: 1.5;
    color: #922b21;
}


/* ==================================================================================================
   Q. THE PASSWORD PAGES — /ps/reset-password, /ps/change-password, /ps/quick-reset

   Lettered, not numbered, following §P: several page fixers append to this file at once and the
   numeric sequence above is contested, so a page-scoped section takes a letter and can never
   collide.

   Two opt-in rules. Neither fires anywhere until a page ADDS a product class, so no section above
   and no page that has not opted in can be affected by this one.
   ================================================================================================== */

/* --- Q1. `table.BareTable.aims-legacy-fieldtable` — DELETED 2026-08-12 ---------------------------
   The rule was `table.aims-legacy-fieldtable { border-collapse: separate; border-spacing: 0 18px }`
   at (0,1,1), and it defeated the ported legacy `table.BareTable { border-spacing:0;
   border-collapse:collapse }` (AIMSBase.css:97-101) on source order alone.

   IT WAS THE CAUSE OF D-H, not the cure. `border-spacing` charges EVERY row, including the rows
   legacy uses AS spacing and the rows the customer hides. Measured on /ps/change-password the four
   inter-field gaps were 74, 18, 18, 18px, and the 74 decomposed exactly as
       18 (our border-spacing)
     + 20 (legacy's OWN `<tr><td>&nbsp;</td><td></td></tr>` spacer — PB70ChangePassword.aspx:315,
           325, 354, 446, 463)
     + 18 (border-spacing charged to that spacer row)
     +  0 (the `<tr class="hide-cox">` whose `span#LabelInstructions` CVS sets to `display:none`)
     + 18 (border-spacing charged to that hidden row too).
   Patching border-spacing back to 0/collapse through the CSSOM moved the bands to 20, 18, 0, 0 —
   i.e. the 74px void collapses to legacy's own 20px `&nbsp;` line and the rhythm evens out. Same
   experiment on /ps/reset-password additionally removed 36px of dead space the hidden instructions
   row was charging at the top of the form.

   The class name is no longer emitted by either page (`TableClass="BareTable"` /
   `TableClass="BareTable top-buffer"`), and the trailing spacer row both pages had DROPPED from the
   ASPX (PB70ChangePassword.aspx:463, PB70ResetPassword.aspx:405) is restored in their CLASSIC arm,
   so the vertical rhythm is once again legacy's own `&nbsp;` rows and nothing else. */

/* --- Q2. AN OPTION'S VALUE CONTROL, INSIDE ITS `div.radio` ----------------------------------------
   Legacy gave each Quick-Password-Reset option two table cells: `td[width=30px] > div.radio` for the
   radio and a second cell for that option's value field. The Blazor page renders the value field
   INSIDE the option block instead (it exists only for the selected option), so there is no second
   cell to inset it — measured result, the 999px "Default" password input sat flush under its own
   caption with no relationship to it.

   MODERN insets that control by 24px and adds 4px of air. This reproduces that for CLASSIC on the
   `.aims-legacy-field` wrapper the page adds, lining the control up under the caption text rather
   than under the radio glyph (16px control + the 10px gap §6 sets = 26px).

   Written as a child combinator on a NEW product class, so it cannot reach a `div.radio` on any page
   that has not opted in — including PsAuthSelection's label-less MFA picker, which §6 is likewise
   careful to leave alone. */
:where(.aims-legacy-chrome) .radio > .aims-legacy-field,
:where(.aims-legacy-chrome) .checkbox > .aims-legacy-field {
    margin: 6px 0 0 26px;
}


/* ==================================================================================================
   R. MUDBLAZOR PRODUCT DEFAULTS THAT LEAK INTO CLASSIC

   Category (c) of the governing rule: an element BLAZOR invented, that legacy has no rule for and
   that the customer's sheet never selects. Styling it here is sanctioned; it defeats nothing.
   ================================================================================================== */

/* HOT-PINK SECONDARY TEXT — /ps/welcome's three feature captions and /ps/configure's two card
   captions render at rgb(255, 64, 129).

   PROVENANCE. Those captions are `MudText Color="Color.Secondary"`, i.e. `.mud-typography
   .mud-secondary-text`, and _content/MudBlazor/MudBlazor.min.css declares
   `.mud-secondary-text { color: var(--mud-palette-secondary) !important }`.
   Shared/AvatierTheme.cs sets `Primary` and never sets `Secondary`, so the variable resolves to
   MudBlazor's stock pink. Neither /ps/welcome nor /ps/configure has a legacy counterpart (there is
   no PS/Welcome.aspx and no Configure hub), and the customer sheet contains ZERO `.mud-*` selectors
   in all 2148 of its lines — so there is no legacy rule to port and no customer declaration to
   defeat.

   THE COLOUR. #666666 is legacy's OWN token for muted body text: `.face8, .face8center
   { color: #666666; font-size: 13px }`, AIMSBase.css:1258 — the class Success.aspx:52 puts on its
   own body cell.

   THE `!important` IS THE ONE EXCEPTION TO THIS FILE'S NO-!important RULE, and it is deliberate:
   MudBlazor declares the colour `!important`, so nothing else can reach it. It is aimed at a
   PRODUCT default, never at a customer declaration — and `/branding/css/global` still loads after
   this sheet, so a customer `.mud-secondary-text { color: … !important }` of equal weight would
   still win on source order.

   SCOPED TO `.mud-typography.` ON PURPOSE so MudIcon / MudButton / MudChip `Color.Secondary` (which
   get `.mud-secondary-text` WITHOUT `.mud-typography`) keep MudBlazor's palette.

   FIXING THIS IN MARKUP WAS REJECTED: the `MudText` nodes on PsWelcome and PsConfigure sit OUTSIDE
   their `@if (IsClassic)` arms, so editing them — or setting `Secondary` in AvatierTheme.cs — would
   change MODERN. One CSS rule also covers the seven challenge pages (PsDuoChallenge, PsFidoChallenge,
   PsOktaChallenge, PsIvrChallenge, PsFortinetChallenge, PsGenericOidcChallenge, PsPingIdChallenge)
   that have no Classic arm at all and would otherwise each need a markup edit. */
:where(.aims-legacy-chrome) .mud-typography.mud-secondary-text,
:where(.aims-legacy-auth) .mud-typography.mud-secondary-text {
    color: #666666 !important;
}


/* ==================================================================================================
   S. /ps/configure-questions — THE PIN VALIDATION MESSAGE

   Lettered, not numbered, for the reason §Q gives: several page fixers append to this file at once.

   ONE RULE, and it exists because a legacy inline declaration was deliberately NOT ported into the
   markup. ConfigurePhone.aspx.cs:468 sets `LabelPINLength.ForeColor = Color.Red` on the failure
   path, which WebForms renders as `style="color:Red"` on the span. An inline style outranks every
   non-!important rule in /branding/css/global, so porting it literally would silently void a
   customer's own colour for this id — the one thing this layer may never do. It is reproduced here
   instead, at `#LabelPINLength` = (1,0,0): exactly the specificity the customer writes for the same
   element (their `#LabelNoSystems, … , #LabelPINLength` block declares
   `color: indianred !important`), from a sheet that loads BEFORE theirs. We beat the legacy default,
   the customer beats us — in both directions, which is the invariant this file is built on.

   IT CANNOT FIRE ON PAGE LOAD. `span#LabelPINLength` is now emitted only when a save has actually
   failed the 4-10-digit rule, matching ConfigurePhone.aspx:175 (`visible="False"`) + .cs:430 + :469.
   Nothing else on any Classic page carries this id.

   THE COLOUR is the same #922b21 §P uses for `.ps-transient-error`, not `red`: legacy's literal
   `Color.Red` is pure #FF0000, which fails contrast against the white content band and is louder
   than every other error on the Classic surface. A customer who wants the legacy value writes it.
   ================================================================================================== */

:where(.aims-legacy-chrome) #LabelPINLength {
    color: #922b21;
}


/* ==================================================================================================
   T. TWO LEGACY BASELINES THE CLASSIC CONTENT COLUMN WAS MISSING

   Lettered, not numbered, for the reason §Q gives: several page fixers append to this file at once.

   Both rules exist because a CLASSIC arm stopped emitting MudBlazor and started emitting legacy's
   own markup (/ps/configure-questions' section headings, /ps/enroll-instructions' step list), and
   the legacy declarations that markup depends on had never been ported to the post-login surface.
   Neither is a re-presentation and neither is an override: each reproduces a legacy declaration AT
   THAT DECLARATION'S OWN SPECIFICITY, from a sheet that loads before /branding/css/global, so the
   customer still wins every tie. No !important.
   ================================================================================================== */

/* `.sectionHeader8` — AIMSBase.css:1223-1233, legacy's IN-PAGE section heading, as opposed to
   `.sectionHeader9`, which is the page-title strip. Legacy uses it as `div.sectionHeader8 >
   asp:Label`: PS/Default.aspx:547-548 (PASSWORD STATION SYSTEM STATUS), :561-562 (the Edge
   warning), :567-568 (ENROLLMENT WARNING), :577-578 (AUTHENTICATION OPTION), and
   PS/ChangeLocalPSEnrollment.aspx:35 / ChangeNewActivCardApplet.aspx:33 use it as a `td` class.

   WHY IT IS ONLY ARRIVING NOW. The login shell already carries this port —
   aims-legacy-auth.css:424-430, same five declarations — because Authenticate.razor emits three
   `div.sectionHeader8` blocks. Nothing on the POST-LOGIN Classic surface had emitted the class, so
   `:where(.aims-legacy-chrome)` never needed it. /ps/configure-questions now does: its three group
   headings ("Security Questions" / "Notification Email" / "Phone PIN") were
   `p.mud-typography-subtitle1`, i.e. 16px Roboto that /branding/css/global cannot select at all.

   The two copies are kept deliberately identical so the same class cannot mean two different things
   on the two sides of the login. (0,1,0) — exactly what AIMSBase.css writes, and what a customer
   writes back. */
:where(.aims-legacy-chrome) .sectionHeader8 {
    margin: 0 0 10px;
    font-size: 20px;
    color: #333;
    border-bottom: solid 1px #BBBBBB;
    padding: 2px 0;
}

/* `ol` — stylesheets/bootstrap-ms.css:1344-1347 (`ul, ol { margin-top: 0; margin-bottom: 10px }`)
   plus the list indent every engine's UA sheet supplies and MudBlazor's `* { padding: 0 }` reset
   takes away. Without the indent an ordered list's numerals render outside their own table cell.

   ONLY `ol`, NOT `ul`. The Classic chrome's navbars and the login shell's rebranded PRIMARY_ACCOUNT
   block are `ul`s with their own ported baselines (aims-legacy-auth.css:117-127); re-indenting every
   `ul` on the post-login surface would move markup this pass never looked at. `ol` appears on the
   Classic surface only in the enrolment panels and on /ps/enroll-instructions' step list, and for
   both of those this restores the legacy default rather than changing it.

   (0,0,1) — a bare type selector, the same weight bootstrap-ms.css writes, so a customer `ol { … }`
   of equal weight still wins on source order. */
:where(.aims-legacy-chrome) ol {
    margin-top: 0;
    margin-bottom: 10px;
    padding-left: 40px;
}

/* ---------------------------------------------------------------
   §12 — THE LEGACY BASE FONT. Verbatim port of avatier_bootstrap.css:44-46:

       div, span, input.form-control, select.form-control { font-size: 16px; }

   This is the rule every em-based measurement in a customer sheet was authored
   against, and the one whose absence broke /ps/main the moment the tile ids started
   binding (2026-08-13): CVS sizes each tile `#Panel*{width:10em}`, which is 160px on
   legacy (em base 16px from this rule) but computed 140px here (Mud's 14px base) —
   clipping every tile label, because the 159px inner `.MainImageAndLabel` no longer
   fit. Same weight as legacy — bare type selectors at (0,0,1) / (0,1,1) inside
   :where(), so any customer rule of equal weight still wins on source order. Emitted
   for both Classic roots because legacy loads avatier_bootstrap.css on the login
   pages too. ---------------------------------------------------------------- */
:where(.aims-legacy-chrome) div,
:where(.aims-legacy-chrome) span,
:where(.aims-legacy-chrome) input.form-control,
:where(.aims-legacy-chrome) select.form-control,
:where(.aims-legacy-auth) div,
:where(.aims-legacy-auth) span,
:where(.aims-legacy-auth) input.form-control,
:where(.aims-legacy-auth) select.form-control {
    font-size: 16px;
}
