﻿html {
  font-size: 14px;
  /* Always render the vertical scrollbar so users get a persistent visible
     scroll affordance. Modern Windows defaults to "auto-hide scrollbars"
     and only fades them in during active scrolling — Mike reported the
     dashboard had no visible scrollbar despite content overflowing the
     viewport. Forcing overflow-y: scroll reserves the scrollbar gutter
     at all times, so the control is always there to grab. */
  overflow-y: scroll;
}

@media (min-width: 768px) {
  html {
    font-size: 16px;
  }
}

body {
    min-height: 100vh;
    display: flex;
    flex-direction: column;    
}

main {
  flex: 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;
}

/* Keep a floating label inside its own field.

   Bootstrap positions .form-floating > label absolutely with no width bound, so a label longer
   than its input simply runs past the field and over whatever sits beside it. On the Email Log
   filter row "Search by recipient, Details or Sent By" overlapped the Filter by Server select
   and read as a rendering fault. Clipping is the right fix rather than shortening every label
   one at a time: the <label> still carries the full text for assistive technology, and the help
   text under these fields already says more than the label does.

   max-width rather than width because Bootstrap shrinks the label with a transform once it
   floats, and a hard width would then leave a gap on the right of the shrunken text. */
.form-floating > label {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Icon pair - works for both mobile and desktop buttons */
.icon-pair {
    display: inline-flex;
    align-items: center;
    vertical-align: middle;
}

    .icon-pair span {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        height: 1.2em;
        vertical-align: middle;
    }

        /* Default spacing for narrow icons (🧍, ⚡, ⚙️) */
        .icon-pair span:first-child {
            margin-right: -8px;
        }

    /* Reduced spacing for wider icons (🧍, 🌎) - prevents overlap */
    .icon-pair.icon-wide span:first-child {
        margin-right: -4px;
    }

/* Gentler spacing in navbar menus (mobile hamburger renders larger) */
.navbar .icon-pair span:first-child {
    margin-right: -5px;
}

.navbar .icon-pair.icon-wide span:first-child {
    margin-right: -2px;
}

/* Desktop CTA buttons need more space for wide icons */
.icon-pair.icon-wide span:first-child {
    margin-right: 0px;
}


/* #region Spinner & Animation Utilities */

.spin {
    animation: spin 1s linear infinite;
}

@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

/* Border-radius helpers */
.no-right-radius {
    border-top-right-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
}

.no-left-radius {
    border-top-left-radius: 0 !important;
    border-bottom-left-radius: 0 !important;
}

/* #endregion Spinner & Animation Utilities */




/* #region HTML5 Native Date Input Styling */

input[type="date"],
input[type="datetime-local"],
input[type="month"],
input[type="week"] {
    position: relative;
    padding: 0.5rem 0.75rem;
    font-size: 1rem;
    line-height: 1.5;
    color: #212529;
    background-color: #fff;
    border: 1px solid #ced4da;
    border-radius: 0.375rem;
    transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}

input[type="date"]:focus,
input[type="datetime-local"]:focus,
input[type="month"]:focus,
input[type="week"]:focus {
    color: #212529;
    background-color: #fff;
    border-color: #86b7fe;
    outline: 0;
    box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}

/* Style the calendar icon */
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%230d6efd' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z'/%3E%3C/svg%3E");
    cursor: pointer;
    padding: 0.25rem;
}

input[type="date"]::-webkit-calendar-picker-indicator:hover,
input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover {
    background-color: rgba(13, 110, 253, 0.1);
    border-radius: 0.25rem;
}

/* #endregion HTML5 Native Date Input Styling */


/* #region jQuery UI DatePicker Global Styles */

.date-picker {
    cursor: pointer;
}

.date-picker:focus {
    outline: 2px solid #007bff;
    outline-offset: 2px;
}

/* Fix datepicker z-index to appear above Bootstrap modals and sticky headers */
.ui-datepicker {
    z-index: 9999 !important;
    margin-top: 0.5rem !important;
}

.ui-widget-overlay {
    z-index: 9998 !important;
}

/* Customize datepicker header */
.ui-datepicker .ui-datepicker-header {
    background: #e9ecef !important;
    border: 1px solid #ced4da !important;
    color: #212529 !important;
}

.ui-datepicker .ui-datepicker-title {
    color: #212529 !important;
    text-shadow: none;
}

/* Make month/year dropdowns visible and styled */
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year {
    display: inline-block !important;
    visibility: visible !important;
    opacity: 1 !important;
    width: auto !important;
    min-width: 70px !important;
    height: auto !important;
    margin: 0 3px !important;
    padding: 3px 8px !important;
    font-size: 1em !important;
    line-height: normal !important;
}

/* Override Bootstrap table styles that interfere with datepicker */
.ui-datepicker table {
    width: 100% !important;
    margin: 0 !important;
    border-collapse: collapse !important;
}

.ui-datepicker table td,
.ui-datepicker table th {
    white-space: normal !important;
    min-width: auto !important;
    padding: 2px !important;
    text-align: center !important;
    border: none !important;
}

/* Remove underlines and fix spacing for date links */
.ui-datepicker table td a,
.ui-datepicker table td span,
.ui-datepicker td a.ui-state-default,
.ui-datepicker td span.ui-state-default,
.ui-datepicker a,
.ui-datepicker a.ui-state-default {
    text-decoration: none !important;
    border: none !important;
    padding: 2px !important;
    margin: 0 !important;
    display: block !important;
    line-height: 1.4 !important;
}

.ui-datepicker .ui-state-default,
.ui-datepicker .ui-state-default:link,
.ui-datepicker .ui-state-default:visited,
.ui-datepicker .ui-state-default:hover,
.ui-datepicker .ui-state-default:active {
    text-decoration: none !important;
    border: 1px solid transparent !important;
}

/* Style the month/year navigation buttons */
.ui-datepicker .ui-datepicker-prev,
.ui-datepicker .ui-datepicker-next {
    background: #6c757d !important;
    border: 1px solid #5a6268 !important;
    color: #ffffff !important;
}

.ui-datepicker .ui-datepicker-prev:hover,
.ui-datepicker .ui-datepicker-next:hover {
    background: #5a6268 !important;
    border: 1px solid #4e555b !important;
}

/* Improved date picker button panel */
.ui-datepicker .ui-datepicker-buttonpane {
    background-image: none;
    margin: 0.7em 0 0 0;
    padding: 0 0.2em;
    border-left: 0;
    border-right: 0;
    border-bottom: 0;
}

.ui-datepicker .ui-datepicker-buttonpane button {
    float: right;
    margin: 0.5em 0.2em 0.4em;
    cursor: pointer;
    padding: 0.2em 0.6em 0.3em 0.6em;
    width: auto;
    overflow: visible;
}

.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
    float: left;
}

/* #endregion jQuery UI DatePicker Global Styles */


/* #region ADA Compliance Styles */

/* Skip to main content link */
.skip-link {
    position: absolute;
    top: -40px;
    left: 6px;
    background: #000;
    color: #fff;
    padding: 8px;
    text-decoration: none;
    z-index: 10000;
    border-radius: 0 0 4px 4px;
    font-weight: bold;
    transition: top 0.3s;
}

.skip-link:focus {
    top: 0;
    outline: 2px solid #fff;
    outline-offset: 2px;
}

/* Enhanced focus indicators for keyboard navigation */
a:focus,
button:focus,
input:focus,
select:focus,
textarea:focus,
.btn:focus,
.form-control:focus,
.form-select:focus {
    outline: 2px solid #0066cc !important;
    outline-offset: 2px !important;
    box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.3) !important;
}

/* Focus indicators on dark backgrounds */
.bg-dark a:focus,
.bg-dark button:focus,
.bg-primary a:focus,
.bg-primary button:focus {
    outline-color: #ffffff !important;
    box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.3) !important;
}

/* High contrast focus for critical actions */
.btn-danger:focus,
.btn-warning:focus {
    outline: 3px solid #ffff00 !important;
    outline-offset: 2px !important;
    box-shadow: 0 0 0 3px rgba(255, 255, 0, 0.5) !important;
}

/* Screen reader only content */
.sr-only {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    white-space: nowrap !important;
    border: 0 !important;
}

/* Screen reader only content that becomes visible on focus */
.sr-only-focusable:focus {
    position: static !important;
    width: auto !important;
    height: auto !important;
    padding: 0.25rem 0.5rem !important;
    margin: 0 !important;
    overflow: visible !important;
    clip: auto !important;
    white-space: normal !important;
    background-color: #000 !important;
    color: #fff !important;
    border-radius: 0.25rem !important;
    z-index: 1000 !important;
}

/* Form field error associations */
.field-validation-error {
    color: #dc3545;
    font-size: 0.875rem;
    margin-top: 0.25rem;
    display: block;
}

.input-validation-error {
    border-color: #dc3545 !important;
    box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25) !important;
}

/* ARIA live regions for dynamic content */
.aria-live-polite {
    position: absolute;
    left: -10000px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* Enhanced dropdown accessibility */
.dropdown-toggle[aria-expanded="true"] {
    background-color: rgba(0, 123, 255, 0.1) !important;
    border-color: #007bff !important;
}

/* Form field aria-invalid styling */
input[aria-invalid="true"],
select[aria-invalid="true"],
textarea[aria-invalid="true"] {
    border-color: #dc3545 !important;
    box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25) !important;
}

/* Required field indicators */
.form-label .text-danger {
    font-weight: bold;
    margin-left: 0.25rem;
}

/* Improved help text visibility */
.form-text {
    color: #6c757d;
    font-size: 0.875rem;
    line-height: 1.4;
    margin-top: 0.375rem;
}

/* Enhanced keyboard navigation indicators */
[tabindex]:focus,
[tabindex="0"]:focus {
    outline: 2px solid #007bff !important;
    outline-offset: 2px !important;
    box-shadow: 0 0 0 4px rgba(0, 123, 255, 0.25) !important;
}

/* Form field error state improvements */
.is-invalid {
    border-color: #dc3545 !important;
    box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25) !important;
}

.invalid-feedback {
    font-weight: 500;
    font-size: 0.875rem;
}

/* Advanced validation styling — :not(:empty) so an empty validation
   summary (no errors yet) doesn't render a stray red marker bar */
.text-danger[role="alert"]:not(:empty) {
    background-color: rgba(220, 53, 69, 0.1);
    border-left: 3px solid #dc3545;
    padding: 0.25rem 0.5rem;
    border-radius: 0.25rem;
    font-weight: 500;
    margin-top: 0.25rem;
}

/* Enhanced alert accessibility */
.alert {
    border-left: 4px solid;
    font-weight: 500;
}

.alert-success { border-left-color: #28a745; }
.alert-info { border-left-color: #17a2b8; }
.alert-warning { border-left-color: #ffc107; }
.alert-danger { border-left-color: #dc3545; }

/* Improved modal accessibility */
.modal-header .btn-close:focus {
    outline: 2px solid #fff !important;
    outline-offset: 2px !important;
    box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.5) !important;
}

/* Better dropdown accessibility */
.dropdown-item:focus {
    background-color: #007bff !important;
    color: #fff !important;
}

/* Improved form control accessibility */
.form-control:focus,
.form-select:focus {
    border-color: #007bff !important;
    box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25) !important;
}

/*
   CHECKBOX AND RADIO BORDERS, contrast-corrected.

   Bootstrap 5 draws these at rgba(0, 0, 0, .25), which is about 1.7:1 against a white card and
   fails the WCAG 2.1 AA minimum of 3:1 for the boundary of a user interface component. It is the
   kind of failure that passes every automated check aimed at TEXT contrast and looks fine to
   whoever chose the palette on a good monitor. Reported by Mike, 2026-08-21, on the opt-in page:
   "I can barely make out the outline of the checkbox we are asking the parent to click."

   #6c757d is 4.7:1 on white, clearing the bar for a boundary with room to spare. Applied to every
   checkbox in the app rather than only the page it was noticed on, because the same faint outline
   is on every staff screen.
*/
.form-check-input {
    border-color: #6c757d;
}

/* A disabled control may look disabled, but it still has to be FINDABLE. The public opt-in page
   renders its checkbox disabled until a family arrives with their own link, and that is exactly the
   state a carrier reviewing the campaign is looking at. */
.form-check-input:disabled {
    border-color: #7a828a;
    opacity: 1;
    background-color: #f1f3f5;
}

/* Bootstrap mutes the LABEL of a disabled control too. On an ordinary form that is right; on the
   opt-in page the label IS the consent statement and the disclosure a reviewer came to read, so
   greying it out hides the point of the page. */
.form-check-input:disabled ~ .form-check-label {
    opacity: 1;
    color: inherit;
}

/*
   THE OPT-IN CHECKBOX, deliberately oversized.

   This is the only decision the page exists to collect, it is made mostly on a phone, and it is
   made by parents of school-age children who are not all twenty-five. A default 1rem box with a
   hairline border is not adequate for that job even once its contrast is fixed above, so this one
   is larger, thicker, and sits on white rather than on the page's tinted background.
*/
.consent-check .form-check-input {
    width: 1.4rem;
    height: 1.4rem;
    margin-top: 0.15rem;
    border-width: 2px;
    background-color: #fff;
}

.consent-check .form-check-label {
    padding-left: 0.35rem;
    line-height: 1.5;
}

.form-check-input:focus {
    box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25) !important;
    outline: 2px solid #007bff !important;
    outline-offset: 2px !important;
}

/* Enhanced pagination accessibility */
.page-link:focus {
    outline: 2px solid #007bff !important;
    outline-offset: 2px !important;
    z-index: 3 !important;
}

/* Improved card accessibility */
.card:focus-within {
    outline: 2px solid #007bff;
    outline-offset: 2px;
}

/* Better status indicator accessibility */
.badge {
    font-weight: 600;
    border: 1px solid transparent;
}

/* Enhanced breadcrumb accessibility */
.breadcrumb-item + .breadcrumb-item::before {
    color: #6c757d;
    font-weight: bold;
}

.breadcrumb-item.active {
    font-weight: 600;
}

/* Improved spacing for touch targets (44px minimum per WCAG) */
.btn {
    min-height: 44px;
    padding: 0.75rem 1rem;
}

.btn-sm {
    min-height: 36px;
    padding: 0.5rem 0.75rem;
}

/* Reset touch-target overrides for buttons inside the navbar */
.navbar .btn.nav-link {
    min-height: auto;
    padding: 0.5rem 1rem;
}

/* High contrast mode support */
@media (prefers-contrast: high) {
    .btn {
        border-width: 2px !important;
        font-weight: bold !important;
    }

    .nav-link:focus,
    .btn:focus {
        outline: 3px solid #ffff00 !important;
        outline-offset: 2px !important;
    }

    .table th,
    .table td {
        border-width: 2px !important;
        border-color: #000 !important;
    }
}

/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
    *,
    ::before,
    ::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
    /* Loading spinners convey live status, not decoration. A frozen spinner reads
       as "dead," so keep them turning even under reduced motion (slower, per
       Bootstrap's own reduced-motion convention) so an in-progress import or save
       never looks stalled to a reduced-motion user. */
    .spinner-border,
    .spinner-grow {
        animation-duration: 1s !important;
        animation-iteration-count: infinite !important;
    }
}

/* WCAG AA Color Contrast Fixes */
/* Override Bootstrap's text-muted and text-secondary for better contrast (4.5:1 minimum) */
.text-muted {
    color: #595959 !important; /* Contrast ratio ~7:1 on white */
}

.text-secondary {
    color: #545454 !important; /* Contrast ratio ~7.5:1 on white */
}

/* Bootstrap's default .text-warning (#ffc107, a light amber) fails contrast against a
   light background -- ~1.6:1 on white, far below the 4.5:1 AA minimum for normal text.
   Darken to an amber that still reads as "warning" but is actually legible. Contrast
   ratio of #8a5a00 is ~5.9:1 on white and ~5.6:1 on the app's lightest card background
   (#f8f9fa), both above the 4.5:1 AA floor. */
.text-warning {
    color: #8a5a00 !important; /* Contrast ratio ~5.9:1 on white */
}

/* Bootstrap's .btn-outline-warning renders light-amber text and border on a light
   background, the same ~1.6:1 failure as .text-warning above, so the button label is
   unreadable at rest. Darken the resting text/border to the same legible amber. The
   solid hover/focus/active state (dark text on an amber fill) already passes AA, so it
   is left as Bootstrap's default. */
.btn-outline-warning {
    color: #8a5a00 !important;
    border-color: #8a5a00 !important;
}

.btn-outline-warning:hover,
.btn-outline-warning:focus,
.btn-outline-warning:active {
    color: #000 !important;
    background-color: #ffc107 !important;
    border-color: #ffc107 !important;
}

/* Ensure small text in cards has sufficient contrast */
.card .text-muted,
.card-body .text-muted {
    color: #595959 !important;
}

/* #endregion ADA Compliance Styles */


/* #region Top Button */

#top-button {
    display: none;
    position: fixed;
    bottom: 20px;
    right: 30px;
    z-index: 99;
    font-size: 18px;
    border: none;
    background-color: transparent;
    color: white;
    cursor: pointer;
    padding: 8px 24px;
    border-radius: 4px;
}

    #top-button:hover {
        background-color: #4A4B4C; 
    }

    #top-button:focus {
        outline: 2px solid #007bff;
        outline-offset: 2px;
    }

    #top-button:active {
        background-color: transparent;
    }

/* #endregion Top Button */


/* #region Hide Flashing Cursor */

/* Hide flashing cursor on non-input elements */
.container, .card, p, h1, h2, h3, h4, h5, h6, div, span {
    caret-color: transparent;
}

/* Restore cursor in editable fields, INCLUDING descendants of a contenteditable
   region. Quill puts its text in child <p> elements inside .ql-editor; without the
   descendant selector, the rule above (p { caret-color: transparent }) wins on
   exactly those <p> and the typing caret is invisible (on touch only the OS caret
   handle shows). */
input, textarea, select, [contenteditable="true"], [contenteditable="true"] * {
    caret-color: auto;
}

/* #endregion Hide Flashing Cursor */


/* #region Mobile overflow guard */

@media (max-width: 768px) {
    /* Prevent horizontal overflow on mobile */
    .container, .card {
        overflow-x: hidden;
    }
}

/* #endregion Mobile overflow guard */



/* ── Horizontally-scrollable table wrapper ───────────────────────────────
   Wrap a wide <table> in <div class="table-scroll-x" data-drag-scroll> so
   that on narrow viewports it scrolls sideways instead of clipping at the
   viewport edge or squishing columns. The data-drag-scroll attribute
   (handled by drag-scroll.js) adds click-and-drag panning with a grab
   cursor. */
.table-scroll-x { overflow-x: auto; max-width: 100%; cursor: grab; }
.table-scroll-x.drag-scrolling { cursor: grabbing; user-select: none; }
.table-scroll-x.drag-scrolling * { cursor: grabbing !important; }
.table-scroll-x > table { min-width: 600px; }


/* ── Sticky thead for Bootstrap tables ───────────────────────────────────
   Column headers stay visible at the top of the viewport as the page
   scrolls down through a long table. Pure CSS, no JS.

   Tradeoff: any ancestor with `overflow != visible` (e.g., the
   `.table-scroll-x` wrapper above) creates a sticky-containing block
   that traps the thead inside it — sticky still "works" but pins to
   the wrapper's top, not the viewport. Tables wrapped in
   `.table-scroll-x` therefore won't get the page-scroll sticky effect;
   that's an accepted tradeoff for keeping horizontal scroll intact on
   narrow viewports. Pages that need both can address it case-by-case. */
.table > thead > tr > th {
    position: sticky;
    top: 0;
    z-index: 2;
    /* Plain thead with no Bootstrap variant: white bg so rows don't
       bleed through during scroll. */
    background-color: #fff;
    /* Inset shadow draws the bottom hairline on top of cell content,
       so it stays visible while rows scroll under the sticky row.
       A normal border-bottom moves with the cell, which can look
       like the line is sliding around as rows scroll past. */
    box-shadow: inset 0 -1px 0 #dee2e6;
}
.table > thead.table-dark > tr > th {
    background-color: #212529;
}
.table > thead.table-light > tr > th {
    background-color: #f8f9fa;
}

/* ------------------------------------------------------------------
   Account management (Manage area) tab strip
   ------------------------------------------------------------------
   Moved out of an inline <style> in
   Areas/Identity/Pages/Account/Manage/_Layout.cshtml per a11y triage
   ACC-T01. The active tab pairs the Bootstrap success colour with a
   thicker bottom border (a non-colour cue) so the active state is
   perceivable for users who cannot distinguish the green/grey contrast
   (a11y triage ACC-I02). Scoped to the Manage area's nav wrappers so
   it does not leak into other nav-tabs/nav-pills usage app-wide.
*/
/* Tab-button visual: give every tab a clear button-shaped border and
   slight gray fill so the strip reads as a tabbed container, not as
   floating text labels. Active tab uses a prominent green top accent
   (the ACC-I02 non-colour cue) and a white bottom that merges into the
   white card-body content panel below. */
nav[aria-label="Account management sections"] .nav-tabs .nav-link {
    font-weight: 500;
    color: #495057;
    background-color: #f8f9fa;
    border: 1px solid #dee2e6;
    margin-right: 2px;
}
nav[aria-label="Account management sections"] .nav-tabs .nav-link:hover {
    background-color: #e9ecef;
    border-color: #dee2e6;
}
nav[aria-label="Account management sections"] .nav-tabs .nav-link.active {
    font-weight: 700;
    color: var(--bs-success);
    background-color: #fff;
    /* Prominent 3px green top border = the ACC-I02 non-colour cue.
       Right/left stay neutral; bottom is white so the active tab
       visually attaches to the card-body content panel below. */
    border-color: var(--bs-success) #dee2e6 #fff;
    border-top-width: 3px;
}
/* Force the entire card (header + body) to white so the active tab's
   white bottom border merges seamlessly into the content panel below.
   Without this, the card-body inherited the page's pale blue background
   and the active tab appeared to "float" against a different colour. */
.card:has(nav[aria-label="Account management sections"]),
.card:has(nav[aria-label="Account management sections"]) .card-header,
.card:has(nav[aria-label="Account management sections"]) .card-body {
    background-color: #fff;
}

/* The Manage layout's "Delete" tab is a real <button> (a11y triage
   ACC-C02 — destructive action needs button semantics, not anchor). Strip
   browser button chrome so it visually matches the sibling anchor tabs. */
.manage-nav-delete-btn {
    background: transparent;
    border: 1px solid transparent;
}

/* Sidebar pill nav (Account management _ManageNav partial): pair the
   coloured active pill with a left border so the active item is
   perceivable without colour. */
nav[aria-label="Account management sections"] .nav-pills .nav-link.active {
    border-left: 3px solid var(--bs-success);
}

/* Branded MIQ.confirm/alert modal header (see wwwroot/js/miq-modal.js).
   Uses the brand navy (--rc-navy) from the palette region at the foot of this
   file, with a literal fallback so the header stays on-brand if the var is
   ever missing. */
.miq-modal-header { background-color: var(--rc-navy, #003366); color: #fff; }
.miq-modal-header .btn-close { filter: invert(1) grayscale(100%) brightness(200%); }

/* #region Shared chrome (de-inlined from _PartialFooter / _PartialHeader /
   _Layout / _TopButton / Error). Values reproduced verbatim for visual parity. */

/* Footer */
.footer-nav-col { max-width: 160px; }
.footer-members-menu { margin-top: 0; width: 180px; }
.footer-copyright-bar { background-color: #1a1a2e; }
.footer-copyright-text { font-size: 12px; }
.footer-logo { height: 20px; }

/* Header */
.header-logo { height: 36px; }
.header-dropdown-menu { width: 250px; }
/* .header-tools-submenu went with the Admin Tools sub-menu it sized. It was the only
   nested dropdown in the header, and its screens are Site Tools cards now. */

/* Layout: off-screen element kept in the DOM for the social-share crawler but
   pulled out of view and the interaction tree. */
.offscreen-hint {
    position: absolute;
    left: -9999px;
    top: -9999px;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

/* Error page */
.error-page { max-width: 640px; }
.error-title { color: var(--rc-navy, #003366); }

/* Reusable brand-navy heading color (docs guides, error page, etc.). */
.text-brand-navy { color: var(--rc-navy, #003366); }

/* Admin-tool de-inline utilities (shared across the admin/tool views). */
.pre-wrap-inherit { white-space: pre-wrap; word-break: break-word; font-family: inherit; }
.fs-085 { font-size: 0.85rem !important; }
.mw-240 { max-width: 240px; }
/* Inline-style de-inlining (CSP style-src hardening, 2026-06-25). Static
   values lifted verbatim from former style="" attributes so style-src can
   drop 'unsafe-inline'. Reuse Bootstrap .fw-semibold/.w-auto/.d-inline/
   .d-inline-block/.d-none and the existing .card-header-brand where they fit. */
.min-vh-50 { min-height: 50vh; }
.min-vh-80 { min-height: 80vh; }
.mw-400 { max-width: 400px; }
.mw-720 { max-width: 720px; }
.w-90 { width: 90%; }
.w-100px { width: 100px; }
.w-160px { width: 160px; }
.fs-0875 { font-size: 0.875rem; }
.fs-110 { font-size: 1.1rem; }
.fs-140 { font-size: 1.4rem; }
.fs-090em { font-size: 0.9em; }
.bg-white-80 { background-color: rgba(255, 255, 255, 0.8); }
.access-denied-card { background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); }
.dash-z2 { z-index: 2; }
.fs-2rem { font-size: 2rem; }
.ch-msg-row { text-align: center; color: #888; padding: 20px; }
.ch-msg-row-error { text-align: center; color: #7b1a12; padding: 20px; }
.ch-form-label { font-size: 12px; font-weight: 600; }
.ch-form-label-block { font-size: 12px; font-weight: 600; margin-top: 6px; display: block; }

/* Docs role guides */
.docs-page { max-width: 920px; }
.docs-page-wide { max-width: 1100px; }
.docs-switcher { font-size: 14px; }

/* Info / public content pages */
.page-narrow { max-width: 920px; }
.lead-narrow { max-width: 640px; margin-left: auto; margin-right: auto; }
.lead-prose { font-size: 1.05rem; line-height: 1.7; }
.card-header-brand { background-color: var(--rc-navy, #003366); color: #fff; }
.link-brand { color: var(--rc-link, #003d7a); }
.contact-textarea { resize: vertical; min-height: 150px; }
.cursor-pointer { cursor: pointer; }
/* Quill rich-text editors: a bold, high-contrast text caret so the typing
   position is obvious, especially on touch where the OS caret handle (the
   Android teardrop) otherwise dominates. White surface + text cursor too, so
   the editor reads as a normal text box regardless of its container's tint. */
.ql-editor { cursor: text; background: #fff; }
.ql-editor, .ql-editor * { caret-color: #003366; }   /* navy caret on the editor AND its child <p>/spans */
/* #endregion */

/* Leave-confirm dialog (back-button guard) */
.miq-leave-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1060;
    padding: 1rem;
}
.miq-leave-dialog {
    background: #fff;
    border-radius: 0.5rem;
    box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.3);
    max-width: 22rem;
    width: 100%;
    padding: 1.25rem 1.25rem 1rem;
}
.miq-leave-title { font-size: 1.15rem; margin: 0 0 0.5rem; }
.miq-leave-body { margin: 0 0 1rem; color: #444; }
.miq-leave-actions { display: flex; gap: 0.5rem; justify-content: flex-end; }


/* #region Brand palette + chrome (merged from the former colors.css, 2026-06-25) */
/* MoveableIQ palette: navy + gold, light neutral background, no background image. */

:root {
    --rc-navy: #003366;
    --rc-navy-dark: #002650;
    --rc-navy-light: #003d7a;
    --rc-gold: #D4AF37;
    --rc-gold-dark: #b8960e;
    /* v1-inspired cool blues: page is a light cornflower wash, cards sit on top a shade darker so
       they "lift" off the page (inverse of the usual dark-page/white-card pattern).

       MIKE, 2026-08-26: "the cards are the same color as the background. it is hard to distinguish
       a card other than by the faint border. the card needs a little more shading to stand out from
       the background, but not enough to cause readability problems."

       He was right, and it was measurable: card against page was 1.104:1, under the step an eye
       reliably reads as two surfaces, so the border was doing all the work alone. It is 1.275:1 now,
       reached from BOTH sides (the page lighter, the card a shade deeper) rather than by darkening
       the card alone, which is what protects the second half of the request.

       READABILITY WENT UP, NOT DOWN, and this is the part to check before touching these again.
       Darkening the card on its own would have taken muted text from 4.52:1 to 4.06:1, under the
       4.5:1 floor: the exact "readability problem" named above, arriving as a side effect of the
       fix. So --rc-muted moved with it and now computes 5.02:1, better than it has ever been.

       Every value on the new card, computed:
         body  #222     11.58:1      muted #5a5a5a   5.02:1
         navy  #003366   9.18:1      link  #003d7a   7.85:1

       KEPT IN STEP WITH THE VAULT ON PURPOSE. Both apps declare this same block, and they are one
       product to the person looking at them. Change one and change the other, or the two drift into
       different blues and the shared identity goes with them. */
    --rc-bg: #f2f7fd;
    --rc-card-bg: #cddeef;
    --rc-card-border: #93b0cb;
    --rc-card-footer-bg: #c3d5e9;
    --rc-text: #222;
    /* Secondary text ON the navy header fill. 8.19:1 against --rc-navy, computed, against a
       4.5:1 AA floor, and deliberately below the header's own white (12.61:1) so it still reads
       as secondary. A token rather than a literal because "muted, but on navy" is a real role
       that comes up again, and the next person must not reach for a grey that vanishes. */
    --rc-muted-on-navy: #c3d2e2;
    /* Moved with the card, see above: on the deeper card #666 would have fallen under 4.5:1. */
    --rc-muted: #5a5a5a;
    --rc-link: #003d7a;
    --rc-link-hover: #002650;
}

/* Body — light neutral, NO background image. */
body {
    background-color: var(--rc-bg);
    background-image: none;
    color: var(--rc-text);
}

/* App banner (sits above the navbar) */
.app-banner {
    background: linear-gradient(to bottom, var(--rc-navy-light) 0%, var(--rc-navy) 100%);
    color: #fff;
    position: relative;
    overflow: hidden;
}
.app-banner::after {
    content: "";
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 5px;
    background: linear-gradient(to right, var(--rc-gold), var(--rc-gold-dark));
}
.app-banner-inner {
    max-width: 1100px;
    margin: 0 auto;
    padding: 14px 24px 18px;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    row-gap: 4px;
}
.app-banner-eyebrow {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.78);
}
.app-banner-title {
    font-family: "Source Serif 4", Georgia, serif;
    font-size: clamp(20px, 4vw, 26px);
    font-weight: 600;
    line-height: 1.15;
    margin: 0;
    color: #fff;
}
.app-banner-sub {
    font-size: 13px;
    color: rgba(255, 255, 255, 0.78);
}
.app-banner-pill {
    margin-top: 6px;
    background: rgba(255, 255, 255, 0.12);
    border: 1px solid rgba(255, 255, 255, 0.25);
    border-radius: 8px;
    padding: 6px 14px;
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    font-family: "Source Sans 3", Arial, sans-serif;
}
.app-banner-pill-prefix {
    font-size: 11px;
    font-weight: 700;
    color: rgba(255, 255, 255, 0.55);
    text-transform: uppercase;
    letter-spacing: 1.4px;
}
.app-banner-pill-name {
    font-size: 13px;
    font-weight: 700;
    color: var(--rc-gold);
}
.app-banner-pill-version {
    font-size: 12px;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.55);
    letter-spacing: 0.5px;
    font-feature-settings: "tnum";
}

@media (max-width: 600px) {
    .app-banner-inner {
        row-gap: 6px;
    }
}

/* Cards — clean white surface with subtle navy accent on header */
/* ONE DEFINITION OF A RAISED SURFACE, wearing two names.

   Mike, 2026-08-26, after the card fill was deepened: "the cards and background being the same must
   be wider spread. this page suffers: /Accounts/Edit/227".

   He was right, and the reason is that .card was only ONE of the ways this app draws a panel. The
   others were a bare "border rounded", which has no fill at all and so is literally the page with a
   hairline on it, and "bg-light", which is Bootstrap's near-white #f8f9fa and computes 1.022:1
   against this app's page. Both were invisible, and lightening the page to lift the cards made the
   bg-light ones worse. Fixing .card alone fixed one spelling of the word.

   .surface is that concept named once. Anything that should read as a panel carries it and cannot
   drift from a card, because there is one rule here rather than a colour copied into each view.

   NOT applied to every bg-light. A badge and a card header also use it and want to stay light: a
   badge is a small light chip ON a surface, and .card-header.bg-light already has its own text rule
   below. Those are a different job from drawing a panel. */
.card,
.surface {
    border-radius: 6px;
    background: var(--rc-card-bg);
    border: 1px solid var(--rc-card-border);
    /* A SECOND CUE, and the only one that costs no contrast at all. The fill step above is what
       Mike asked for and does the work; a soft drop shadow adds the depth that says "this sits on
       top of the page" rather than "this is a differently tinted region of it". Navy-tinted rather
       than black so it reads as shade on a blue ground instead of grey dirt, and kept faint: it is
       reinforcement, not the carrier. */
    box-shadow: 0 1px 3px rgba(0, 51, 102, .10);
}
.card-header {
    border-radius: 6px 6px 0 0;
    background: var(--rc-navy);
    color: #fff;
}
/* Light-background card headers (Bootstrap bg-light or any *-subtle utility)
   override the default navy fill but NOT the white text, which leaves an
   unreadable white-on-light heading. Restore dark text whenever the header
   background is a light utility. Higher specificity than the rule above, so
   no !important is needed. */
.card-header.bg-light,
.card-header.bg-info,
.card-header.bg-warning,
.card-header[class*="-subtle"] {
    color: var(--rc-text);
}

/* MUTED TEXT IN A NAVY CARD HEADER. Ported from the Vault, where Mike hit it twice in one day,
   2026-08-27: "you have test I can't read on a dark blue background on the ownership card
   heading", and then on a card added hours later, "cant read the heading text on your new one
   either". Two reports, one rule, because it was never about either card.

   COMPUTED, not judged. The block above sets `.card .text-muted { color: #595959 }` to clear
   4.5:1 on a white card BODY, and a .card-header lives inside .card, so that grey lands on the
   navy fill: #595959 on #003366 is 1.80:1 against a 4.5:1 floor. It is not dim, it is gone.

   Nothing in Tracker triggered it yet, which is exactly why it is worth closing now: the trap is
   armed and the markup that springs it looks perfectly reasonable. Specificity (0,3,0) plus
   !important is what it takes to beat the (0,2,0) !important above.

   THE RELATIONSHIP IS THE GUARD, not the element: any muted span in any navy header is covered,
   including ones not written yet. The light-header case is re-darkened below, because a blanket
   light rule would invert exactly the headers the rule above exists to protect. */
.card .card-header .text-muted,
.card .card-header .text-secondary,
.card .card-header small {
    color: var(--rc-muted-on-navy) !important;
}

/* ...and back to dark for a header that is NOT navy. Same set as above, one level deeper. */
.card .card-header.bg-light .text-muted,
.card .card-header.bg-info .text-muted,
.card .card-header.bg-warning .text-muted,
.card .card-header[class*="-subtle"] .text-muted,
.card .card-header.bg-light .text-secondary,
.card .card-header.bg-info .text-secondary,
.card .card-header.bg-warning .text-secondary,
.card .card-header[class*="-subtle"] .text-secondary,
.card .card-header.bg-light small,
.card .card-header.bg-info small,
.card .card-header.bg-warning small,
.card .card-header[class*="-subtle"] small {
    color: #595959 !important;
}
.card-body {
    border-radius: 0;
    background: var(--rc-card-bg);
}
/* When the body is the first/last thing in the card (no header/footer), round
   the exposed corners so the fill doesn't square off past the card's border. */
.card-body:first-child {
    border-top-left-radius: 6px;
    border-top-right-radius: 6px;
}
.card-body:last-child {
    border-bottom-left-radius: 6px;
    border-bottom-right-radius: 6px;
}
.card-footer {
    border-radius: 0 0 6px 6px;
    background: var(--rc-card-footer-bg);
    color: var(--rc-text);
    border-top: 1px solid var(--rc-card-border);
}

/* Helper backgrounds used by existing partials. Keep the class names so we
   don't have to rename them throughout the project — just rebind the colors. */
.bg-card-header { background: var(--rc-navy) !important; color: #fff !important; }
.bg-card-body { background: var(--rc-card-bg) !important; color: var(--rc-text) !important; }
.bg-card-footer { background: var(--rc-navy) !important; color: #fff !important; }

/* Navbar — keep the dropdown menu's appearance Bootstrap-default but make
   sure the navbar itself reads as v1 navy.

   Role-coded variants: Manager gets a lifted navy, Admin gets a lifted
   wine/burgundy. Both at roughly the same lightness as the base navy
   so headers don't shout but the role is visible at a glance. Applied
   in _PartialHeader by adding the modifier class alongside
   bg-card-footer; compound-class specificity (no !important needed)
   lets the variant override the base. White text from the existing
   .navbar.bg-card-footer rule continues to apply. */
.navbar.bg-card-footer { background: var(--rc-navy) !important; }
.navbar.bg-card-footer.bg-card-footer-manager { background: #3070b3 !important; }
.navbar.bg-card-footer.bg-card-footer-admin   { background: #5c1a1a !important; }
.navbar.bg-card-footer .navbar-brand,
.navbar.bg-card-footer .nav-link,
.navbar.bg-card-footer .dropdown-item {
    color: #fff;
}
.navbar.bg-card-footer .dropdown-menu {
    background: #fff;
}
.navbar.bg-card-footer .dropdown-menu .dropdown-item {
    color: var(--rc-text);
}
.navbar.bg-card-footer .dropdown-menu .dropdown-item:hover {
    background: #f0f4f8;
    color: var(--rc-link);
}

/* Buttons */
.btn-primary {
    background-color: var(--rc-navy);
    border-color: var(--rc-navy);
}
.btn-primary:hover, .btn-primary:focus {
    background-color: var(--rc-navy-dark);
    border-color: var(--rc-navy-dark);
}
.btn-outline-primary {
    color: var(--rc-navy);
    border-color: var(--rc-navy);
}
.btn-outline-primary:hover, .btn-outline-primary:focus {
    background-color: var(--rc-navy);
    color: #fff;
}

/* AN OUTLINE-SECONDARY INSIDE A TINTED CARD BODY. Bootstrap draws it as #6c757d text on a
   transparent fill. On --rc-card-bg (#cddeef) that is 3.41:1, which fails AA, and it also reads as
   DISABLED, because muted grey with no fill is precisely Bootstrap's disabled language. Mike,
   2026-08-29, from the User's Guide: "these buttons blend with the card background making them
   look disabled." Both halves of that sentence were true and they had different causes.

   SCOPED TO .card-body ON PURPOSE. The same button on white is #6c757d on #ffffff, 4.69:1, which
   passes and is deliberately left alone. There are 55 of these across 40 views and only the ones
   sitting on a tinted card are broken, so fixing the RELATIONSHIP in one rule cannot miss an
   instance the way rewriting 55 class attributes would, and cannot damage the ones already fine.

   White fill so it stops blending into the card, house navy for text and border:
     text   #003366 on #ffffff  12.61:1
     border #003366 on #cddeef   9.18:1  (past the 3:1 a non-text boundary needs)
   Computed, not eyeballed, in the same spirit as the navy-on-navy note further down. */
.card-body .btn-outline-secondary {
    background-color: #fff;
    color: var(--rc-navy);
    border-color: var(--rc-navy);
}
.card-body .btn-outline-secondary:hover,
.card-body .btn-outline-secondary:focus {
    background-color: var(--rc-navy);
    border-color: var(--rc-navy);
    color: #fff;
}

/* Links — but don't override Bootstrap button colors. The :visited variant of
   the bare-element selector has higher specificity than .btn / .btn-primary,
   so without :not(.btn) the Staff Sign In button (an <a class="btn btn-primary">)
   went unreadable navy-on-navy after the user had visited /Identity/Account/Login. */
a:not(.btn), a:not(.btn):visited { color: var(--rc-link); }
a:not(.btn):hover { color: var(--rc-link-hover); }

/* Focused Elements */
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
    box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

/* #region Pagination */
.pagination .page-link {
    background-color: var(--pagination-bg, #FFFFFF);
    border-color: var(--pagination-border, #808080);
    color: var(--pagination-text, #424143);
}

.pagination .page-item.active .page-link {
    background-color: var(--pagination-active-bg, #808080);
    border-color: var(--pagination-active-border, #808080);
    color: var(--pagination-active-text, #FFFFFF);
}

.pagination .page-link:hover {
    background-color: var(--pagination-hover-bg, #D3D3D3);
    color: var(--pagination-hover-text, #424143);
}

.pagination .page-item.disabled .page-link {
    background-color: var(--pagination-disabled-bg, #f8f9fa);
    border-color: var(--pagination-disabled-border, #808080);
    color: var(--pagination-disabled-text, #6c757d);
}
/* #endregion Pagination */

/* Footer */
.footer-nav-link {
    color: #ffffffcc;
    text-decoration: none;
    transition: color 0.2s, background-color 0.2s;
    padding: 2px 6px;
}

    .footer-nav-link:hover {
        color: #fff;
        background-color: rgba(255, 255, 255, 0.1);
    }

.footer-fs {
    font-size: 0.85rem;
}
/* End Footer */
/* #endregion Brand palette + chrome */

/* ------------------------------------------------------------------
   Account management tab strip: out of the card, 2026-08-21
   ------------------------------------------------------------------
   The strip used to live in a .card-header inside a .card. Between them
   they spent about 2rem of horizontal padding before the first tab was
   drawn, which on a phone pushed the strip onto two rows while the first
   row sat half empty. It also made the strip read as a panel containing
   labels rather than as tabs.

   This app can show six tabs (External logins appears when Google is
   configured), so the nowrap-and-scroll below is doing more work here
   than in the Vault. Reported by Mike from a phone.
*/
.manage-tabs-strip {
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
.manage-tabs-strip::-webkit-scrollbar { display: none; }

.manage-tabs-strip .nav-tabs {
    border-bottom: 1px solid #dee2e6;
    flex-wrap: nowrap;
    margin-bottom: 0;
}
.manage-tabs-strip .nav-tabs .nav-item { white-space: nowrap; }

/* Squared and joined to the strip above, so the two read as one panel. */
.manage-tabs-panel {
    border-top-left-radius: 0;
    border-top-right-radius: 0;
    border-top: 0;
}

/* ── Passkey list ──────────────────────────────────────────────────────────
   Lives here rather than in a @section Styles on the page: the Manage layout
   forwards only "Scripts" to the outer layout, so a Styles section defined on
   a Manage page is never rendered and Razor throws for defining it.

   The name field was the width of its column, which on a desktop is most of
   the table for a value that is usually two or three words. Capped, and the
   cap is lifted on phones where the field owns its own row anyway. */
.pk-col-name {
    width: 45%;
}

.pk-name-input {
    max-width: 22rem;
}

/* Phone: each passkey is three rows in its own tbody. The rule separates
   ENTRIES rather than rows, so the eye groups the name, the dates and the
   Remove button as one key instead of three unrelated lines. */
.pk-stacked .pk-entry + .pk-entry > tr:first-child > td {
    border-top: 2px solid var(--bs-border-color, #dee2e6);
}

.pk-stacked td {
    padding-top: 0.4rem;
    padding-bottom: 0.4rem;
}

/* The field owns a full row here, so the desktop cap would only waste it. */
@media (max-width: 767.98px) {
    .pk-name-input {
        max-width: none;
    }
}

/* ---------------------------------------------------------------------------
   A WIDE TABLE STACKS INTO ONE CARD PER ROW ON A PHONE.

   PORTED FROM THE VAULT, which has run this on real phones since 2026-08-13.
   Mike, 2026-08-27, sending a screenshot of the Vault's sheet: "wHEN i WAS
   TALKING ABOUT WRAPPING THAT TABLE, i MEANT LIKE WE DID IN THE vAULT."

   Wrapping the CELLS is not the same thing and does not solve it. Five columns
   squeezed into a phone gives every one of them about seventy pixels, so the
   text wraps into unreadable ribbons and the table is still miserable. Stacked,
   each row becomes a card and each value gets the full width with its column
   name above it.

   THE BREAKPOINT IS 991.98px AND THAT NUMBER WAS LEARNED THE HARD WAY. The
   Vault first shipped this at 575.98px; plenty of phones report a CSS width
   above that, so the stacked layout never engaged and the operator got the full
   desktop table scrolled sideways with the left of every row cut off. Anything
   narrower than a landscape tablet stacks.

   Put here rather than in a view so a second table opting in cannot drift from
   the first. Opt in with class="stacked-table" and give every <td> a
   data-label naming its column. --------------------------------------------- */
@media (max-width: 991.98px) {

    /* The table stops being a table: each row is a block, each cell says which
       column it came from, and nothing scrolls horizontally. */
    .stacked-table thead { display: none; }

    /* And its wrapper stops offering to scroll. Bootstrap's .table-responsive sets overflow-x
       auto at every width, so a single stubborn child could still drag the page sideways even
       once the cells were stacked, which is the failure this whole block exists to prevent. */
    .table-responsive:has(> .stacked-table),
    .table-scroll-x:has(> .stacked-table) {
        overflow-x: visible;
    }

    .table-scroll-x > .stacked-table { min-width: 0; }

    .stacked-table,
    .stacked-table tbody,
    .stacked-table td { display: block; width: 100%; }

    /* THE ROW IS A TWO COLUMN GRID, and by default nothing uses the second column: every cell spans
       the full width, which is the stacked layout as it has always behaved. The grid is here so a
       cell CAN opt into a half, with .stacked-pair, and two consecutive halves then sit on one line.

       Mike, 2026-08-28, on the Users card: "First and last date can probably be on the same line."
       Two short dates each taking a whole line made the card taller than the screen for no benefit,
       and a date is narrow enough that two fit a phone comfortably.

       Pairs form in DOM order, so only cells that are already neighbours in the table can share a
       line. That is deliberate: reordering on a phone would mean the card and the table disagree
       about what order the columns are in, and the label above each value is doing enough work
       already. */
    .stacked-table tr {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .stacked-table td { grid-column: 1 / -1; }

    /* Half a row. Two of these next to each other pair up; one on its own simply takes the left
       half, which is a fine resting state rather than a bug. */
    .stacked-table td.stacked-pair { grid-column: auto; }

    /* NOTHING IN A STACKED CARD IS CENTRED OR RIGHT ALIGNED. The alignment classes on these cells
       are COLUMN alignment: they line a badge up under its heading in the desktop table. Stacked,
       there is no column to line up with, and the result was a card where three labels sat centred
       and one sat right while the rest read down the left edge. Reported with a screenshot,
       2026-08-28.

       Bootstrap's .text-center and .text-end are !important, so overriding them takes !important
       too. Exactly the same situation as .text-nowrap further down this block, and it was simply
       not thought of when that one was written. */
    .stacked-table td,
    .stacked-table td.text-center,
    .stacked-table td.text-end {
        text-align: left !important;
    }

    .stacked-table tr {
        border: 1px solid var(--rc-card-border, #dee2e6);
        border-radius: .5rem;
        margin-bottom: .75rem;
        padding: .25rem 0;
    }

    .stacked-table td {
        border: 0;
        padding: .4rem .75rem;
        overflow-wrap: anywhere;
        white-space: normal;
    }

    /* THE COLUMN NAME IS A HEADING, so it is allowed to look like one. On a stacked row it is the
       only thing telling you which value you are looking at.

       Navy rather than the usual muted grey, and that is contrast rather than taste: computed on
       this app's own card fill #cddeef, --bs-secondary-color #6c757d is 3.41:1, under the 4.5:1
       floor, while navy is 9.18:1. Identical numbers to the Vault, because the two apps share the
       palette. Weight and size carry the emphasis; the colour is there to be readable. */
    .stacked-table td[data-label]::before {
        content: attr(data-label);
        display: block;
        font-size: .8rem;
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: .04em;
        color: var(--rc-navy);
        margin-bottom: .2rem;
    }

    /* NOTHING IN A STACKED ROW MAY REFUSE TO WRAP. The Vault found .text-nowrap still winning on a
       cell whose own rule said normal, on a live phone. Rather than hunt the next selector and
       then the one after that, everything inside a stacked cell wraps. Bootstrap's .text-nowrap
       is !important, so overriding it takes !important too. */
    .stacked-table td,
    .stacked-table td label,
    .stacked-table td span,
    .stacked-table td code,
    .stacked-table td small,
    .stacked-table td .btn,
    .stacked-table .text-nowrap {
        white-space: normal !important;
    }

    /* An inline max-width set for the desktop table would strangle a stacked cell. */
    .stacked-table td[style*="max-width"] { max-width: none !important; }

    .stacked-table input[type="text"],
    .stacked-table input[type="password"],
    .stacked-table textarea { min-width: 0 !important; width: 100%; }
}
