a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

/* One focus ring for every focusable control and component in this app.

   --boe-validation-error (issue #114): the one error colour shared by the invalid field border and
   its validation message (see §details below). #dc0000 clears 4.5:1 on #f5f5f5 (e50000 was 4.45:1). */
:root {
    --boe-focus-ring: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
    --boe-validation-error: #dc0000;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: var(--boe-focus-ring);
}

.content {
    padding-top: 1.1rem;
}

/* WCAG 1.4.3 (Contrast Minimum) — muted secondary text.

   Note for future maintainers: Bootstrap's stock .text-muted (#6c757d) is NOT
   what renders in this app. The CA state template's cagov.core.css overrides it
   with `.text-muted { color: var(--text-muted, #5e5e6a) !important }`, where
   :root defines --text-muted: var(--gray-700, #5e5e6a). Measured in a running
   browser against this app's #f5f5f5 page background (forced in
   BOEPublicLayout) and against the white cards:

       #5e5e6a (what the dev template actually renders)
           5.86:1 on #f5f5f5   6.39:1 on #fff    both PASS
       #6c757d (Bootstrap stock — did NOT render here)
           4.30:1 on #f5f5f5   4.69:1 on #fff

   So the template's muted text already passed AA, and this token deliberately
   pins that same value rather than changing it — the visual result is identical
   to before.

   Why pin it at all: BOEPublicLayout.IncludeBase() only fetches the template
   over HTTP in the Localhost environment; every other environment reads
   template files from local disk next to the deployment. The template artifact
   shipped alongside PROD is therefore not necessarily the one measured above.
   If it is an older build carrying stock Bootstrap, .text-muted there would be
   #6c757d at 4.30:1 — an actual failure. Pinning the app's own token makes this
   app render compliant regardless of which template version it is deployed
   against, and protects it if the statewide --gray-700 is ever lightened.

   Re-measure before changing the value; it must stay at or above 4.5:1 on
   #f5f5f5. The --boe- prefix keeps it clear of the template's own properties. */
:root {
    --boe-text-muted-accessible: #5e5e6a;
}

/* Applied only to this app's own class, never to a bare .text-muted selector.
   Overriding .text-muted globally would reach the state template's header and
   footer chrome, whose background colours this repo cannot see, to "fix" text
   that already passes — unbounded risk for no benefit.

   !important is retained defensively. Nothing in the template currently
   outranks this selector, so it is not strictly required today, but the
   template is externally controlled and a future revision adding something like
   `.main-primary p { color: ... }` would be (0,1,1) and silently beat this
   (0,1,0) rule. app.css loads after the template CSS (App.razor links it below
   <HeadOutlet />), so with !important this reliably wins. */
.text-muted-accessible {
    color: var(--boe-text-muted-accessible) !important;
}

h1:focus {
    outline: none;
}

.breadcrumb li::before {
    border-left: none !important;
}

/* The template draws the slash divider with its own li::after and empties Bootstrap's "/" ::before,
   but that empty ::before keeps Bootstrap's float and 8px padding, which widened every crumb after
   the first by 8px (20px before the next slash instead of 12px). Collapse it so both sides match. */
.breadcrumb .breadcrumb-item + .breadcrumb-item::before {
    float: none;
    padding: 0;
}

ul li::before {
    content: "" !important;
}

ul li {
    list-style-type: disc !important;
    margin-bottom: 0 !important;
}

/* ==========================================================================
   §details — Details lane: PRC + BC Details (#105 lane; #114 →)
   Components: Components/Custom/ValidationMessageAdv.razor, InputNumberAdv.razor, InputTextAreaAdv.razor,
   CharacterCount.razor; plus the PRC row-action buttons (`.prc-row-action*`, markup in PRC Details.razor).
   (InfoTooltip.razor is also part of the Details lane, but its own rules live in the
   `.info-tooltip*` section further down this file, not here.)
   Pages: Components/Pages/PrivateRailroadCar/Details.razor, BoundaryChange/Details.razor.
   Later Details-lane layers (#116 etc.) extend this section instead of adding new, disconnected
   validation/input rules elsewhere in this file.

   #114 validation: errors-only styling (no valid/success state below); one shared invalid-field
   treatment via the --boe-validation-error token declared above.
   ========================================================================== */

/* Issue #114 (PRC #22 / BC #11): one border-color + tinted background instead of a doubled
   outline. Scoped to `.form-control.invalid`/`.form-select.invalid` (specificity (0,2,0)) to beat
   the CA.gov branding stylesheet's own `!important` borders on `input[type="text"]`/`select.form-select`
   (0,1,1). */
.form-control.invalid,
.form-select.invalid {
    border-color: var(--boe-validation-error) !important;
    background-color: #fff2f4 !important;
}

/* #157: unchecked required checkbox matches the invalid text boxes (1px error border + light red fill); the group
   message below carries the error for WCAG 1.4.1, so this is reinforcement only. */
input.form-check-input.invalid[type="checkbox"] {
    border-color: var(--boe-validation-error) !important;
    background-color: #fff2f4;
}

.boe-payer-confirmations {
    margin-top: 2rem;
}

.boe-payer-confirmations > legend {
    float: none;
    width: auto;
    margin-bottom: .625rem;
    font-size: 1.125rem;
    font-weight: 600;
    color: inherit;
}

/* Option text sits one step below the group legend (1rem vs body 1.125rem) so the long sentences read as choices, not labels. */
.boe-payer-confirmations .form-check-label {
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.4;
}

.boe-payer-confirmations .validation-message-container {
    margin-top: .5rem;
}

/* WCAG 1.4.10 (Reflow): wraps instead of the old `white-space: nowrap`, which clipped long
   messages or forced horizontal scroll at 320px / 400% zoom. */
.validation-message {
    color: var(--boe-validation-error);
    font-size: .9rem;
    display: block;
    max-width: 100%;
    white-space: normal;
    overflow-wrap: break-word;
    overflow: visible;   /* Allow overflow content to be visible */
    position: relative;  /* Ensure the child div can break out of the parent */
}

/* #119 character count: over the limit, the count uses the validation error colour and weight, so it reads as the
   error it is (js/character-count.js adds the class). Compound selector to outrank Bootstrap's .form-text colour. */
.form-text.boe-character-count__status--error {
    color: var(--boe-validation-error);
    font-weight: 700;
}

/* --------------------------------------------------------------------------
   #116 PRC Details card grid.
   Why app CSS: the entry row needs CSS subgrid (Bootstrap has none) so a wrapped label can't push
   its input off the centreline the row buttons sit on. The header, saved, total and entry rows
   share one 3/4/3/2 template (the same fractions as their col-md-* fallback classes), so their
   columns line up. `.boe-card.prc-payment-table-card .row.prc-grid-row` outranks the shared PRC
   helpers further down, which Review/Confirmation also use and which aren't changed here.
   -------------------------------------------------------------------------- */
.boe-card.prc-payment-table-card .row.prc-saved-header-row {
    padding-bottom: .5rem;
}

.boe-card.prc-payment-table-card .row.saved-payment-row {
    padding-block: .25rem;
}

/* The divider between saved records: the row's negative gutter margins pushed the shared
   `.saved-payment-row + .saved-payment-row` border 8px past the content on both sides, so it was the
   only rule in this card not lined up with the total and "Add another record" dividers. Drawn as a
   border on a pseudo-element (not a background) so it survives forced-colors mode. */
.boe-card.prc-payment-table-card .row.saved-payment-row + .saved-payment-row {
    position: relative;
    border-top: 0;
}

.boe-card.prc-payment-table-card .row.saved-payment-row + .saved-payment-row::before {
    content: "";
    position: absolute;
    top: 0;
    inset-inline: calc(var(--bs-gutter-x) * .5);
    border-top: var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
}

.boe-card.prc-payment-table-card .row.prc-total-row {
    padding-block: .5rem;
}

.prc-payment-table-card .prc-total-cell {
    margin: 0;
    overflow-wrap: anywhere;
}

.prc-payment-table-card .prc-total-cell strong {
    white-space: nowrap;
}

/* Heading size comes from the template's .h5; this adds the divider that ties it to the list. */
.prc-payment-table-card .prc-add-another-heading {
    margin: 0 0 .5rem;
    padding-top: 1rem;
    border-top: var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
}

.boe-card.prc-payment-table-card .row.prc-edit-row > .prc-entry-cell > label {
    align-self: end;
}

.boe-card.prc-payment-table-card .row.prc-grid-row > .prc-row-actions {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .5rem;
}

/* `print` too: the printable width is under 768px, and the table layout must still print. */
@media (min-width: 768px), print {
    /* !important: the shared mobile rule is `display: grid !important` and PR #137's print rule is flex. */
    .boe-card.prc-payment-table-card .row.prc-grid-row {
        display: grid !important;
        grid-template-columns: minmax(0, 3fr) minmax(0, 4fr) minmax(0, 3fr) minmax(0, 2fr);
        align-items: center;
    }

    .boe-card.prc-payment-table-card .row.prc-grid-row > * {
        width: auto;
        grid-column: auto;
        grid-row: auto;
    }

    .boe-card.prc-payment-table-card .row.prc-total-row > .prc-total-cell {
        grid-column: 1 / 4;
        text-align: end;
    }

    /* Label / control / message tracks shared across the cells; buttons sit on the control track. */
    .boe-card.prc-payment-table-card .row.prc-edit-row {
        grid-template-rows: auto auto auto;
        align-items: stretch;
    }

    .boe-card.prc-payment-table-card .row.prc-edit-row > .prc-entry-cell {
        grid-row: 1 / span 3;
        display: grid;
        grid-template-rows: subgrid;
    }

    .boe-card.prc-payment-table-card .row.prc-edit-row > .prc-row-actions {
        grid-column: 4;
        grid-row: 2;
        align-self: center;
    }
}

@media screen and (max-width: 767.98px) {
    /* One column, like BC Details: each label/value pair gets its own line, and Delete sits in a
       separate auto track at the top right, so values can never run under it. */
    .boe-card.prc-payment-table-card .row.saved-payment-row {
        grid-template-columns: minmax(0, 1fr) auto;
        row-gap: .5rem;
        padding-block: .75rem;
    }

    .boe-card.prc-payment-table-card .row.saved-payment-row > div:nth-child(-n+3) {
        grid-column: 1;
        grid-row: auto;
    }

    .boe-card.prc-payment-table-card .row.saved-payment-row > div:nth-child(4) {
        grid-column: 2;
        grid-row: 1 / span 3;
        /* Top right, the usual "remove this item" spot, with the button centred on the first
           label line (.prc-col-header: .85rem at line-height 1.5). The negative margin only
           reaches into the row's top padding, so the tap target stays inside the row. */
        align-self: start;
        margin-block-start: calc((.85rem * 1.5 - var(--prc-row-action-size, 44px)) / 2);
        justify-content: flex-end;
        padding-inline: calc(var(--bs-gutter-x) * .5);
    }

    /* Centred on the label line, the 44px target's top edge lands right on the divider above, so the
       hover/press circle and the focus ring touched it. The target stays 44px; only what's drawn is
       inset: the tint is clipped 4px in (a 36px circle, near the 38px desktop one) and the ring sits
       inside the target's edge, leaving a visible gap under the divider. */
    .boe-card.prc-payment-table-card .row.saved-payment-row .prc-row-action {
        padding: 4px;
        background-clip: content-box;
    }

    .boe-card.prc-payment-table-card .row.saved-payment-row .prc-row-action:focus-visible {
        outline-offset: -4px;
    }

    .boe-card.prc-payment-table-card .row.prc-total-row {
        display: block !important;
    }

    .boe-card.prc-payment-table-card .row.prc-edit-row {
        grid-template-columns: minmax(0, 1fr);
        row-gap: .75rem;
    }

    .boe-card.prc-payment-table-card .row.prc-edit-row > * {
        grid-column: 1;
        grid-row: auto;
    }

    /* !important: the shared mobile rule sets the column gap with !important. */
    .boe-card.prc-payment-table-card .row.prc-edit-row > .prc-row-actions {
        flex-direction: row;
        justify-content: flex-end;
        gap: .5rem !important;
        padding-inline: calc(var(--bs-gutter-x) * .5);
    }
}

/* Unsaved entry after a warned Next (moved from the page's inline <style>): one amber border
   (#b58105, 3.44:1 on white) replacing the template's, not an outline stacked on it. Only set
   while the row is valid, so it never meets .invalid; :not(.invalid) keeps it that way. */
.prc-edit-row-unsaved .form-control:not(.invalid) {
    border-color: #b58105 !important;
    background-color: #fff8e6 !important;
}

/* --------------------------------------------------------------------------
   #116 PRC row action buttons: borderless icon buttons; the tinted circle only appears on
   hover/press. The circle stays the hit target (38px, 44px on touch) even though it's invisible
   at rest. Why app CSS: the template has no icon-only circular button. CaGov glyphs aren't centred
   in their em box, so each gets an em-based nudge that scales with size and zoom; sizes stay even
   so the centre lands on a whole pixel.
   -------------------------------------------------------------------------- */
.prc-row-action {
    --prc-row-action-size: 38px;
    --prc-row-action-icon: 20px;
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    width: var(--prc-row-action-size);
    height: var(--prc-row-action-size);
    padding: 0;
    border: 0;
    border-radius: 50%;
    background-color: transparent;
    font-size: var(--prc-row-action-icon);
    font-weight: 400;
    line-height: 1;
    cursor: pointer;
    transition: background-color .15s ease-in-out;
    /* Own layer: the hover circle snaps to whole pixels but the glyph didn't, so a fractional
       column offset moved the glyph up to 0.5px off centre. */
    will-change: transform;
}

.prc-row-action-save {
    color: var(--color-p2, #046b99);
}

.prc-row-action-clear {
    color: var(--gray-700, #5e5e6a);
}

.prc-row-action-delete {
    color: var(--bs-danger, #dc3545);
}

.prc-row-action > [class^="ca-gov-icon-"] {
    display: block;
    width: 1em;
    height: 1em;
    font-size: inherit;
    line-height: 1;
    text-align: center;
}

.prc-row-action-save > [class^="ca-gov-icon-"] {
    transform: translate(-.031em, .004em);
}

/* The close-mark glyph is drawn ~27% smaller than the check mark in its em box (measured ink
   10px vs 13.75px at 20px), so it's scaled up to roughly match. 1.45 (not the exact 1.375) and
   the per-size nudge were picked by measuring the rendered ink against the hover circle: at
   1.375 the 1x ink is an odd 13px tall in a 38px circle and rounds ~0.6px high. These values
   keep it within ~0.35px of centre at 1x, 1.5x and 2x. */
.prc-row-action-clear > [class^="ca-gov-icon-"],
.prc-row-action-delete > [class^="ca-gov-icon-"] {
    transform: translate(0, var(--prc-close-mark-nudge, -.045em)) scale(1.45);
}

/* Hover-capable pointers only: touch browsers keep :hover after a tap, which would leave a
   stuck tint on the empty circle. Touch still gets the :active press tint. */
@media (hover: hover) {
    .prc-row-action:hover:not(:disabled) {
        background-color: color-mix(in srgb, currentColor 14%, var(--bs-body-bg, #fff));
    }
}

.prc-row-action:active:not(:disabled) {
    background-color: color-mix(in srgb, currentColor 26%, var(--bs-body-bg, #fff));
}

.prc-row-action:focus {
    outline: none;
    box-shadow: none;
}

/* #8c8c8c is 3.36:1 on white. */
.prc-row-action:focus-visible {
    outline: 2px solid #8c8c8c;
    outline-offset: 3px;
}

.prc-row-action:disabled {
    opacity: .4;
    cursor: not-allowed;
}

@media (max-width: 767.98px), (pointer: coarse) {
    .prc-row-action {
        --prc-row-action-size: 44px;
        --prc-row-action-icon: 22px;
        --prc-close-mark-nudge: -.055em;
    }
}

@media (prefers-reduced-motion: reduce) {
    .prc-row-action {
        transition: none;
    }
}

/* High Contrast mode replaces the tinted fill and there's no border, so hover/press would show
   no change; a system-colour ring brings the circle back. Hover is guarded like the tint above;
   focus matches the hover/active specificity so its ring wins when both apply. */
@media (forced-colors: active) {
    @media (hover: hover) {
        .prc-row-action:hover:not(:disabled) {
            outline: 1px solid ButtonText;
            outline-offset: -1px;
        }
    }

    .prc-row-action:active:not(:disabled) {
        outline: 1px solid ButtonText;
        outline-offset: -1px;
    }

    .prc-row-action:focus-visible:not(:disabled) {
        outline: 2px solid Highlight;
        outline-offset: 3px;
    }
}

/* --------------------------------------------------------------------------
   #116 BC Details sections: <fieldset> + <legend> inside a SectionCard. The legend takes the #107
   card title and divider, so a BC section reads like a Review/Confirmation card. Bootstrap's
   reboot floats the legend at 100% width; it's widened by the card padding so the divider is
   full-bleed like `.boe-card-header--divider`.
   -------------------------------------------------------------------------- */
.boe-details-fieldset {
    min-width: 0;
    margin: 0;
    padding: 0;
    border: 0;
}

.boe-details-fieldset > legend.boe-card-title {
    box-sizing: border-box;
    width: calc(100% + 2 * var(--boe-card-padding));
    margin: calc(-1 * var(--boe-card-padding)) calc(-1 * var(--boe-card-padding)) var(--boe-card-padding);
    padding: var(--boe-card-padding) var(--boe-card-padding) .75rem;
    border-bottom: var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
}

/* --------------------------------------------------------------------------
   Cancel/Next on both Details pages: the same action row as Review and Confirmation (polish pass),
   flush with the card edges and left-aligned, so every page's buttons share the page's left line and
   sizes. The #116 inset to the field width was dropped; `.boe-details-actions` stays as a hook.
   -------------------------------------------------------------------------- */

/* Details print: the four-column table layout above is kept at every print width, and the header
   row (visually hidden on narrow screens) is restored as the only heading source. Letter with
   default margins prints at ~720-740 CSS px, under md, where Bootstrap's `d-md-none` / `text-md-end`
   still act as narrow-screen rules; so the per-row mobile labels are hidden and the amount stays
   right-aligned under its heading. */
@media print {
    .boe-card.prc-payment-table-card .saved-payment-row .d-md-none {
        display: none !important;
    }

    .boe-card.prc-payment-table-card .saved-payment-row > .text-md-end {
        text-align: right !important;
    }

    /* The shared mobile rule's .75rem column-gap isn't screen-only and would shift saved cells off the header's columns. */
    .boe-card.prc-payment-table-card .row.saved-payment-row {
        column-gap: normal;
    }

    .boe-card.prc-payment-table-card .row.prc-saved-header-row {
        position: static !important;
        width: auto !important;
        height: auto !important;
        margin: 0 calc(var(--bs-gutter-x) * -.5) !important;
        padding: 0 0 .5rem !important;
        overflow: visible !important;
        clip: auto !important;
        white-space: normal !important;
        border: 0 !important;
        border-bottom: var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6) !important;
    }

    /* #117: BOE custom.css prints each link's URL after its text, which repeats the record lookup alert's
       contact as "(mailto:…)" and "(tel:+1…)". The link text already says it all (as the #112 status pages do). */
    .boe-details-page-alert a:link::after,
    .boe-details-page-alert a:visited::after {
        content: none !important;
    }
}
/* ==========================================================================
   end §details
   ========================================================================== */

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-control {
    font-size: 1rem;
}

select.form-control option {
    color: #495057;
    opacity: 1;
}

select.form-control:has(option[value=""]:checked) {
    color: #6c757d;
    opacity: 1;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/*
    Bootstrap v4.6.2 implementation of Grid Cards
    https://getbootstrap.com/docs/4.6/components/card/#grid-cards
*/

.row-cols-1 > * {
    -ms-flex: 0 0 100%;
    flex: 0 0 100%;
    max-width: 100%;
}

.row-cols-2 > * {
    -ms-flex: 0 0 50%;
    flex: 0 0 50%;
    max-width: 50%;
}

.row-cols-3 > * {
    -ms-flex: 0 0 33.333333%;
    flex: 0 0 33.333333%;
    max-width: 33.333333%;
}

.row-cols-4 > * {
    -ms-flex: 0 0 25%;
    flex: 0 0 25%;
    max-width: 25%;
}

.row-cols-5 > * {
    -ms-flex: 0 0 20%;
    flex: 0 0 20%;
    max-width: 20%;
}

.row-cols-6 > * {
    -ms-flex: 0 0 16.666667%;
    flex: 0 0 16.666667%;
    max-width: 16.666667%;
}

@media (min-width: 576px) {
    .row-cols-sm-1 > * {
        -ms-flex: 0 0 100%;
        flex: 0 0 100%;
        max-width: 100%;
    }

    .row-cols-sm-2 > * {
        -ms-flex: 0 0 50%;
        flex: 0 0 50%;
        max-width: 50%;
    }

    .row-cols-sm-3 > * {
        -ms-flex: 0 0 33.333333%;
        flex: 0 0 33.333333%;
        max-width: 33.333333%;
    }

    .row-cols-sm-4 > * {
        -ms-flex: 0 0 25%;
        flex: 0 0 25%;
        max-width: 25%;
    }

    .row-cols-sm-5 > * {
        -ms-flex: 0 0 20%;
        flex: 0 0 20%;
        max-width: 20%;
    }

    .row-cols-sm-6 > * {
        -ms-flex: 0 0 16.666667%;
        flex: 0 0 16.666667%;
        max-width: 16.666667%;
    }
}

@media (min-width: 768px) {
    .row-cols-md-1 > * {
        -ms-flex: 0 0 100%;
        flex: 0 0 100%;
        max-width: 100%;
    }

    .row-cols-md-2 > * {
        -ms-flex: 0 0 50%;
        flex: 0 0 50%;
        max-width: 50%;
    }

    .row-cols-md-3 > * {
        -ms-flex: 0 0 33.333333%;
        flex: 0 0 33.333333%;
        max-width: 33.333333%;
    }

    .row-cols-md-4 > * {
        -ms-flex: 0 0 25%;
        flex: 0 0 25%;
        max-width: 25%;
    }

    .row-cols-md-5 > * {
        -ms-flex: 0 0 20%;
        flex: 0 0 20%;
        max-width: 20%;
    }

    .row-cols-md-6 > * {
        -ms-flex: 0 0 16.666667%;
        flex: 0 0 16.666667%;
        max-width: 16.666667%;
    }
}

@media (min-width: 992px) {
    .row-cols-lg-1 > * {
        -ms-flex: 0 0 100%;
        flex: 0 0 100%;
        max-width: 100%;
    }

    .row-cols-lg-2 > * {
        -ms-flex: 0 0 50%;
        flex: 0 0 50%;
        max-width: 50%;
    }

    .row-cols-lg-3 > * {
        -ms-flex: 0 0 33.333333%;
        flex: 0 0 33.333333%;
        max-width: 33.333333%;
    }

    .row-cols-lg-4 > * {
        -ms-flex: 0 0 25%;
        flex: 0 0 25%;
        max-width: 25%;
    }

    .row-cols-lg-5 > * {
        -ms-flex: 0 0 20%;
        flex: 0 0 20%;
        max-width: 20%;
    }

    .row-cols-lg-6 > * {
        -ms-flex: 0 0 16.666667%;
        flex: 0 0 16.666667%;
        max-width: 16.666667%;
    }
}

@media (min-width: 1200px) {
    .row-cols-xl-1 > * {
        -ms-flex: 0 0 100%;
        flex: 0 0 100%;
        max-width: 100%;
    }

    .row-cols-xl-2 > * {
        -ms-flex: 0 0 50%;
        flex: 0 0 50%;
        max-width: 50%;
    }

    .row-cols-xl-3 > * {
        -ms-flex: 0 0 33.333333%;
        flex: 0 0 33.333333%;
        max-width: 33.333333%;
    }

    .row-cols-xl-4 > * {
        -ms-flex: 0 0 25%;
        flex: 0 0 25%;
        max-width: 25%;
    }

    .row-cols-xl-5 > * {
        -ms-flex: 0 0 20%;
        flex: 0 0 20%;
        max-width: 20%;
    }

    .row-cols-xl-6 > * {
        -ms-flex: 0 0 16.666667%;
        flex: 0 0 16.666667%;
        max-width: 16.666667%;
    }
}


/* Read-only summary captions. Replicates the state template's bare `label` rule so
   captions that are not associated with a form control render identically. */
.form-caption {
    display: inline-block;
    font-weight: 600;
    font-size: var(--font-size-regular);
    margin-bottom: .5rem;
}


/* ==========================================================================
   §cards — Cards and label/value summaries (issue #107, A2)
   Components: Components/Custom/SectionCard.razor, SummaryList.razor, SummaryRow.razor.

   Why app CSS: the CA State Template has no section-card or label/value summary
   component. The card keeps the existing PRC card look (Bootstrap utilities
   `bg-white border rounded-3 shadow-sm`, applied by SectionCard) and the title
   uses the template's `.h4` heading typography, as the Home page cards do; only
   spacing, the divider, wrapping and print are defined here.

   Pattern: one card per section, same padding at every breakpoint, same gap
   between cards, same max width on every Review/Confirmation page (wrap the
   cards, and the buttons that should align with them, in `.boe-card-stack`;
   every child of the stack is spaced by the card gap).
   ========================================================================== */
:root {
    /* One inset for every card surface in the portal (the Home cards' p-4 is the same 1.5rem). Was 1rem,
       which read as crowded against the card edge. */
    --boe-card-padding: 1.5rem;
    --boe-card-gap: 1rem;
    /* About the width the PRC Review card had in its col-xl-9 column at xl. */
    --boe-card-max-width: 54rem;
}

.boe-card-stack {
    width: 100%;
    max-width: var(--boe-card-max-width);
}

/* Card corners: the template sets Bootstrap's `.rounded-3` to 3px, which read as nearly square. Every card
   surface (SectionCard, including the PRC records cards, and the Home picker cards) uses 6px instead, chosen during the
   polish pass. `.rounded-3` is !important, so this has to be too; scoped to app card classes, not bare `.rounded-3`. */
.boe-card.rounded-3,
.boe-home-cards .rounded-3 {
    border-radius: .375rem !important;
}

/* One gap, not two: a card that matches both selectors still gets a single
   margin-top. `.boe-card + .boe-card` spaces cards used outside a stack. */
.boe-card + .boe-card,
.boe-card-stack > * + * {
    margin-top: var(--boe-card-gap);
}

/* PortalAlert's own `margin: 0 0 1rem` comes later in this file at the same specificity, so it zeroed the
   stack gap and a Details error alert sat flush against the card above it. */
.boe-card-stack > * + .portal-alert {
    margin-top: var(--boe-card-gap);
}

.boe-card-header {
    padding: var(--boe-card-padding) var(--boe-card-padding) 0;
}

/* Title-only card: the header closes the card. */
.boe-card-header:last-child {
    padding-bottom: var(--boe-card-padding);
}

.boe-card-header--divider {
    padding-bottom: .75rem;
    border-bottom: var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
}

/* Scoped to the card so the template's own .h4 margins (md and up) don't apply. */
.boe-card .boe-card-title {
    margin: 0;
    overflow-wrap: anywhere;
    text-wrap: balance;
}

.boe-card-body {
    padding: var(--boe-card-padding);
}

.boe-card-header:not(.boe-card-header--divider) + .boe-card-body {
    padding-top: .5rem;
}

.boe-card-body > :last-child {
    margin-bottom: 0;
}

/* PRC "Payment Records" cards on Review and Confirmation. There are no inputs here, so the .75rem cell inset
   Details needs (to line values up with input text) only pushed the records in from the content edge every
   other card uses. The rows' own Bootstrap gutters cancel out, so text lands on the card padding. */
.boe-card.review-records-card .prc-display-cell,
.boe-card.review-records-card .prc-col-header,
.boe-card.prc-confirmation-payment-table-card .prc-display-cell,
.boe-card.prc-confirmation-payment-table-card .prc-col-header {
    padding-inline: 0;
}

/* Below md each record row has .75rem block padding of its own; take it back out of the card's, so the
   first label and last value sit the same distance from the card edge as any other card's text. */
@media screen and (max-width: 767.98px) {
    .boe-card.review-records-card > .boe-card-body,
    .boe-card.prc-confirmation-payment-table-card > .boe-card-body {
        padding-block: calc(var(--boe-card-padding) - .75rem);
    }

    /* Below md the records stop reading as table rows and become stacked sections, so every divider in
       the PRC records cards runs the full card width, like the line under the card title (polish pass,
       user request). md+ keeps them inset, as table-row rules. */
    .boe-card.review-records-card hr,
    .boe-card.prc-confirmation-payment-table-card hr {
        margin-inline: calc(-1 * var(--boe-card-padding)) !important;
    }

    .boe-card.prc-payment-table-card .row.saved-payment-row + .saved-payment-row::before {
        inset-inline: calc(var(--bs-gutter-x) * .5 - var(--boe-card-padding));
    }

    /* Bordered blocks: pulled out to the card border, padded back so their content stays put. */
    .boe-card.prc-payment-table-card .prc-saved-payment-table,
    .boe-card.prc-payment-table-card .prc-add-another-heading {
        margin-inline: calc(-1 * var(--boe-card-padding));
        padding-inline: var(--boe-card-padding);
    }

    /* Details' .75rem cell inset lines values up with input text in the md+ table. Stacked, it only
       pushed records, total and field labels in from the inputs' edge and the "Add another record"
       heading, and wrapped the total sooner than Review's; drop it so everything shares the content
       edge, as on Review and Confirmation (polish pass). */
    .boe-card.prc-payment-table-card .prc-display-cell,
    .boe-card.prc-payment-table-card .prc-col-header {
        padding-inline: 0;
    }
}

/* Review's total is the last row of its records card, under an inset divider, as on Details (polish pass;
   it used to sit outside the card). Same block padding as Details' total row. */
.boe-card.review-records-card .row.review-total-row {
    padding-block: .5rem;
}

/* md+ and print: label right beside the amount, both against the amount column's right edge, as on
   Details. The col-8/col-4 classes stay for the markup contract; only their widths give way. */
@media (min-width: 768px), print {
    .boe-card.review-records-card .row.review-total-row {
        display: flex !important;
        flex-wrap: nowrap !important;
        justify-content: flex-end !important;
        align-items: baseline;
        column-gap: .35rem;
        margin-inline: 0;
    }

    .boe-card.review-records-card .row.review-total-row > .review-total-label,
    .boe-card.review-records-card .row.review-total-row > .review-total-amount {
        display: block !important;
        flex: 0 0 auto !important;
        width: auto !important;
        padding-inline: 0;
    }
}

/* Label/value summary: a <dl> of Bootstrap rows. Stacked on mobile; the
   col-md-4 / col-md-8 classes make it label | value from md up. The rows have
   no gutter, so they never reach past their container (a bare SummaryList
   needs no padded parent); from md up the label keeps a gutter-wide gap. */
.boe-summary-list {
    margin: 0;
}

.boe-summary-row {
    --bs-gutter-x: 0;
    padding-top: .375rem;
    padding-bottom: .375rem;
}

@media (min-width: 768px) {
    .boe-summary-row > .boe-summary-label {
        padding-inline-end: 1.5rem;
    }
}

.boe-summary-label,
.boe-summary-value {
    min-width: 0;
    margin: 0;
    font-size: var(--font-size-regular, 1rem);
    /* WCAG 1.4.10 (Reflow): long unbroken values (emails, transaction IDs,
       comments) wrap inside the card instead of overflowing it. */
    overflow-wrap: anywhere;
}

.boe-summary-label {
    font-weight: 600;
}

.boe-summary-value {
    font-weight: 400;
}

/* Multi-line values (e.g. Boundary Change Comments). Values are still
   HTML-encoded text; this only keeps the user's line breaks. */
.boe-summary-value--pre-line {
    white-space: pre-line;
}

/* Money total: the last row of the card it totals, bold, label next to amount. */
.boe-summary-row--total {
    margin-top: .375rem;
    padding-top: .75rem;
    border-top: var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
}

.boe-summary-row--total > .boe-summary-label,
.boe-summary-row--total > .boe-summary-value {
    font-weight: 700;
}

/* Stacked below md, the total's divider runs the full card width, like the records-card dividers
   (polish pass). Drawn on a pseudo-element so the row's own box, and its text, don't move. */
@media screen and (max-width: 767.98px) {
    .boe-summary-row--total {
        position: relative;
        border-top: 0;
    }

    .boe-summary-row--total::before {
        content: "";
        position: absolute;
        top: 0;
        inset-inline: calc(-1 * var(--boe-card-padding));
        border-top: var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
    }
}

/* Print rules for the card pattern (black on white, no shadow, thin border,
   kept whole) live in the shared §print section below, alongside §print's
   other shared/cross-page print rules (issue #124, A7). */
/* ==========================================================================
   end §cards
   ========================================================================== */

/* ==========================================================================
   §print — Shared print stylesheet (issue #124, A7)
   Print is a first-class breakpoint (rule 7, #105). This section owns every
   *shared/cross-page* print rule: template chrome (header, nav, footer,
   back-to-top — already carrying Bootstrap's `.d-print-none` at the point of
   use, BOEPublicLayout.razor/page action rows), link-URL decoration,
   heading/spacing, breakpoint-utility forcing (`.d-md-none` etc.), the
   shared components (§cards' `.boe-card`/`.boe-summary-row`, PortalAlert,
   InfoTooltip), and the PRC row/table patterns (`.saved-payment-row`,
   `.prc-total-row`) still shared by Details/Review/Confirmation. (BC's own
   `form > .row:has(> .col.text-break)` label/value-row pattern, once shared
   by BC Confirmation and Review, was removed here once both migrated to
   `SummaryList`/`SummaryRow` — Review in #146, Confirmation in this issue,
   #125/C4 — leaving no remaining consumer; see the `.boe-summary-row`
   PreLine rule below, which replaces it.) A new fix that benefits more than
   one page belongs here, not in a page section.

   It is NOT the only `@media print` block in the file: §details (~L413) and
   §review (~L1698) each carry their own page-scoped print rules, targeting
   only that lane's own page classes (`.boe-card.prc-payment-table-card`,
   `.boe-details-page-alert`, `.review-*`,
   `#prc-review-limit-info`/`#bc-review-limit-info`) — those were merged and
   signed off as part of the Details (#143) and Review (#146) lanes and are
   out of scope here. Where they overlap a rule in this section (e.g.
   §details' and §review's own `d-md-none`/`text-md-end` forcing, or
   §review's link-`::after` suppression), the overlap is intentional and
   redundant-with-the-same-effect, called out in each rule's own comment.
   Consolidating them into this section is a possible future cleanup, not
   part of #124.
   ========================================================================== */
@media print {
    /* BOEPublicLayout's inline <style> (rendered before app.css via
       HeadOutlet) forces a light-grey page background with !important for
       screen use. Restore true white for print so nothing prints as a grey
       background/wasted ink; app.css loading after that inline style wins
       the same-specificity !important tie. */
    body,
    .main-content {
        background: #fff !important;
    }

    /* No printed URL after internal/empty links (rule 7, #105 — external
       links keep the template's printed-URL behaviour). BOE's template print
       styles add `content: " (" attr(href) ")"` after every link, which for
       the breadcrumb's relative Home link rendered as the empty "BOE Payment
       Portal ()" (#124). Excluding http(s)/protocol-relative hrefs (rather
       than suppressing every link) also covers ProgramContact's mailto:/tel:
       links, whose visible text is already the full address/number, and the
       status pages' internal home link (#112, A4 — this rule replaces that
       page's own now-redundant `.boe-status-page` print rule, since status
       pages render inside this same `.main-content`). */
    .main-content a:not([href^="http://"]):not([href^="https://"]):not([href^="//"])::after {
        content: none !important;
    }

    /* Modest heading, no stray gap before the receipt content. */
    h1 {
        font-size: 1.5rem;
        margin-top: 0;
        margin-bottom: .5rem;
    }

    .breadcrumb {
        margin-bottom: .5rem !important;
    }

    h1 + p {
        margin-bottom: .75rem !important;
    }

    /* Headings kept with their content, not stranded at the bottom of a page. */
    h1, h2, h3 {
        break-after: avoid;
        page-break-after: avoid;
    }

    /* PRC rows/header (Details/Review/Confirmation) use Bootstrap's md
       breakpoint utilities to switch between the mobile stacked layout and
       the desktop table-like layout. Bootstrap's display utilities key off
       screen width; some browsers evaluate that against the current window
       instead of the print page box, leaving the mobile layout on paper.
       Forcing the md+ state guarantees the table-like layout every time. */
    .d-md-none {
        display: none !important;
    }

    /* :not(.d-print-none) so an element that is ever both a breakpoint
       utility and explicitly print-hidden still doesn't print — `.d-print-none`
       must always win. No element combines them today; this is defensive. */
    .d-none.d-md-flex:not(.d-print-none) {
        display: flex !important;
    }

    .d-none.d-md-block:not(.d-print-none) {
        display: block !important;
    }

    .text-start.text-md-end {
        text-align: end !important;
    }

    /* PRC Review/Confirmation's header/row columns (Details.razor moved to
       §details' own CSS grid and no longer uses these) carry only the md/xl
       breakpoint column classes with no bare fallback (e.g. "col-xl-3
       col-md-3", never plain "col-3"), so outside their matching breakpoint
       Bootstrap gives them no width at all — the display forcing above
       still leaves them collapsed to full-width stacked blocks. Re-declare
       the same widths Bootstrap's md/xl breakpoints already give them so the
       columns hold their table-like layout at print regardless of viewport
       width. */
    .col-xl-3.col-md-3:not(.d-print-none) {
        flex: 0 0 auto !important;
        width: 25% !important;
    }

    .col-xl-4.col-md-4:not(.d-print-none) {
        flex: 0 0 auto !important;
        width: 33.333333% !important;
    }

    /* PRC Confirmation's Assessment column (3/5/4, matching Review; polish pass). */
    .col-xl-5.col-md-5:not(.d-print-none) {
        flex: 0 0 auto !important;
        width: 41.666667% !important;
    }

    .col-xl-1.col-md-1:not(.d-print-none) {
        flex: 0 0 auto !important;
        width: 8.333333% !important;
    }

    /* The mobile stacked layout below (`@media (max-width: 767.98px)`) has no
       `screen` qualifier, so it can also match at print time on a narrow
       window, switching this row back to its CSS grid and breaking the
       table-like layout forced above. `.row` is present on every element
       carrying `.saved-payment-row` (Confirmation/Review). `.prc-edit-row`
       (Details' entry row) doesn't need a copy of this rule: it always
       carries `.prc-grid-row` too, and §details' own
       `.boe-card.prc-payment-table-card .row.prc-grid-row { display: grid
       !important }` (0-4-0, also matched at print) already outranks the
       mobile block's unqualified `.saved-payment-row` (0-1-0) for it.
       `.row.saved-payment-row:not(.d-print-none)` is 0-3-0 (`:not()` adds
       its argument's specificity, so `.d-print-none` counts as a third
       class here), which beats that same mobile rule regardless of which
       block appears later in the file. That same mobile rule's 0.75rem
       `column-gap` isn't reset by switching back to flex, though: a printed
       page is only ~720px wide, under the mobile rule's 767.98px cutoff, so
       it still applies its `column-gap: 0.75rem` on top of this flex row.
       PRC Confirmation's cells (25% + 33.33% + 33.33% + 8.33% spacer, forced
       visible in print by the `.d-none.d-md-block` rule above) already sum to
       100% width, so the extra gap pushes every cell right of its header
       column and can wrap the trailing spacer onto its own line (Copilot
       review R3 on this PR caught this on a narrow print width; same root
       cause Copilot review R2 on #148 caught for Review, and §review below
       carries its own identical reset for that reason — kept there as a
       redundant-but-safe page-scoped copy rather than removed now that the
       fix is shared here). §details doesn't need a copy: its rows are grid,
       not flex, there (see above), and its own `column-gap: normal` further
       down this file is equivalent to `0` for a grid with no explicit gap. */
    .row.saved-payment-row:not(.d-print-none) {
        display: flex !important;
        column-gap: 0 !important;
        flex-wrap: nowrap !important;
    }

    /* InfoTooltip (Components/Custom/InfoTooltip.razor) is an interactive
       help control, not receipt data, so it doesn't print — same reasoning
       as the `d-print-none` Details already puts on its own action/Save-
       Clear cells (§details), just applied here since InfoTooltip is a
       shared component with no `d-print-none` of its own. Used on PRC and
       BC Details' Payment Amount fields. Hiding the whole `.info-tooltip`
       wrapper (icon button + tooltip text) also covers a tooltip left
       pinned open (`.info-tooltip-text-shown`), so no floating tooltip box
       can appear on a printed page either. */
    .info-tooltip {
        display: none !important;
    }

    /* Every current consumer of these raw Bootstrap utility classes also carries
       `.boe-card` (PRC Details', Review's and Confirmation's `<SectionCard>` — its
       `BaseCssClass` is "boe-card bg-white border rounded-3 shadow-sm"),
       so this rule is redundant-but-harmless everywhere it fires, alongside the
       `.boe-card` print rule below — both set the same box-shadow/border values.
       (Before this issue, #125/C4, PRC Confirmation's payment-table card used only
       these raw classes with no `.boe-card`, making this rule load-bearing there;
       its SectionCard migration removed that last non-`.boe-card` consumer.) Kept
       rather than removed: harmless today, and a future page could reintroduce a
       raw-classes-only card without `.boe-card`. Details' entry row (`.prc-edit-row`)
       still keeps its own `break-inside: avoid` below so it can't split either. */
    .bg-white.border.rounded-3.shadow-sm {
        box-shadow: none !important;
        border-color: #767676 !important;
    }

    .saved-payment-row,
    .prc-edit-row,
    .prc-total-row {
        break-inside: avoid;
        page-break-inside: avoid;
    }

    /* The PRC total row (Details/Review) is the last row inside its records card; avoiding a break before
       it keeps it on the same page as the last record instead of orphaned alone. */
    .prc-total-row {
        break-before: avoid;
        page-break-before: avoid;
    }

    /* Keeps a records card's last row with the one before it. Descendant, not direct-child: SectionCard
       renders its rows inside `.boe-card-body`. Only Confirmation's records card ends on a
       `.saved-payment-row`; Details and Review end on their total row, which the rule above covers. */
    .bg-white.border.rounded-3.shadow-sm .saved-payment-row:last-child {
        break-before: avoid;
        page-break-before: avoid;
    }

    /* Card pattern (issue #107, A2): black on white, no shadow or background,
       a thin border, cards and rows kept whole, titles kept with their
       content, and the two-column summary layout even when the printed page
       is narrower than md. Dividers print at the card border's grey so they
       survive on paper, and "Not provided" prints black (the template's muted
       colour is !important) and italic, so it still reads as no value. */
    .boe-card {
        color: #000;
        background: #fff !important;
        box-shadow: none !important;
        border: 1px solid #767676 !important;
        break-inside: avoid;
        page-break-inside: avoid;
    }

    .boe-card-header {
        break-after: avoid;
        page-break-after: avoid;
    }

    .boe-card-header--divider {
        border-bottom-color: #767676;
    }

    /* The colour theme sets heading colours with !important. */
    .boe-card .boe-card-title {
        color: #000 !important;
    }

    .boe-summary-row {
        break-inside: avoid;
        page-break-inside: avoid;
    }

    /* SummaryRow's PreLine variant (issue #107) keeps a long free-text value's line
       breaks (BC Comments today; any future PreLine value). Like the old BC
       Confirmation/Review comment-row rule it fully replaces (`.main-content form >
       .row:has(> .col.text-break)`, removed here — BC Review moved to
       SummaryList in #146 and BC Confirmation moved in this issue, #125/C4,
       leaving zero remaining `.col.text-break` markup for it to match), it's a
       Bootstrap `.row` (flex), and Chromium never fragments a flex container
       across a page break — it moves the whole row to the next page, leaving a
       blank gap on the page before it. Restoring block flow lets it wrap like
       ordinary text, and relaxing this row's own break-inside back to auto stops
       a long value being forced whole onto one page. This selector's specificity
       beats the general `.boe-summary-row` rule above regardless of source
       order, so no `!important` is needed. Shared, not BC-specific — any future
       PreLine row benefits the same way (issue #125, C4). */
    .boe-summary-row:has(> .boe-summary-value--pre-line) {
        display: block;
        break-inside: auto;
        page-break-inside: auto;
    }

    /* The "Boundary Change Information" card can contain a long Comments value (the PreLine row
       above). `.boe-card`'s own `break-inside: avoid` (above, generic to every card) would still force the
       whole *card* — not just the row — to stay on one page, working against the PreLine row rule above: a
       card tall enough to need the row to fragment is, by definition, too tall to avoid breaking as a whole.
       Relax it back to auto specifically for a card containing a PreLine value; ordinary (short) cards are
       unaffected and still avoid breaking. Shared, not BC-specific, matching the row-level rule's own
       reasoning (issue #125, C4). */
    .boe-card:has(.boe-summary-value--pre-line) {
        break-inside: auto;
        page-break-inside: auto;
    }

    /* Migrating the PRC record-list card from a raw div to <SectionCard> (issue #125, C4) newly applies
       `.boe-card`'s generic `break-inside: avoid` to it — previously it had no such rule. With 1-3 records
       this is harmless, but at 15 records the card is taller than one printed page; forcing the *whole card*
       to avoid breaking would either push all 15 rows onto a fresh page (leaving the entire previous page
       blank) or force an overflow, instead of flowing naturally across pages. Individual `.saved-payment-row`s
       already keep their own `break-inside: avoid` (existing #124 rule above, unaffected), so rows still
       don't split mid-row; only the outer card's own break-inside needs relaxing so it CAN span multiple
       pages. */
    .boe-card.prc-confirmation-payment-table-card {
        break-inside: auto;
        page-break-inside: auto;
    }

    .boe-summary-row--total {
        border-top-color: #767676;
    }

    .boe-summary-empty {
        color: #000 !important;
        font-style: italic;
    }

    .boe-summary-row > .boe-summary-label {
        flex: 0 0 33.333333%;
        width: 33.333333%;
        max-width: 33.333333%;
        padding-inline-end: 1.5rem;
    }

    .boe-summary-row > .boe-summary-value {
        flex: 0 0 66.666667%;
        width: 66.666667%;
        max-width: 66.666667%;
    }

    /* PortalAlert (issue #106): black on white with no shadow or tint; a border, the icon and
       the text still say what it is. BOE custom.css removes alert borders with
       `.alert { border: none !important }` and hides every `.alert` in print with
       `.alert, .alert-boe .alert-dismissable { display: none; }`. Force the component back to
       flex, defensively using !important because the external template controls that rule.
       Other !important declarations beat template rules and Bootstrap's `.shadow-sm`. */
    .portal-alert {
        display: flex !important;
        color: #000 !important;
        background: none !important;
        box-shadow: none !important;
        border: 1px solid #000 !important;
        break-inside: avoid;
        page-break-inside: avoid;
    }

    .portal-alert a:not(.btn) {
        color: #000 !important;
    }

    /* A focused alert (role="alert" takes focus on render) keeps its screen focus
       ring in print: PortalAlert.razor's own `.portal-alert:focus-visible` rule
       (0-2-0, `box-shadow: var(--boe-focus-ring) !important`) sits after this
       @media print block in source order, so at equal specificity it would win
       both the cascade tie and the source-order tie, printing a blue ring on an
       alert that has focus (e.g. Details' error alert). Repeating the class
       (`.portal-alert.portal-alert:focus-visible`, 0-3-0) outranks it regardless
       of source order. Outline is reset too: the screen rule's `outline: 2px
       solid transparent` isn't `!important` and wouldn't print visibly on its
       own, but it's cleared here anyway so print never depends on that. */
    .portal-alert.portal-alert:focus-visible {
        box-shadow: none !important;
        outline: none !important;
    }
}
/* ==========================================================================
   end §print
   ========================================================================== */


/* Shared PRC table-card display helpers */

/* Matches Bootstrap form-control padding (0.375rem 0.75rem) so saved row text
   aligns pixel-perfectly with text inside input boxes */
.prc-display-cell {
    display: block;
    padding: 0.375rem 0.75rem;
}

/* Column header labels — uppercase, muted, slightly smaller to distinguish from data rows.
   These render inside the white .prc-payment-table-card, where the previously
   hardcoded #6c757d measured 4.69:1 — a pass, but the thinnest margin in the
   app against the 4.5:1 AA threshold (0.85rem is normal-size text, so 4.5
   applies, not 3.0). Sharing the token above takes it to 6.39:1. */
.prc-col-header {
    font-size: 0.85rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    color: var(--boe-text-muted-accessible);
    text-transform: uppercase;
}

.saved-payment-row {
    font-size: 1rem;
}

.prc-saved-header-row {
    border-bottom: var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
}

.prc-saved-payment-table {
    border-bottom: var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
}

.saved-payment-row + .saved-payment-row {
    border-top: var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
}

.prc-edit-row .prc-col-header {
    display: block;
    padding-left: 0.75rem;
}

/* Mobile (xs/sm < 768px): switch to CSS Grid so the action button can span
   both data rows and stay vertically centered — impossible with flex-wrap */
@media (max-width: 767.98px) {
    .prc-saved-header-row {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
        border: 0;
    }

    .saved-payment-row,
    .prc-edit-row {
        display: grid !important;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
        column-gap: 0.75rem;
    }

    .saved-payment-row > div,
    .prc-edit-row > div {
        width: auto;
        min-width: 0;
    }

    .prc-edit-row .form-control {
        min-width: 0;
        max-width: 100%;
    }

    .saved-payment-row {
        row-gap: 0.5rem;
    }

    .prc-edit-row {
        row-gap: 0.75rem;
    }

    /* SBE Number */
    .saved-payment-row > div:nth-child(1),
    .prc-edit-row > div:nth-child(1) {
        grid-column: 1;
        grid-row: 1;
    }

    /* Assessment Notice Number */
    .saved-payment-row > div:nth-child(2),
    .prc-edit-row > div:nth-child(2) {
        grid-column: 2;
        grid-row: 1;
    }

    /* Payment Amount — spans both columns */
    .saved-payment-row > div:nth-child(3),
    .prc-edit-row > div:nth-child(3) {
        grid-column: 1 / 3;
        grid-row: 2;
    }

    /* Action button — spans both rows, vertically centered */
    .saved-payment-row > div:nth-child(4),
    .prc-edit-row > div:nth-child(4) {
        grid-column: 3;
        grid-row: 1 / 3;
        align-self: stretch;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 0;
    }

    .prc-edit-row > div:nth-child(4) {
        flex-direction: column;
        gap: 0.5rem !important;
    }

    /* Align all mobile inline labels with their values (matches prc-display-cell left padding) */
    .saved-payment-row .prc-col-header {
        padding-left: 0.75rem;
    }

    /* Larger touch targets for row action buttons on mobile */
    .saved-payment-row > div:nth-child(4) .btn,
    .prc-edit-row > div:nth-child(4) .btn {
        min-width: 44px;
        min-height: 44px;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    /* Value spans hug their inline labels — breathing room comes from py-4 on the row */
    .saved-payment-row .prc-display-cell {
        padding-top: 0;
    }
}

/* Mobile: center the total payment row and auto-size columns */
@media (max-width: 767.98px) {
    .prc-total-row {
        display: flex !important;
        flex-wrap: nowrap;
        justify-content: center !important;
        padding-left: 0 !important;
        padding-right: 0 !important;
        gap: 0.5rem;
    }

    .prc-total-row > div {
        width: auto !important;
        flex: none !important;
    }

    /* Hide the empty spacer column */
    .prc-total-row > div:last-child {
        display: none !important;
    }

    /* Amount value left-aligned when centered (not right-aligned to an invisible edge) */
    .prc-total-row .col-4 {
        text-align: left !important;
    }

    /* Increase total amount prominence on mobile */
    .prc-total-row strong {
        font-size: 1.25rem;
    }
}

.info-tooltip-icon {
    font-size: 1.25rem;
    vertical-align: middle;
}

.info-tooltip {
    position: relative;
    display: inline-block;
}

/* The trigger is a real button (see Components/Custom/InfoTooltip.razor) so it is reachable and
   operable by keyboard. It is stripped back to look like the bare icon it replaces. */
.info-tooltip-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* #116: WCAG 2.5.8 target size (the icon alone was 16x20). */
    min-width: 24px;
    min-height: 24px;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    line-height: 1;
    cursor: pointer;
}

@media (pointer: coarse) {
    .info-tooltip-trigger {
        min-width: 44px;
        min-height: 44px;
    }
}

/* WCAG 1.4.11 (Non-text Contrast) / 2.4.7 (Focus Visible): this element previously had
   its outline suppressed with no replacement, leaving keyboard users with no indicator at all. Reuse the
   same focus ring the buttons and form controls use at the top of this file. */
.info-tooltip-trigger:focus-visible {
    box-shadow: var(--boe-focus-ring);
    border-radius: 0.25rem;
}

/* Hidden with opacity rather than visibility/display so the text remains in the accessibility tree
   and the trigger's aria-describedby always resolves to real content (WCAG 4.1.2).

   width: 0 at rest so the hidden tooltip can't widen the page (issue #114): removing the PRC
   card's overflow-hidden below let this element's full-width layout box leak past the viewport at
   rest. Text stays in the DOM regardless of box width, so this costs nothing for assistive tech;
   .info-tooltip-text-shown restores the real width only while actually visible. */
.info-tooltip-text {
    opacity: 0;
    pointer-events: none;
    width: 0;
    overflow: hidden;
    background-color: #f8f9fa;
    color: #212529;
    text-align: left;
    border-radius: 0.25rem;
    padding: 0.5rem 1rem;
    position: absolute;
    z-index: 10;
    left: 100%;
    margin-left: 0.5rem;
    top: 50%;
    transform: translateY(-50%);
    box-shadow: 0 2px 8px rgba(0,0,0,0.15);
    font-size: 0.95rem;
    border: 1px solid #b6b6b6;
    white-space: normal;
}

/* WCAG 1.4.13 (hoverable): an invisible bridge spans the gap between the trigger and the tooltip so
   the pointer can travel into the tooltip without leaving the wrapper and dismissing it.
   The bridge is 4px wider than the 0.5rem gap so that it OVERLAPS the trigger instead of merely
   abutting it. Measured in Edge, a bridge sized exactly to the gap left a sub-pixel seam at the
   wrapper's right edge on the Boundary Change page: the pointer landed on the parent column for one
   pixel, mouseleave fired, and the tooltip closed before the pointer could reach it. */
.info-tooltip-text::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(-0.5rem - 4px);
    width: calc(0.5rem + 4px);
}

.info-tooltip-text-shown {
    opacity: 1;
    pointer-events: auto;
    width: 320px;
    overflow: visible;
}

/* Below Bootstrap's xl breakpoint the tooltip doesn't fit beside the trigger — flip it below
   instead (issue #114 §15 #2). Widened from lg to xl after verification found the side placement
   still overflowed the viewport across the whole 992-1199px lg band on the PRC page. Anchoring to
   the wider field wrapper (each Payment Amount field's own `.d-flex.position-relative` in
   PrivateRailroadCar/Details.razor and BoundaryChange/Details.razor) rather than .info-tooltip's
   own tiny icon-sized box avoids the same overflow on the left edge at 320px.

   #116: below md (the mobile layout, where the field is full width) the shown tooltip joins the
   wrapper's flex flow on a line of its own (`display: contents` on .info-tooltip; the wrapper is
   `flex-wrap flex-md-nowrap`), so it pushes the content below down instead of covering it. At
   rest it stays absolute and 0 wide. From md the PRC amount column is too narrow for that, and
   an in-flow tooltip would move the row buttons off the input centreline. */
@media (max-width: 1199.98px) {
    .info-tooltip {
        position: static;
    }

    .info-tooltip-text {
        left: 0;
        right: auto;
        top: 100%;
        margin-left: 0;
        margin-top: 0.5rem;
        transform: none;
    }

    /* Width lives on -shown, not the base rule: at rest it must stay 0 (see .info-tooltip-text
       above) so it can never widen the page, even though .info-tooltip is non-positioned here. */
    .info-tooltip-text-shown {
        width: 260px;
        max-width: calc(100vw - 2rem);
    }

    .info-tooltip-text::before {
        left: 0;
        right: 0;
        bottom: auto;
        top: calc(-0.5rem - 4px);
        width: auto;
        height: calc(0.5rem + 4px);
    }
}

@media (max-width: 767.98px) {
    .info-tooltip {
        display: contents;
    }

    /* The input would otherwise take the whole line and push the trigger onto the next one. */
    :has(> .info-tooltip) > .form-control {
        flex: 1 1 0%;
        min-width: 0;
    }

    /* The wrapper's own margin (e.g. ms-2) doesn't apply under display: contents. */
    .info-tooltip > .info-tooltip-trigger {
        margin-left: .5rem;
    }

    .info-tooltip-text-shown {
        position: relative;
        top: auto;
        flex: 0 0 100%;
        width: auto;
        max-width: 100%;
    }
}

/* ==========================================================================
   Shared alerts — PortalAlert (issue #106)
   Components/Custom/PortalAlert.razor. Colours, padding and square corners
   are the state template's own `.alert` / `.alert-{level}` rules (the
   template's --alert-*-text / --alert-*-bg tokens) and are deliberately not
   redefined here; on screen BOE's custom.css removes the border. The one edge
   treatment added is Bootstrap's `.shadow-sm`, as the app's cards use. The
   template has no in-page alert pattern with an icon (its icon layout is
   scoped to the header `.alert-banner`), so this section only adds the icon +
   text layout, spacing, High Contrast and print rules it lacks.

   The icon glyphs come from the template's CaGov font. It is served without
   CORS headers, so it loads only when the app shares the template's origin
   (every deployed environment). Running on localhost, the icons are blank.
   ========================================================================== */

/* Icon beside left-aligned text. Width is the parent's content column. The 1rem gap matches the
   template's 1rem alert padding, so the icon sits evenly between the alert's edge and the text. */
.portal-alert {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    text-align: left;
    margin: 0 0 1rem;
    /* Same side inset as the cards (padding + their 1px border; alerts have none), so the icon lines up with
       card text in a stack. */
    padding-inline: calc(var(--boe-card-padding) + var(--bs-border-width, 1px));
}

/* Sized relative to the text so it sits centred on the first line at any zoom: 1.375em x (1.5 / 1.375)
   line height = the 1.5em line box of the template's body text. */
.portal-alert-icon {
    flex: none;
    font-size: 1.375em;
    line-height: calc(1.5 / 1.375);
}

/* WCAG 1.4.10 (Reflow): min-width: 0 lets the text column shrink inside the flex row and
   overflow-wrap breaks long unbroken tokens (URLs, email addresses), so nothing scrolls
   sideways at 320px / 400% zoom. */
.portal-alert-content {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}

/* One paragraph rhythm for every multi-paragraph alert, tighter than body copy's 1rem so the lines
   read as one message. Call sites don't set their own mb-* classes. */
.portal-alert-content > p {
    margin-bottom: 0.5rem;
}

.portal-alert-content > .portal-alert-title {
    font-weight: 700;
    margin-bottom: 0.25rem;
}

/* Must stay after the title rule (same specificity) so a title-only alert keeps no bottom margin. */
.portal-alert-content > :last-child {
    margin-bottom: 0;
}

/* Alerts that take focus (role="alert") use the same focus ring as buttons and form controls.
   The transparent outline becomes visible in forced-colors (High Contrast) mode, where box-shadow is dropped. */
.portal-alert:focus-visible {
    outline: 2px solid transparent;
    outline-offset: 2px;
    box-shadow: var(--boe-focus-ring) !important;
}

/* WCAG 1.4.11 (Non-text Contrast): High Contrast mode drops the tinted background and the shadow, and
   BOE custom.css has already removed the border, which would leave nothing marking the alert's edge.
   A system-colour border restores it. !important beats custom.css's `border: none !important`. */
@media (forced-colors: active) {
    .portal-alert {
        border: 1px solid CanvasText !important;
    }
}

/* Print rules for PortalAlert (black on white, no shadow, a border) live in the shared
   §print section above, alongside §print's other shared/cross-page print rules (issue #124, A7). */

/* Print rules for StatusPage's link-URL suppression (issue #112, A4) live in
   the shared §print section above (the `.main-content a:not(...)::after`
   rule), alongside §print's other shared/cross-page print rules (issue #124, A7) —
   `.boe-status-page` renders inside that same `.main-content`, so a separate
   rule here would only duplicate it. */

/* ==========================================================================
   §review — Review lane (issue #122, C3)
   Components/Pages/PrivateRailroadCar/Review.razor, BoundaryChange/Review.razor.

   Why app CSS: §cards (#107) supplies `.boe-card-stack`/`.boe-card` (both pages
   now wrap their content in a stack, and PRC keeps its own hand-rolled table
   with `.boe-card` added for the print/spacing hooks, per decision #2); the
   shared PRC table CSS above supplies `.saved-payment-row`/`.prc-total-row`
   used by Details, Confirmation and Review alike. This section only adds
   Review's own additive classes — it never edits those shared rules, so
   Details/Confirmation/#116/#124 are untouched.

   PRC Review dropped the dead 4th (spacer) column and rebalanced its 3
   columns to SBE 3 / Assessment Notice Number 5 / Payment Amount 4 (decision
   #3); `.review-col-sbe`/`.review-col-assessment`/`.review-col-amount` carry
   the new `.col-xl-*.col-md-*` combos so they can be targeted without
   touching the shared `.col-xl-4.col-md-4`/`.col-xl-1.col-md-1` combos still
   used by Details/Confirmation. `.review-header-row`/`.review-record-row`
   mark Review's own header/data rows; `.review-total-row`/
   `.review-total-label`/`.review-total-amount` mark its total row (now
   `col-8`/`col-4`, was `col-7`/`col-4`/`col-1`).
   ========================================================================== */

/* Below md (screen only): each record is one column, SBE Number -> Assessment
   Notice Number -> Payment Amount, label above value (decision #4). The
   compound `.review-record-row.saved-payment-row` selector (0-2-0) beats the
   shared `.saved-payment-row` 2-column grid (0-1-0, #107/Details/Confirmation,
   left untouched) regardless of source order, the same technique used
   throughout the shared PRC table CSS above. `screen` keeps this out of print
   entirely; print gets its own forcing rule below instead. */
@media screen and (max-width: 767.98px) {
    .review-record-row.saved-payment-row {
        display: grid !important;
        grid-template-columns: minmax(0, 1fr) !important;
        row-gap: 0.5rem;
        /* Same record padding as Details and Confirmation below md (was `py-4`, twice as loose). */
        padding-block: 0.75rem;
    }

    .review-record-row.saved-payment-row > .review-col-sbe {
        grid-column: 1;
        grid-row: 1;
    }

    .review-record-row.saved-payment-row > .review-col-assessment {
        grid-column: 1;
        grid-row: 2;
    }

    .review-record-row.saved-payment-row > .review-col-amount {
        grid-column: 1;
        grid-row: 3;
    }

    /* Total row: label and amount on one line, start-aligned like the record values above, as on Details;
       it wraps onto two lines only when the viewport is too narrow (the shared rule's `nowrap` is what
       clipped at 320-375px, decision #5). The row is inside the card now, so its own gutters cancel and
       its text lands on the card's content edge; the gutters are dropped here only so the label and amount
       sit a word-space apart rather than a full gutter. .75rem block padding matches the record rows (the
       card body gives that much back below md). */
    .review-total-row.prc-total-row {
        flex-wrap: wrap !important;
        justify-content: flex-start !important;
        align-items: baseline;
        margin-inline: 0;
        column-gap: .35rem;
        row-gap: 0;
        padding-block: .75rem;
    }

    .review-total-row.prc-total-row > .review-total-label,
    .review-total-row.prc-total-row > .review-total-amount {
        width: auto !important;
        flex: 0 0 auto !important;
        padding-inline: 0;
    }

    .review-total-row.prc-total-row > .review-total-label {
        justify-content: flex-start !important;
    }

    /* The shared rule hides `.prc-total-row > div:last-child` (originally the
       empty spacer column); dropping the spacer makes the amount cell the new
       last child, so it must be explicitly restored here. */
    .review-total-row.prc-total-row > .review-total-amount.col-4:last-child {
        display: block !important;
    }

    /* Amount stays on one line at 1.25rem (decision #5); the shared rule
       already sets the font size, this only stops it wrapping. */
    .review-total-row .review-total-amount .prc-display-cell {
        white-space: nowrap;
    }
}

/* Print: force Review's own 3/5/4 layout regardless of print viewport width,
   independent of whether #124/#137's shared print stylesheet has landed
   (decision #3 condition). #137 re-declares `.col-xl-4.col-md-4`/
   `.col-xl-1.col-md-1`/`.col-xl-3.col-md-3` widths and forces
   `.row.saved-payment-row` back to flex for Details/Confirmation, which still
   use those combos — but Review's new `.col-xl-5.col-md-5` Assessment column
   doesn't match any of them, so Review needs its own copy of the same
   technique (the approach PR #144 used to scope a mobile override to one
   page), scoped to Review's own classes so it can never double up with
   #137's rules for the shared combos. Verified both standalone and merged
   locally with #137's head branch (not pushed). */
@media print {
    .review-header-row.d-none,
    .review-record-row.saved-payment-row {
        display: flex !important;
        /* The shared mobile grid rule (`.saved-payment-row, .prc-edit-row { display: grid;
           column-gap: 0.75rem }`, not screen-scoped) still matches `.review-record-row` in
           print and leaks its 0.75rem gap onto this now-flex row — `gx-3` already supplies
           the column gutter via padding, so the extra gap pushed the Amount column past the
           row's width and wrapped it onto its own line under `.row`'s default `flex-wrap:
           wrap` (Copilot review R2 on PR #148 caught the resulting header/value offset).
           `nowrap` is kept as a second guard so no column can ever wrap here. */
        column-gap: 0 !important;
        flex-wrap: nowrap !important;
    }

    /* Header divider (`hr.d-md-block`, right after the header row) is hidden below
       768px like the rest of the mobile layout, but the header row above it is
       forced visible for print — leaving it invisible left the header floating with
       no rule under it. Scoped to the adjacent-sibling of Review's own
       `.review-header-row` rather than `.boe-card` (the shared #107 card class):
       Confirmation.razor has an identical `hr.d-md-block` right after its own header
       row, and #125's PRC Confirmation migration to #107 cards (this issue, C4) means
       a `.boe-card` selector now does reach Confirmation's divider too — scoping to
       `.review-header-row` keeps this rule Review-only, as intended (Copilot review
       R2 on PR #148 caught the missing divider; orchestrator caught the scoping
       risk). */
    .review-header-row + hr.d-md-block {
        display: block !important;
    }

    /* The screen-only mobile rule above gives records 0.75rem padding; print is not
       `screen`, so without this the print rows would fall back to `py-md-1` only at 768px+.
       `py-md-1`'s desktop density is restored explicitly so print matches it
       regardless of viewport width (Copilot review R2 on PR #148 caught this). */
    .review-record-row {
        padding-top: 0.25rem !important;
        padding-bottom: 0.25rem !important;
    }

    .review-col-sbe.col-xl-3.col-md-3 {
        flex: 0 0 auto !important;
        width: 25% !important;
    }

    .review-col-assessment.col-xl-5.col-md-5 {
        flex: 0 0 auto !important;
        width: 41.666667% !important;
    }

    .review-col-amount.col-xl-4.col-md-4 {
        flex: 0 0 auto !important;
        width: 33.333333% !important;
        text-align: end !important;
    }

    /* The total row's print layout lives with its screen md+ rule (§cards, "label right beside the
       amount"); the old 66/33 split here left a wide gap between label and amount. */

    /* The per-cell inline mobile labels (`d-block d-md-none`) are hidden only
       above 768px, but a printable page is usually narrower than that (Letter
       portrait is ~720-750 CSS px), so without this they'd print alongside
       the header row above, duplicating every "SBE NUMBER" / "ASSESSMENT
       NOTICE NUMBER" / "PAYMENT AMOUNT" label under each record (Copilot
       review R1 on PR #148 caught this). #137's shared print stylesheet has
       its own blanket `.d-md-none { display: none !important }` that would
       also cover these, making this rule redundant once #137 lands — but
       harmless, so it's kept so Review is correct standalone before #137
       merges. Verified with headless print emulation at 700/750px and a real
       Letter-format PDF, 1 record and many records: 0 inline labels visible,
       header row shows exactly once. */
    .review-record-row .prc-col-header.d-md-none {
        display: none !important;
    }

    /* Rows/heading kept whole, matching the shared PRC print rules above. */
    .review-record-row,
    .review-total-row {
        break-inside: avoid;
        page-break-inside: avoid;
    }

    /* Issue #123's proactive over-limit alert's "go back and edit your
       records"/"...details" link is an internal Details link, so BOE
       custom.css's `a:link:after { content: " (" attr(href) ") " }` would
       print "(private-railroad-car/details)"/"(boundary-change/details)"
       after it — parent #105 rule 7 says internal links print no URL, same
       reasoning as §status-pages above. Scoped to the alert's own id (not
       every Review link) so it can never touch the breadcrumb or any future
       Review link with different intent. Redundant-but-safe once #137 lands:
       PR #137 (head 4aacf99) adds `.main-content
       a:not([href^="http://"]):not([href^="https://"]):not([href^="//"])
       ::after { content: none !important; }`, a blanket rule for every
       relative link in `.main-content` that already covers this alert's
       link — but this narrower rule is kept so Review prints correctly
       standalone before #137 merges (same precedent as the `d-md-none` rule
       above). */
    #prc-review-limit-info a:link::after,
    #prc-review-limit-info a:visited::after,
    #bc-review-limit-info a:link::after,
    #bc-review-limit-info a:visited::after {
        content: none !important;
    }
}
/* ==========================================================================
   end §review
   ========================================================================== */

/* ==========================================================================
   §confirmation — Confirmation page polish (issue #125, C4)
   Components: PrivateRailroadCar/Confirmation.razor, BoundaryChange/Confirmation.razor.

   Why app CSS: mirrors PRC Details' own mobile record-stacking pattern
   (issue #116, PR #144, §details, `.boe-card.prc-payment-table-card`), scoped
   under a Confirmation-only class so it doesn't touch the shared unqualified
   `@media (max-width: 767.98px) .saved-payment-row` rule that PRC Review and
   Details also use (rule 6 parity is via *separate*, page-scoped rules with
   the same shape, not one shared rule yet — that unification is a follow-up
   issue, not this one). `screen`-qualified so it never applies at print time,
   unlike the legacy unqualified rule it overrides (which needs explicit
   print-time correction elsewhere in this file).
   ========================================================================== */
@media screen and (max-width: 767.98px) {
    /* Three full-width rows, label above value, in document order (SBE,
       Assessment, Amount) — a single grid column undoes the shared rule's
       2-up placement (SBE|Assessment side by side, Amount spanning below)
       without touching that shared rule itself. The 4th (spacer) column is
       already `d-none d-md-block`, so there's no Delete-column case to
       handle (unlike Details' PR #144 pattern, which has one). */
    .boe-card.prc-confirmation-payment-table-card .row.saved-payment-row {
        grid-template-columns: minmax(0, 1fr);
        row-gap: .5rem;
        padding-block: .75rem;
    }

    .boe-card.prc-confirmation-payment-table-card .row.saved-payment-row > div {
        grid-column: 1;
        /* The shared unqualified mobile rule this overrides sets an explicit `grid-row` per nth-child
           (1, 1, 2) for its 2-up layout. Cascade resolves `grid-column` and `grid-row` as separate
           properties — this selector's higher specificity wins the `grid-column` declaration on its own, but
           doesn't erase the shared rule's `grid-row` values unless this rule also declares `grid-row`.
           Without this line, children 1 and 2 both land in row 1 (same column, same row = overlapping), and
           child 3 lands in row 2, instead of the intended 3 separate full-width rows. `auto` lets the
           single-column grid place every child in document order. */
        grid-row: auto;
    }
}
/* ==========================================================================
   end §confirmation
   ========================================================================== */


/* ==========================================================================
   §home — Home page (issue #126, C1)
   Component: Components/Pages/Home.razor.

   Three rules the template/Bootstrap have no equivalent for: a balanced H1 wrap
   (not a bare Bootstrap utility), a width cap on the two picker cards so
   they don't stretch full-container-width on wide screens, and a card-body
   padding that survives the template's below-md override. Everything else on
   Home (prose column, card margins, gutters, spacing) uses existing
   template/Bootstrap classes directly in the markup.
   ========================================================================== */
.text-wrap-balance {
    text-wrap: balance;
}

.boe-home-cards {
    max-width: 1000px;
}

/* Print: Home has no page-specific print rules. Printed link URLs (the contact mailto:/tel: links and
   the internal card links) are suppressed by the shared rule in §print (A7, #124), and the two Get Started
   buttons carry Bootstrap's `.d-print-none` in the markup, like the action buttons on the other pages. */


/* ==========================================================================
   §payment-request — Payment Request page (issue #127, B5)
   Component: Components/Pages/Payment/Request.razor.

   Why app CSS: the template has no delayed reveal. The page is normally shown for a moment, so the status text
   only appears if the redirect is slow (3s), using animation-delay so no script timer is needed.
   ========================================================================== */
.payment-request-spinner {
    /* Still intentionally large, but shrinks on short viewports (the template header takes about 15rem) so the spinner
       and status line stay in the first screen instead of being pushed below the fold. */
    width: clamp(6rem, calc(100vh - 24rem), 15rem);
    height: clamp(6rem, calc(100vh - 24rem), 15rem);
}

.payment-request-status {
    max-width: 30rem;
    opacity: 0;
    animation: payment-request-status-in .5s ease-out 3s forwards;
}

@keyframes payment-request-status-in {
    to { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    /* Keep the 3s delay but skip the fade. */
    .payment-request-status {
        animation-duration: .01s;
    }
}

@media print {
    .payment-request-status {
        display: none !important;
    }
}
/* ==========================================================================
   end §payment-request
   ========================================================================== */
