/* GLOBAL STYLES
-------------------------------------------------- */
/* Padding below the footer and lighter body text */

body {
    padding-bottom: 40px;
    background-color: #FFFFFF;

    /* Blue accent palette */
    --accent-blue: #2563EB;
    --accent-blue-light: #EFF6FF;
    --accent-blue-border: #BFDBFE;
    --accent-blue-hover: rgba(37, 99, 235, 0.08);
    --accent-blue-active: rgba(37, 99, 235, 0.12);

    /* Posture readiness palette — single CSS source of truth, mirroring
       staffing.constants.LEVEL_COLORS / LEVEL_TEXT_COLORS (the Python source).
       Every posture surface (the duty grid .posture-level-*, the crew board
       wash .cs-cell-l*, and the officer console trend strip pL*) derives from
       these tokens so the levels read the same colour everywhere.
       (--posture-0 is the grid's light baseline-cell grey; LEVEL_COLORS[0]
       #6c757d is the heavier value used only for borders/strips in Python.) */
    --posture-0: #e9ecef;  --posture-text-0: #495057;
    --posture-1: #d4edda;  --posture-text-1: #155724;
    --posture-2: #a3d9a5;  --posture-text-2: #155724;
    --posture-3: #28a745;  --posture-text-3: #ffffff;
    --posture-4: #1e7e34;  --posture-text-4: #ffffff;
    --posture-5: #155724;  --posture-text-5: #ffffff;
    /* Pale washes (same hue family) for chip-bearing crew cells — kept light so
       the role-coloured initials chips stay legible on top. */
    --posture-wash-0: #f1f3f5;
    --posture-wash-1: #edf7ee;
    --posture-wash-2: #e3f3e5;
    --posture-wash-3: #d9efdb;
    --posture-wash-4: #cfead2;
    --posture-wash-5: #c5e6c9;
}

/* Dark-mode values for the body-scoped tokens above (Bootstrap 5.3 color modes;
   data-bs-theme="dark" is set on <html> by the /private/ theme toggle in
   private_base.html — public pages never set it, so these are inert there).
   The accent SURFACES go deep desaturated navy-blue; the accent itself lightens
   so accent text/icons/outlines keep AA-ish contrast on dark surfaces.
   The saturated --posture-0..5 badge colors are intentionally NOT overridden —
   they stay identical in both modes, mirroring staffing.constants.LEVEL_COLORS.
   Only the pale chip-cell washes get dark equivalents, keeping the same ordinal
   neutral->green readiness ramp so Grid<->Crew still reads the same per level. */
[data-bs-theme="dark"] body {
    --accent-blue: #6EA8FE;
    --accent-blue-light: #1C2A44;
    --accent-blue-border: #35507F;
    --accent-blue-hover: rgba(110, 168, 254, 0.12);
    --accent-blue-active: rgba(110, 168, 254, 0.20);

    --posture-wash-0: #222933;
    --posture-wash-1: #1F2E24;
    --posture-wash-2: #22362A;
    --posture-wash-3: #263F2F;
    --posture-wash-4: #2A4834;
    --posture-wash-5: #2E5139;
}

/* CUSTOMIZE THE NAVBAR
-------------------------------------------------- */

.navbar-wrapper > .container {
    padding-right: 0;
    padding-left: 0;
}
.navbar-wrapper .navbar {
    padding-right: 15px;
    padding-left: 15px;
}
.navbar-wrapper .navbar .container {
    width: auto;
}

/* Private navbar (navy) */
.navbar-private {
    background-color: #1A2332;
    border-bottom: 3px solid #CC3333;
}

.navbar-private .navbar-brand {
    color: #F1F5F9;
}


.navbar-private .navbar-text {
    color: #94A3B8;
}

.navbar-private .navbar-nav .nav-link {
    color: #F1F5F9;
}

/* ux-048: prevent page-wide horizontal scroll (clip, not hidden, to preserve sticky navbar) */
body { overflow-x: clip; }
.navbar-private .container-fluid { flex-wrap: nowrap; }
.navbar-private .navbar-brand { min-width: 0; overflow: hidden; }

/* Public navbar (dark red/inverse) */
.navbar-dark {
    background-color: #1A2332;
    border-color: #1A2332;
}
.navbar-dark .navbar-brand {
    color: #F1F5F9;
}
.navbar-dark .navbar-text {
    color: #F1F5F9;
}
.navbar-dark .navbar-nav .nav-link {
    color: #F1F5F9;
}

/* Public pages: offset for fixed-top navbar */
body.public-page {
    padding-top: 56px;
}

/* SIDEBAR NAVIGATION
-------------------------------------------------- */

.sidebar {
    width: 260px;
    background-color: #E2E8F0;
    border-right: 1px solid #CBD5E1;
    overflow-y: auto;
}

.sidebar .offcanvas-header {
    background-color: #1A2332;
    color: #F1F5F9;
}

.sidebar .nav-sublabel {
    font-size: 0.65rem;
    font-weight: 600;
    color: #64748B;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 0.5rem 1rem 0.125rem;
}

.sidebar .sidebar-search {
    padding: 0.5rem 0.75rem;
    background-color: #F8FAFC;
    border-bottom: 1px solid #CBD5E1;
    position: sticky;
    top: 0;
    z-index: 5;
}

.sidebar .sidebar-search input {
    width: 100%;
    padding: 0.3rem 0.5rem 0.3rem 1.75rem;
    border: 1px solid #CBD5E1;
    border-radius: 4px;
    font-size: 0.85rem;
    background: white url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394A3B8' stroke-width='2.5'><circle cx='11' cy='11' r='7'/><path d='M21 21l-4.35-4.35'/></svg>") 0.5rem center no-repeat;
}

.sidebar .sidebar-search input:focus {
    outline: none;
    border-color: var(--accent-blue, #3B82F6);
}

/* Keep a visible focus ring for keyboard/switch users (the :focus rule above
   suppresses it for all focus, including click). */
.sidebar .sidebar-search input:focus-visible {
    outline: 2px solid var(--accent-blue, #3B82F6);
    outline-offset: 1px;
}

/* Hidden by search filter */
.sidebar .nav-hidden { display: none !important; }

.sidebar .accordion-body {
    padding: 0;
}

.sidebar .accordion-body a {
    display: block;
    padding: 0.35rem 1rem 0.35rem 2rem;
    font-size: 0.85rem;
    color: #555;
    text-decoration: none;
}

.sidebar .accordion-body a:hover {
    background-color: var(--accent-blue-hover);
    color: #1A2332;
}

.sidebar .nav-link-single {
    display: block;
    padding: 0.5rem 1rem;
    font-size: 0.9rem;
    color: #333;
    text-decoration: none;
}

.sidebar .nav-link-single:hover {
    background-color: var(--accent-blue-hover);
    color: #1A2332;
}

.sidebar .accordion-body a.active,
.sidebar .nav-link-single.active {
    background-color: var(--accent-blue-active);
    color: #1A2332;
    font-weight: 600;
    border-left: 3px solid var(--accent-blue);
    padding-left: calc(2rem - 3px);
}

.sidebar .nav-link-single.active {
    padding-left: calc(1rem - 3px);
}

/* Registry-driven area rows (private/navigation.py). Reuses .nav-link-single
   colors/tap-targets; these rules only add the row layout. */
.sidebar .nav-area-row {
    display: flex;
    align-items: center;
    gap: 0.45rem;
}

.sidebar .nav-area-row .nav-area-chevron {
    margin-left: auto;
}

/* Row of the area containing the current page (a child link carries the
   strong .active bar; the row itself just goes semibold). */
.sidebar .nav-area-row.area-active {
    font-weight: 600;
}

.sidebar .nav-area-children {
    padding-bottom: 0.25rem;
}

.sidebar .nav-footer-divider {
    border-top: 1px solid #CBD5E1;
    margin: 0.5rem 0 0.25rem;
    opacity: 1;
}

/* Pinned rows: link fills the row, unpin button sits at the right edge. */
.sidebar .nav-pin-row {
    display: flex;
    align-items: stretch;
}

.sidebar .nav-pin-row .nav-link-single {
    flex: 1 1 auto;
    min-width: 0;
}

.nav-pin-btn {
    background: none;
    border: 0;
    padding: 0.5rem 0.75rem;
    min-width: var(--tap-min, 44px);
    color: #94A3B8;
    cursor: pointer;
}

.nav-pin-btn:hover,
.nav-pin-btn:focus-visible {
    color: var(--accent-blue, #3B82F6);
}

.nav-pin-btn.is-pinned {
    color: var(--accent-blue, #3B82F6);
}

/* Go-to search results (rows reuse .nav-link-single + .active styling) */
.sidebar .nav-search-result {
    display: flex;
    align-items: center;
    gap: 0.45rem;
}

/* Section label shown above recents in the search dropdown */
.sidebar .nav-search-heading {
    font-size: 0.65rem;
    font-weight: 600;
    color: #64748B;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 0.5rem 1rem 0.125rem;
}

.sidebar .nav-search-result .nav-result-area {
    margin-left: auto;
    font-size: 0.7rem;
    color: #64748B;
    white-space: nowrap;
}

/* Desktop: pin sidebar fixed, offset content */
@media (min-width: 992px) {
    .sidebar.offcanvas-lg {
        position: fixed !important;
        top: 56px;
        bottom: 0;
        left: 0;
        width: 260px !important;
        background-color: #E2E8F0 !important;
        border-right: 1px solid #CBD5E1;
        z-index: 1019;
        visibility: visible !important;
        transform: none !important;
    }

    .sidebar.offcanvas-lg .offcanvas-body {
        display: block !important;
        background-color: transparent !important;
        overflow-y: auto !important;
        height: 100%;
        padding: 0 !important;
    }

    .page-with-sidebar {
        margin-left: 260px;
        width: auto;
    }
}

/* Carousel and marketing content styles moved to avfd-modern.css */

/* Featurettes - Keep for About Us page */

.featurette-divider {
    margin: 80px 0;
}

.featurette-heading {
    font-weight: 300;
    line-height: 1;
    letter-spacing: -1px;
    color: #D91408;
}

.text-muted {
    /* AA-passing muted text (token: ~5.48:1 on white). The old override was
       3.09:1 and failed WCAG AA on load-bearing secondary data. */
    color: var(--text-muted) !important;
}

@media (min-width: 768px) {
    .featurette-heading {
        font-size: 50px;
    }
}

@media (min-width: 992px) {
    .featurette-heading {
        margin-top: 120px;
    }
}

/* Headings inherit the body text color; page titles use .page-title (see the
   mobile-first primitives below). The legacy global h1-red/h2-gray rules are
   gone — they forced templates to pick heading levels for color instead of
   semantics, and the h2 gray (#8295A6) failed WCAG AA at 3.09:1. */

.no-padding {
    padding: 0;
}

.blue{
    color:blue;
}

.lightblue{
    color:#4B9CD3
}

/* Form validation feedback */
.invalid-feedback {
    font-size: 0.875rem;
}

/* ux-058: style Django's default as_p errorlist (red, no bullets, alert-like) */
ul.errorlist {
    list-style: none;
    margin: 0 0 0.5rem;
    padding: 0;
    color: #dc3545;
    font-size: 0.875rem;
    font-weight: 600;
}
ul.errorlist li { margin: 0; }

/* Heading consistency for private pages */
h3 {
    color: #5a5a5a;
}

/* Enabling prettier tables */
.table td.no_left_border {
    border-left: none;
}

.table td.no_right_border {
    border-right: none;
}

.table td.thick_left_border {
    border-left: 1px solid black;
}

.table td.thick_right_border {
    border-right: 1px solid black;
}

.table th.thick_left_border {
    border-left: 1px solid black;
}

.table th.thick_right_border {
    border-right: 1px solid black;
}

.table th.thick_top_border {
    border-top: 1px solid grey;
}

.table td.thick_top_border {
    border-top: 1px solid grey;
}

.table td.thin_left_border {
    border-left: 1px solid #ddd;
}

.table th.thin_left_border {
    border-left: .5px solid #ddd;
}

/* ── Today column highlight ── */
.duty-calendar th.today-col,
.duty-calendar td.today-col {
    background-color: rgba(37, 99, 235, 0.06);
}
.duty-calendar thead th.today-col {
    font-weight: 700;
}
/* Preserve posture level colors over today highlight */
.duty-calendar td.today-col.posture-level-0 { background-color: var(--posture-0); color: var(--posture-text-0); }
.duty-calendar td.today-col.posture-level-1 { background-color: var(--posture-1); color: var(--posture-text-1); }
.duty-calendar td.today-col.posture-level-2 { background-color: var(--posture-2); color: var(--posture-text-2); }
.duty-calendar td.today-col.posture-level-3 { background-color: var(--posture-3); color: var(--posture-text-3); }
.duty-calendar td.today-col.posture-level-4 { background-color: var(--posture-4); color: var(--posture-text-4); }
.duty-calendar td.today-col.posture-level-5 { background-color: var(--posture-5); color: var(--posture-text-5); }

/* ── Mini month calendar ── */
.mini-cal {
    min-width: 240px;
    user-select: none;
}
.mini-cal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.25rem;
}
.mini-cal-title {
    font-weight: 600;
    font-size: 0.85rem;
}
.mini-cal-nav {
    background: none;
    border: none;
    cursor: pointer;
    padding: 0.15rem 0.4rem;
    font-size: 0.75rem;
    color: #555;
    border-radius: 0.25rem;
}
.mini-cal-nav:hover {
    background-color: #e9ecef;
}
.mini-cal-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 1px;
    text-align: center;
    font-size: 0.75rem;
    line-height: 1;
}
.mini-cal-dow {
    font-weight: 600;
    color: #888;
    padding: 0.2rem 0;
    font-size: 0.65rem;
    text-transform: uppercase;
}
.mini-cal-day {
    padding: 0.3rem 0.15rem;
    cursor: pointer;
    border-radius: 0.2rem;
    color: #333;
}
.mini-cal-day:hover {
    background-color: #e9ecef;
}
.mini-cal-blank {
    cursor: default;
}
.mini-cal-blank:hover {
    background: none;
}
.mini-cal-active {
    background-color: #CC3333;
    color: #fff;
    font-weight: 600;
}
.mini-cal-active:hover {
    background-color: #a82a2a;
    color: #fff;
}
.mini-cal-today {
    outline: 2px solid #333;
    outline-offset: -2px;
}

/* ── Empty cell indicator ── */
.duty-calendar td .cell-empty {
    color: #ccc;
    font-size: 0.85em;
}

/* ── Legend ── */
.schedule-legend {
    font-size: 0.75rem;
    color: #666;
}
.schedule-legend .legend-group {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
}
.schedule-legend .legend-label {
    font-weight: 600;
    color: #444;
}
.schedule-legend .legend-item {
    white-space: nowrap;
}

/* ── Collapsible shift group headers ── */
.shift-group-header {
    cursor: pointer;
    user-select: none;
    background-color: var(--accent-blue-light);
}
.shift-group-header:hover {
    background-color: #e9ecef;
}
.shift-group-header td {
    font-weight: 600;
    font-size: 0.95rem;
    padding: 0.5rem 0.75rem;
    border-top: 2px solid #dee2e6;
}
.shift-group-header .toggle-icon {
    transition: transform 0.2s;
    display: inline-block;
}
.shift-group-header.collapsed .toggle-icon {
    transform: rotate(-90deg);
}
.shift-row.hidden-by-group,
.shift-row.hidden-by-filter {
    display: none;
}

/* ── Schedule header layout ── */
.schedule-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 1rem;
    margin-bottom: 0.75rem;
}
.schedule-header-left {
    flex: 1;
    min-width: 0;
}
.schedule-header-left h4 {
    font-size: 1.25rem;
    font-weight: 600;
}
.schedule-header-right {
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
}
@media (max-width: 767.98px) {
    .schedule-header {
        flex-direction: column;
        align-items: stretch;
    }
    .schedule-header-right {
        align-items: flex-start;
        flex-direction: row;
        flex-wrap: wrap;
        gap: 0.5rem;
    }
}

/* ux-061: week-nav chevrons get a >=44px tap target on phones */
@media (max-width: 767.98px) {
    .week-nav .btn {
        min-width: 44px;
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}

/* ── Role filter bar ── */
.role-filter-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    align-items: center;
}
.role-filter-bar .btn {
    font-size: 0.8rem;
    padding: 0.2rem 0.6rem;
}
.role-filter-bar .btn.active {
    font-weight: 600;
}
.role-filter-bar .dropdown-item.active {
    background-color: transparent;
    color: inherit;
    font-weight: 600;
}
.role-filter-bar .dropdown-item {
    font-size: 0.85rem;
    padding: 0.3rem 1rem;
}

/* ── Inline sign-up button — quiet ghost (A+ treatment 2026-07-12). A cleared
   member sees one of these per position × day × shift, so at rest it must not
   compete with the names of people on duty; it brightens on cell hover /
   keyboard focus. Urgency (.signup-urgent) stays the only loud affordance. ── */
.signup-inline-btn {
    display: inline-block;
    padding: 0.1rem 0.4rem;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-muted);
    background: none;
    border: none;
    border-radius: 0.25rem;
    cursor: pointer;
    line-height: 1.3;
    opacity: 0.65;
}
.signup-inline-btn::before { content: "+ "; }
td:hover > .signup-inline-btn,
.mobile-position-content .signup-inline-btn,
.signup-inline-btn:hover,
.signup-inline-btn:focus-visible {
    opacity: 1;
}
.signup-inline-btn:hover,
.signup-inline-btn:focus-visible {
    color: var(--accent-blue);
    background-color: var(--accent-blue-light);
}
@media (max-width: 767.98px) {
    /* Phones: the ghost is the primary tap target in the grid card rows —
       keep the 44px floor. */
    .signup-inline-btn {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
    }
}

/* ── Officer Mode ── */
.officer-action-queue {
    background: #fff8e1;
    border: 1px solid #ffe082;
    border-radius: 0.375rem;
    padding: 0.5rem 0.75rem;
    font-size: 0.85rem;
}
.action-queue-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.2rem 0;
    border-bottom: 1px solid #fff3cd;
}
.action-queue-item:last-child { border-bottom: none; }

.officer-readiness-bar {
    display: flex;
    align-items: center;
    gap: 2px;
}
.readiness-label { white-space: nowrap; }
/* Per-day pill with a status dot (A+ treatment): seven solid colored blocks
   read as an alarm wall; a dot + weekday label reads as information. */
.readiness-segment {
    flex: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
    padding: 0.15rem 0.3rem;
    font-size: 0.75rem;
    font-weight: 600;
    border-radius: 999px;
    border: 1px solid var(--bs-border-color);
    background-color: var(--bs-body-bg);
    color: var(--bs-body-color);
    cursor: default;
}
.readiness-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background-color: #adb5bd;
    flex: none;
}
.readiness-green .readiness-dot { background-color: #28a745; }
.readiness-amber .readiness-dot { background-color: #e67e22; }
.readiness-red .readiness-dot { background-color: #dc3545; }
.readiness-gray .readiness-dot { background-color: #adb5bd; }

/* Gap dots hidden by default, visible only in officer view */
.officer-only-indicator { display: none; }
.officer-view .officer-only-indicator { display: inline; }

/* Officer view enhanced gap indicators */
.officer-view td.posture-level-0,
.officer-view td:not([class*="posture-level-"]) {
    border-left: 3px solid #e67e22;
}
.officer-view td[class*="posture-level-"]:not(.posture-level-0) {
    border-left: none;
}

/* ── Gap Filler Panel ── */
.gap-filler-panel {
    border: 1px solid #dee2e6;
    border-radius: 0.375rem;
    background: #fff;
    margin-bottom: 0.75rem;
}
.gap-filler-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.5rem 0.75rem;
    cursor: pointer;
    background: var(--accent-blue-light);
    border-radius: 0.375rem 0.375rem 0 0;
    border-bottom: 1px solid #dee2e6;
}
.gap-filler-panel.collapsed .gap-filler-body {
    display: none;
}
.gap-filler-panel.collapsed .gap-filler-header {
    border-bottom: none;
    border-radius: 0.375rem;
}
.gap-filler-panel.collapsed .gap-filler-toggle i {
    transform: rotate(-90deg);
}
.gap-filler-toggle i {
    transition: transform 0.15s ease;
}
.gap-filler-body {
    max-height: 400px;
    overflow-y: auto;
}
.gap-filler-slot {
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid #eee;
}
.gap-filler-slot:last-child { border-bottom: none; }
.gap-filler-slot-header {
    font-size: 0.8rem;
    margin-bottom: 0.35rem;
    color: #495057;
}
.gap-filler-position {
    margin-left: 0.5rem;
    margin-bottom: 0.35rem;
}
.gap-filler-position-label {
    font-size: 0.75rem;
    font-weight: 600;
    color: #6c757d;
    margin-bottom: 0.15rem;
}
.gap-filler-member {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.15rem 0;
    font-size: 0.8rem;
}
.gap-filler-member-name {
    flex: 1;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.gap-filler-member-count {
    white-space: nowrap;
}
.gap-filler-invite-btn {
    font-size: 0.7rem;
    padding: 0.1rem 0.4rem;
    white-space: nowrap;
}

/* ── Crew chip-density grid (committed duty schedule) ──
   Compact week table mirroring the availability board grammar: posture wash
   + role-colored initials chips in three position lanes. One table, desktop
   and mobile (horizontal scroll on narrow screens via .table-responsive). */
.cs-grid {
    width: 100%;
    border-collapse: separate;
    border-spacing: 3px;
    table-layout: fixed;
}
.cs-grid thead th {
    font-size: 0.72rem;
    font-weight: 600;
    text-align: center;
    color: #6c757d;
    padding: 0.2rem;
}
.cs-grid th.cs-shift-label {
    width: 8.5rem;
    text-align: left;
    font-size: 0.78rem;
    font-weight: 600;
    color: #212529;
    vertical-align: middle;
}
.cs-grid .cs-today { outline: 2px solid var(--accent-blue); outline-offset: -2px; }
.cs-cell {
    position: relative;
    min-height: 56px;
    border-radius: 6px;
    padding: 5px 6px 4px;
    vertical-align: top;
}
/* Posture wash — a LIGHT tint of the same green readiness ramp the grid uses
   (var(--posture-wash-*)), so toggling Grid<->Crew shows the same colour family
   per level; kept pale so the role-coloured chips stay legible on top. */
.cs-cell-l0 { background: var(--posture-wash-0); }
.cs-cell-l1 { background: var(--posture-wash-1); }
.cs-cell-l2 { background: var(--posture-wash-2); }
.cs-cell-l3 { background: var(--posture-wash-3); }
.cs-cell-l4 { background: var(--posture-wash-4); }
.cs-cell-l5 { background: var(--posture-wash-5); }
.cs-pbadge {
    position: absolute;
    top: 3px;
    right: 5px;
    font-size: 0.62rem;
    font-weight: 700;
    color: #495057;
}
.cs-lane {
    display: flex;
    align-items: center;
    gap: 3px;
    flex-wrap: wrap;
    margin-bottom: 3px;
}
.cs-lane-icon { width: 13px; font-size: 0.7rem; text-align: center; flex: none; }
.cs-lane-fire .cs-lane-icon { color: #dc3545; }
.cs-lane-ems .cs-lane-icon { color: var(--accent-blue); }
.cs-lane-other .cs-lane-icon { color: #f59f00; }
.cs-chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    font-size: 0.62rem;
    font-weight: 600;
    color: #fff;
    flex: none;
}
.cs-chip-ff { background: #2fb344; }
.cs-chip-observer { background: transparent; color: #868e96; border: 1.5px solid #ced4da; }
.cs-chip-ems-driver  { background: #15aabf; }
.cs-chip-ems-tech    { background: #1098ad; }
.cs-chip-ems-trainee { background: #15aabf; border: 1.5px dashed #ffffff; }
.cs-chip-ems-student { background: #66d9e8; color: #0b525b; }
.cs-chip-oic { background: #4263eb; }
.cs-chip-collateral { background: #868e96; }
.cs-chip.cs-me { box-shadow: 0 0 0 2px #6ea8fe; }
.cs-chip.cs-pending {
    background: transparent !important;
    color: #b8860b;
    border: 1.5px dashed #f59f00;
}
.cs-chip.hidden-by-filter { display: none; }
.cs-more { font-size: 0.64rem; color: #6c757d; padding: 0 3px; align-self: center; }
.cs-cell-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 4px;
    font-size: 0.64rem;
    color: #6c757d;
    margin-top: 2px;
}
/* One "Sign up" button per cell (opens the role-picker modal) — replaces the
   busy row of per-position pills. Ghost at rest (A+ treatment): the wash +
   chips carry the cell's meaning; the affordance brightens on intent. Mobile
   cards keep a visible outline (no hover on touch). */
.cs-signup-btn {
    font-size: 0.62rem;
    font-weight: 600;
    line-height: 1.3;
    padding: 2px 8px;
    border: 1px solid transparent;
    border-radius: 999px;
    color: #48627e;
    background: none;
    white-space: nowrap;
    opacity: 0.75;
}
.cs-cell:hover .cs-signup-btn,
.cs-signup-btn:hover,
.cs-signup-btn:focus-visible {
    border-color: #6ea8fe;
    color: #0a4bce;
    background: #fff;
    opacity: 1;
}
.cs-mcard .cs-signup-btn { border-color: #6ea8fe; color: #0a4bce; background: #fff; opacity: 1; }
.cs-signup-btn i { font-size: 0.58rem; }
.cs-add { font-size: 0.7rem; color: #adb5bd; }
.cs-foot-left { display: inline-flex; align-items: center; gap: 7px; }
.cs-details { font-size: 0.64rem; white-space: nowrap; text-decoration: none; }
.cs-details:hover { text-decoration: underline; }

/* Crew roster peek offcanvas */
.peek-lane-label {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: #6c757d;
    margin-bottom: 0.2rem;
}
.peek-row { padding: 0.25rem 0; line-height: 1.45; border-bottom: 1px solid #f1f3f5; }
.peek-lane:last-child .peek-row:last-child { border-bottom: none; }
/* Roster position label — shown before each name in the hover pop-up and the
   offcanvas roster so the lossy chip lane gains its exact role (issue #537). */
.peek-pos {
    display: inline-block;
    font-size: 0.62rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: #495057;
    background: #f1f3f5;
    border-radius: 4px;
    padding: 0 5px;
    margin-right: 2px;
    vertical-align: 1px;
}

/* Desktop crew hover pop-up (issue #537): the in-cell .cs-hovercard is hidden;
   initCrewHover() lifts its markup into a Bootstrap popover on .cs-cell-body. */
@media (min-width: 992px) {
    .crew-schedule .cs-cell-body { cursor: help; }
}
.cs-hovercard-popover { max-width: 340px; }
.cs-hovercard-popover .popover-body { padding: 0.5rem 0.7rem; font-size: 0.82rem; }
.cs-hovercard-head {
    font-weight: 700;
    font-size: 0.78rem;
    color: #495057;
    margin-bottom: 0.35rem;
    padding-bottom: 0.25rem;
    border-bottom: 1px solid #e9ecef;
}

/* Compact-view legend (#crew-legend): small swatches reusing the live chip CSS. */
.cs-legend-chip { width: 16px; height: 16px; font-size: 0; vertical-align: -3px; margin-right: 2px; }
.cs-legend-pbadge { position: static; font-size: 0.7rem; }
.crew-legend .cs-lane-icon { width: auto; }
.crew-legend .cs-lane-fire-icon { color: #dc3545; }
.crew-legend .cs-lane-ems-icon { color: var(--accent-blue); }
.crew-legend .cs-lane-other-icon { color: #f59f00; }

/* Mobile crew view: one day at a time, full-width chip cards with room to
   breathe. Reuses the chip lanes + posture wash (.cs-cell-l*) from the cell
   inner partial; .cs-mcard is position:relative so the inner .cs-pbadge anchors
   to the card corner. */
.crew-mobile-day-panel { display: none; }
.crew-mobile-day-panel.active { display: block; }
.cs-mcard {
    position: relative;
    border: 1px solid #dee2e6;
    border-radius: 8px;
    padding: 10px 12px 8px;
    margin-bottom: 10px;
}
.cs-mcard-head {
    font-weight: 600;
    font-size: 0.92rem;
    margin-bottom: 8px;
    padding-bottom: 5px;
    padding-right: 2.4rem;
    border-bottom: 1px solid rgba(0, 0, 0, 0.07);
}
.cs-mcard .cs-lane { gap: 5px; margin-bottom: 6px; }
.cs-mcard .cs-chip { width: 30px; height: 30px; font-size: 0.72rem; }
.cs-mcard .cs-lane-icon { width: 18px; font-size: 0.85rem; }
.cs-mcard .cs-pbadge { top: 10px; right: 12px; font-size: 0.72rem; }
.cs-mcard .cs-cell-footer { margin-top: 8px; font-size: 0.72rem; }
.cs-mcard .cs-details { font-size: 0.78rem; }
.cs-mcard .cs-signup-btn { font-size: 0.78rem; padding: 4px 12px; }

@media (max-width: 991.98px) {
    .cs-grid th.cs-shift-label { width: 4.5rem; font-size: 0.68rem; }
    .cs-cell { padding: 4px; }
}

/* ── Gap indicator dot ── */
.gap-dot {
    color: #e67e22;
    font-size: 0.7rem;
    vertical-align: middle;
    margin-left: 0.25rem;
    cursor: default;
}

/* ── "Your week" hero (above the schedule) ── */
.myweek-hero {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    background: var(--accent-blue-light);
    border: 1px solid #dbe4f7;
    border-radius: 10px;
    padding: 12px 16px;
    margin-bottom: 1rem;
}
.myweek-main { min-width: 0; flex: 1 1 auto; }
.myweek-title {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: #4263eb;
    margin-bottom: 6px;
}
.myweek-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.myweek-chip {
    background: #fff;
    border: 1px solid #cdd9f0;
    border-radius: 999px;
    padding: 3px 11px;
    font-size: 0.82rem;
    white-space: nowrap;
}
.myweek-chip strong { color: #1f3a8a; }
.myweek-nudge { font-size: 0.9rem; color: #495057; }
.myweek-nudge i { color: #4263eb; margin-right: 4px; }
.myweek-stats { display: flex; align-items: center; gap: 14px; flex: none; }
.myweek-ring {
    --pct: 0;
    width: 60px;
    height: 60px;
    border-radius: 50%;
    background: conic-gradient(#2fb344 calc(var(--pct) * 1%), #dde5f0 0);
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
}
.myweek-ring-inner {
    width: 47px;
    height: 47px;
    border-radius: 50%;
    background: #fff;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    line-height: 1.05;
}
.myweek-ring-num { font-size: 1.05rem; font-weight: 700; color: #212529; }
.myweek-ring-den { font-size: 0.58rem; color: #6c757d; }
.myweek-meta { font-size: 0.82rem; color: #495057; }
.myweek-meta > div { margin-bottom: 2px; }

@media (max-width: 575.98px) {
    .myweek-stats { width: 100%; justify-content: space-between; }
}

/* ── Gamification: streak badge ── */
.my-streak-badge {
    color: #e67e22;
    font-weight: 600;
    white-space: nowrap;
}
.my-streak-badge i { color: #e67e22; }

/* ── Urgent sign-up styling ── */
.signup-urgent {
    display: inline-block;
    padding: 0.15rem 0.5rem;
    font-size: 0.8rem;
    font-weight: 600;
    color: #fff;
    background-color: #e67e22;
    border: 1px solid #e67e22;
    border-radius: 0.25rem;
    cursor: pointer;
}
.signup-urgent:hover {
    background-color: #d35400;
    border-color: #d35400;
    color: #fff;
}

/* ── Sign-up modal ── */
.signup-modal-info {
    background-color: var(--accent-blue-light);
    border-radius: 0.25rem;
    padding: 0.5rem 0.75rem;
}
#signupModal .modal-sm { max-width: 360px; }

/* ── Invite autocomplete ── */
.invite-search-wrapper { position: relative; }
.invite-search-results {
    position: absolute;
    z-index: 1050;
    background: #fff;
    border: 1px solid #dee2e6;
    border-radius: 0.25rem;
    max-height: 150px;
    overflow-y: auto;
    width: 100%;
    box-shadow: 0 2px 6px rgba(0,0,0,0.1);
}
.invite-result-item {
    padding: 0.3rem 0.5rem;
    font-size: 0.82rem;
    cursor: pointer;
}
.invite-result-item:hover { background-color: var(--accent-blue-light); }
.invite-tag {
    display: inline-block;
    background: #e9ecef;
    border-radius: 1rem;
    padding: 0.15rem 0.5rem;
    font-size: 0.78rem;
    margin: 0.15rem 0.15rem 0 0;
}
.invite-tag-remove {
    cursor: pointer;
    margin-left: 0.2rem;
    font-weight: bold;
    color: #6c757d;
}
.invite-tag-remove:hover { color: #dc3545; }

/* ── Consistent cell formatting ── */
.shift-entry { margin-bottom: 0.15rem; }
.shift-entry + .shift-entry { padding-top: 0.15rem; border-top: 1px dotted #eee; }
.shift-member { font-weight: 500; }
.shift-badges { font-size: 0.8em; opacity: 0.85; margin-left: 0.15rem; }
.shift-notes { display: block; font-size: 0.8em; color: #888; font-style: italic; }
/* Names in grid cells: text-colored quiet links (A-tier: people are the
   signal, not blue underlines); underline returns on hover as the link cue. */
.duty-calendar .shift-entry a,
.mobile-position-content .shift-entry a {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px dotted currentColor;
}
.duty-calendar .shift-entry a:hover,
.mobile-position-content .shift-entry a:hover { text-decoration: none; border-bottom-style: solid; }

/* ── A+ treatment (2026-07-12): pending badge, in-cell approve, past days,
      board-key offcanvas, hero additions ── */
.shift-pending-badge {
    display: inline-block;
    font-size: 0.68em;
    font-weight: 600;
    padding: 0 0.35em;
    border-radius: 0.25rem;
    background-color: #fff3cd;
    color: #7a5c13;
    white-space: nowrap;
}
.cell-approve-link {
    display: inline-block;
    font-size: 0.72em;
    font-weight: 600;
    padding: 0.05rem 0.45rem;
    border: 1px solid #198754;
    border-radius: 999px;
    color: #198754;
    text-decoration: none;
    white-space: nowrap;
}
.cell-approve-link:hover { background-color: #198754; color: #fff; }

/* Past-day columns read as spent, not broken: dim content and washes alike.
   Today never carries .day-past, so the today emphasis is untouched. */
.duty-calendar th.day-past,
.duty-calendar td.day-past,
.cs-grid th.day-past,
.cs-grid td.day-past { opacity: 0.55; }
.day-tab.day-past { opacity: 0.55; }
.mobile-day-panel.day-past .mobile-shift-card,
.crew-mobile-day-panel.day-past .cs-mcard { opacity: 0.65; }

/* Board key offcanvas */
.key-section-title { margin: 1rem 0 0.15rem; font-weight: 700; }
.key-section-title:first-child { margin-top: 0; }
.key-section-hint { font-size: 0.78rem; color: var(--text-muted); margin-bottom: 0.4rem; }
.key-rows { display: flex; flex-direction: column; gap: 0.4rem; font-size: 0.85rem; }
.key-row { display: flex; align-items: baseline; gap: 0.5rem; }
.key-row .key-swatch {
    flex: none;
    font-size: 0.7rem;
    font-weight: 700;
    padding: 1px 7px;
    border-radius: 0.25rem;
}
.key-lead { flex: none; width: 28px; text-align: center; }
.key-row .key-lead { align-self: center; }
.key-chip-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.35rem 0.75rem;
    margin: 0.5rem 0;
    font-size: 0.85rem;
}
.key-chip-item { display: inline-flex; align-items: center; gap: 0.4rem; }

/* Hero additions: linked title, next-shift lead, cross-station chip tag */
a.myweek-title { text-decoration: none; display: inline-block; }
a.myweek-title:hover { text-decoration: underline; }
.myweek-next { font-size: 0.8rem; color: #495057; margin-top: 4px; }
.myweek-next i { color: #4263eb; }
.myweek-chip-station {
    font-size: 0.68em;
    font-weight: 700;
    padding: 0 0.35em;
    border-radius: 0.25rem;
    background: #d8e2f5;
    color: #1f3a8a;
    vertical-align: 1px;
}
/* Action-queue entity rows sit on the amber panel: entity-list supplies the
   dividers, so the legacy per-item border would double them. */
.officer-action-queue .entity-row { padding: 0.35rem 0; }
.officer-action-queue .entity-row.action-queue-item { border-bottom: none; }
/* Key offcanvas reuses legend classes whose nowrap/centering were meant for
   the old inline strip — in a 400px panel rows wrap and left-align. */
#board-key .legend-item { white-space: normal; }
#board-key .legend-group { align-items: flex-start; }

/* ── Staffing summary dots (inline in table headers & mobile tabs) ── */
.summary-dot {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    margin-right: 0.2rem;
    vertical-align: middle;
}
.summary-dot.status-green { background-color: #28a745; }
.summary-dot.status-yellow { background-color: #ffc107; }
.summary-dot.status-red { background-color: #dc3545; }
.summary-dot.status-gray { background-color: #ccc; }

/* Posture level cell backgrounds */
.posture-level-0 { background-color: var(--posture-0); color: var(--posture-text-0); }
.posture-level-1 { background-color: var(--posture-1); color: var(--posture-text-1); }
.posture-level-2 { background-color: var(--posture-2); color: var(--posture-text-2); }
.posture-level-3 { background-color: var(--posture-3); color: var(--posture-text-3); }
.posture-level-4 { background-color: var(--posture-4); color: var(--posture-text-4); }
.posture-level-5 { background-color: var(--posture-5); color: var(--posture-text-5); }
/* Override Bootstrap .table td background (higher specificity needed) */
.duty-calendar td.posture-level-0 { background-color: var(--posture-0); color: var(--posture-text-0); }
.duty-calendar td.posture-level-1 { background-color: var(--posture-1); color: var(--posture-text-1); }
.duty-calendar td.posture-level-2 { background-color: var(--posture-2); color: var(--posture-text-2); }
.duty-calendar td.posture-level-3 { background-color: var(--posture-3); color: var(--posture-text-3); }
.duty-calendar td.posture-level-4 { background-color: var(--posture-4); color: var(--posture-text-4); }
.duty-calendar td.posture-level-5 { background-color: var(--posture-5); color: var(--posture-text-5); }

/* ── Mobile card view ── */
.mobile-schedule { display: none; }
.mobile-day-tabs {
    display: flex;
    overflow-x: auto;
    gap: 0;
    border-bottom: 2px solid #dee2e6;
    -webkit-overflow-scrolling: touch;
}
.mobile-day-tabs .day-tab {
    flex: 0 0 auto;
    padding: 0.5rem 1rem;
    font-size: 0.85rem;
    font-weight: 500;
    border: none;
    background: none;
    cursor: pointer;
    white-space: nowrap;
    border-bottom: 3px solid transparent;
    color: #666;
}
.mobile-day-tabs .day-tab.active {
    color: #CC3333;
    border-bottom-color: #CC3333;
    font-weight: 700;
}
.mobile-day-panel { display: none; }
.mobile-day-panel.active { display: block; }
.mobile-shift-card {
    border: 1px solid #dee2e6;
    border-radius: 0.5rem;
    margin-bottom: 0.75rem;
    overflow: hidden;
}
.mobile-shift-card-header {
    background-color: var(--accent-blue-light);
    padding: 0.5rem 0.75rem;
    font-weight: 600;
    font-size: 0.9rem;
    border-bottom: 1px solid #dee2e6;
}
.mobile-shift-card-body { padding: 0; }
.mobile-position-row {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid #f0f0f0;
    gap: 0.5rem;
}
.mobile-position-row:last-child { border-bottom: none; }
.mobile-position-label {
    font-weight: 500;
    font-size: 0.85rem;
    min-width: 110px;
    flex-shrink: 0;
    color: #555;
}
.mobile-position-content {
    flex: 1;
    font-size: 0.85rem;
    text-align: right;
}
.mobile-position-content .cell-empty { color: #ccc; }

/* ── Responsive: hide desktop table, show mobile on small screens ── */
/* Note: no !important here so that Bootstrap's d-none (!important) can
   override these defaults when JS toggles between Grid / Crew views. */
@media (max-width: 991.98px) {
    .desktop-schedule { display: none; }
    .mobile-schedule { display: block; }
    .crew-schedule { display: none; }
    .crew-schedule-mobile { display: block; }
}
@media (min-width: 992px) {
    .desktop-schedule { display: block; }
    .mobile-schedule { display: none; }
    .crew-schedule-mobile { display: none; }
}

/* Duty schedule calendar desktop responsive */
@media (max-width: 768px) {
    .duty-calendar td, .duty-calendar th {
        font-size: 0.75rem;
        padding: 0.25rem;
    }
}

/* Posture level link colors (keep links readable on dark backgrounds) */
.posture-level-3 a, .posture-level-4 a, .posture-level-5 a,
.duty-calendar td.posture-level-3 a,
.duty-calendar td.posture-level-4 a,
.duty-calendar td.posture-level-5 a {
    color: #ffffff;
}
.posture-level-0 a, .posture-level-1 a, .posture-level-2 a,
.duty-calendar td.posture-level-0 a,
.duty-calendar td.posture-level-1 a,
.duty-calendar td.posture-level-2 a {
    color: #155724;
}

/* Sign-up buttons inside posture-washed cells (#714), LIGHT mode. The
   button's default var(--accent-blue) #2563EB is only ~4.2:1 on the L1
   pastel, ~3.2:1 on L2, and ~1.7:1 on the saturated L3-5 greens. Deep
   accent (#1E40AF, the btn-primary active stop) is >=5.4:1 on every pale
   wash; white matches the member-name links on the saturated levels.
   L3-5 are the same saturated greens in BOTH modes, so white holds there;
   the pale L0-2 cells are remapped to dark washes under
   [data-bs-theme="dark"] and get their own button/link colors in the dark
   section (~line 1880). Hover keeps the global solid-fill treatment. */
.posture-level-0 .signup-inline-btn,
.posture-level-1 .signup-inline-btn,
.posture-level-2 .signup-inline-btn {
    color: #1E40AF;
    border-color: #1E40AF;
}
.posture-level-3 .signup-inline-btn,
.posture-level-4 .signup-inline-btn,
.posture-level-5 .signup-inline-btn {
    color: #fff;
    border-color: #fff;
}

/* ── Accent utility classes (opt-in via template classes) ── */

/* Card left-border accents */
.card-accent-blue {
    border-left: 3px solid var(--accent-blue);
}

.card-accent-green {
    border-left: 3px solid #22C55E;
}

.card-accent-red {
    border-left: 3px solid #DC2626;
}

/* Colored stat boxes */
.stat-box-blue {
    background-color: var(--accent-blue-light);
    border: 1px solid var(--accent-blue-border);
    border-radius: 0.25rem;
}
.stat-box-blue .stat-value { color: var(--accent-blue); }
.stat-box-blue .stat-label { color: #1E40AF; }

.stat-box-green {
    background-color: #F0FDF4;
    border: 1px solid #BBF7D0;
    border-radius: 0.25rem;
}
.stat-box-green .stat-value { color: #16A34A; }
.stat-box-green .stat-label { color: #166534; }

.stat-box-red {
    background-color: #FEF2F2;
    border: 1px solid #FECACA;
    border-radius: 0.25rem;
}
.stat-box-red .stat-value { color: #DC2626; }
.stat-box-red .stat-label { color: #991B1B; }

/* ux-107: larger tap targets for sidebar sub-links on touch/phones */
@media (max-width: 991.98px) {
    .sidebar .accordion-body a,
    .sidebar .nav-link-single { padding-top: 0.7rem; padding-bottom: 0.7rem; min-height: 44px; }
}

/* ==================================================================
   MOBILE-FIRST FLOORS — Phase 0
   docs/superpowers/specs/2026-06-27-mobile-first-roadmap-design.md
   Global, low-specificity floors applied at the source so the whole
   defect class is fixed once. Scoped to phones/tablets via media
   queries so desktop (>=992px) density is provably untouched. These
   only ever GROW under-sized controls — they never shrink anything.
   ================================================================== */

/* (1) iOS auto-zoom floor. Mobile Safari zooms the viewport (and stays
   zoomed) whenever a focused control renders < 16px. Pin every form
   control — including Bootstrap's -sm variants and Select2's rendered
   inputs — to >= 16px on PHONES ONLY, leaving compact desktop sizing
   intact above 576px. */
@media (max-width: 575.98px) {
    .form-control,
    .form-select,
    .form-control-sm,
    .form-select-sm,
    .input-group-sm > .form-control,
    .input-group-sm > .form-select,
    textarea.form-control,
    .select2-container .select2-selection__rendered,
    .select2-container .select2-search__field,
    #sidebarSearchInput {
        font-size: var(--form-control-min-font);
    }
    /* Select2 selection box: a tappable height on phones (Wave 2B). !important
       because Select2's CDN CSS loads after fireman.css at equal specificity.
       Height-only — flex centering would break the multi-select's tag wrapping. */
    .select2-container .select2-selection {
        min-height: var(--tap-min) !important;
    }
}

/* (2) Touch-target floor. Generalize the sidebar-only 44px rule (above)
   to every control a field user taps. A min-height (never a fixed
   height) plus inline-flex centering grows only controls shorter than
   44px, preserving desktop visual weight. Phones/tablets only.
   NOTE: .accordion-button is intentionally height-only — Bootstrap needs
   its display:flex/width:100%, so we must not override its display. */
@media (max-width: 991.98px) {
    .btn,
    .navbar-private .btn,
    .page-link,
    .btn-close,
    .fc .fc-button {
        min-height: var(--tap-min);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
    /* Icon-only / compact actions need a horizontal floor too, so the hit
       area is roughly square rather than a thin sliver. A single-digit
       .page-link is ~33px wide, so it needs the floor as well. */
    .navbar-private .btn,
    .table .btn-sm,
    .btn-close,
    .page-link {
        min-width: var(--tap-min);
    }
    /* …except the pager's ellipsis, which is decoration, not a tap target.
       Reserving 44px for something unclickable costs ~8px of pager width for
       nothing, which is the difference between one row and two on a 414px
       phone. (A full 7-number pager still wraps at 375px — see .pagination.) */
    .page-ellipsis .page-link {
        min-width: 0;
    }
    /* Accordion group toggles: height floor only (keep Bootstrap's display). */
    .accordion-button {
        min-height: var(--tap-min);
    }
}

/* ==================================================================
   PHASE 1 — MOBILE-FIRST COMPONENT PRIMITIVES
   docs/superpowers/specs/2026-06-27-mobile-first-roadmap-design.md
   The shared contract the Phase 2 sweep applies; demonstrated live in
   the gated component gallery at /private/components/.
   ================================================================== */

/* .page-header — title + actions that WRAP instead of colliding at 375px.
   Replaces ad-hoc `d-flex justify-content-between` page headers (theme 5).
   Usage: <div class="page-header"><h1 class="page-title">…</h1>
            <div class="page-header-actions">…buttons…</div></div> */
.page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2) var(--space-3);
    margin-bottom: var(--space-4);
}
.page-header > .page-header-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
}
/* Fluid title — never eats a phone viewport, scales up on desktop. */
.page-title {
    font-size: var(--type-h1);
    margin-bottom: 0;
}

/* .pagination — a pager must never outgrow the viewport. Bootstrap's pager is a
   non-wrapping flex row, so a long page list under `justify-content-center`
   spills off BOTH edges; `body { overflow-x: clip }` then clips the overflow
   with no scrollbar, leaving the low page numbers permanently unclickable
   (issue #835). The elided range in {% pagination %} keeps the widget short and
   this wrap is the backstop: worst case it takes a second line, never a
   clipping. Usage: {% load pagination %}{% pagination page_obj %}. */
.pagination {
    flex-wrap: wrap;
    row-gap: var(--space-2);
}

/* .page-narrow — readable line lengths for form/detail pages. Wide dashboards
   and tables keep the full viewport; single-column forms cap here so inputs
   don't stretch to 1200px lines on desktop. Left-aligned (not centered) so
   pages hang together with the sidebar. */
.page-narrow {
    max-width: 860px;
}

/* .toolbar — a wrapping replacement for inline `.btn-group` action rows that
   overflow the viewport (theme 5, e.g. the message-center inbox toolbar). */
.toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
}

/* .console-nav — per-event workspace tabs shown under the page header on every
   console page (Overview / Details / Schedule / Public page / catalog / sales).
   A single scrollable row on phones: the wrapper scrolls horizontally (nowrap)
   so the strip never wraps or collides at 375px, with the scrollbar hidden but
   scrolling intact. Bootstrap .nav-tabs already handles the color modes, so this
   only pins layout + a muted resting color; no hardcoded surfaces. */
.console-nav {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;                 /* Firefox */
    margin-bottom: var(--space-4);
}
.console-nav::-webkit-scrollbar { display: none; }   /* WebKit */
.console-nav .nav-tabs {
    flex-wrap: nowrap;
    white-space: nowrap;
}
.console-nav .nav-link {
    white-space: nowrap;
    color: var(--text-muted);
}
.console-nav .nav-link.active { font-weight: 600; }

/* .shift-group — the Schedule workspace groups volunteer-shift rows under their
   EventDate so a shift inherits the date's window without re-typing it. Each
   group is a light-bordered block with a heading (weekday/date/time/badges) and
   its own "Add shift" button. Tokens + Bootstrap color modes only — no hardcoded
   surfaces, so dark mode follows [data-bs-theme]. */
.shift-group {
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 12px;
    padding: var(--space-3);
    margin-bottom: var(--space-3);
}
.shift-group-head {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: baseline;
    margin-bottom: var(--space-2);
}
.shift-group-title {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: baseline;
}
.shift-group-date { font-weight: 600; }
/* The per-row "Move to" date select is de-emphasized — the group heading already
   states the date; this is the escape hatch for re-homing a shift. */
.shift-move .form-select { font-size: 0.85rem; }
.shift-remove { white-space: nowrap; }
/* The unsaved-date placeholder reads as a muted, dashed hint, not a real group. */
.shift-group-unsaved { border-style: dashed; opacity: 0.85; }

/* .day-card — the Schedule workspace's one-card-per-day unit: the date's fields
   and that date's shifts together, so the lead never joins two lists by
   scrolling. Header carries the live date title + badges; an unsaved card is
   dashed until Save materializes it (and its auto-created default shift).
   Tokens + Bootstrap color modes only — no hardcoded surfaces. */
.day-card-head {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: baseline;
}
.day-card-title { font-weight: 600; }
.day-card-unsaved { border-style: dashed; }
.day-remove { white-space: nowrap; }

/* .page-tile* — the Public page workspace's content-tile editor. Tiles are
   grouped under their section (Attractions / FAQs / Copy) and collapse to a
   summary row (thumb/icon + title + badges + reorder arrows), expanding to the
   full field editor via a native <details>. Tokens + Bootstrap color modes only
   — no hardcoded surfaces, so dark mode follows [data-bs-theme]. */
.page-tile-group { margin-bottom: var(--space-4); }
.page-tile-group__head {
    font-size: 1rem;
    font-weight: 600;
    margin-bottom: var(--space-2);
}
.page-tile-list { display: flex; flex-direction: column; gap: var(--space-2); }
.page-tile {
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 12px;
    overflow: hidden;
}
.page-tile__wrap > summary { list-style: none; }
.page-tile__wrap > summary::-webkit-details-marker { display: none; }
/* ONE nowrap flex row: [chevron][thumb][title <- grows + truncates][badges][^][v].
   Every fixed part is flex:none so the move buttons sit at the same x-offset on
   every row; only the title flexes (min-width:0 lets it ellipsize), so nothing
   ever wraps or staggers at 375px. */
.page-tile__summary {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-2);
    min-height: var(--tap-min, 44px);
    cursor: pointer;
}
.page-tile__summary > * { flex: none; }
.page-tile__thumb {
    width: 40px;
    height: 40px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bs-tertiary-bg, #f1f3f5);
    color: var(--text-muted);
    overflow: hidden;
}
.page-tile__thumb img { width: 100%; height: 100%; object-fit: cover; }
/* No image and no picked icon: the per-type glyph reads as a faint placeholder,
   not a chosen icon. */
.page-tile__thumb--empty { opacity: 0.55; }
.page-tile__title {
    flex: 1 1 auto;
    min-width: 0;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.page-tile__badge { white-space: nowrap; }
.page-tile__moves { display: flex; gap: var(--space-1); }
.page-tile__move {
    width: var(--tap-min, 44px);
    height: var(--tap-min, 44px);
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.page-tile__chevron {
    width: 1rem;
    text-align: center;
    color: var(--text-muted);
    transition: transform 0.15s ease;
}
.page-tile__wrap[open] .page-tile__chevron { transform: rotate(90deg); }
/* Tiny phones with a marked-Hidden/Delete tile can show three badges at once —
   drop the decorative thumb before anything is forced to squeeze. */
@media (max-width: 374.98px) {
    .page-tile__thumb { display: none; }
}
.page-tile__body {
    padding: var(--space-3);
    border-top: 1px solid var(--bs-border-color, #dee2e6);
}

/* .icon-picker — tap-to-select grid of curated Font Awesome icons; a hidden
   input holds the chosen class, so a chair never types a raw class. Each option
   is a >=44px tap target; the selected one gets the accent ring. */
.icon-picker {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    margin-top: var(--space-1);
}
.icon-picker__opt {
    min-width: var(--tap-min, 44px);
    min-height: var(--tap-min, 44px);
    padding: 0 var(--space-1);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.15rem;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 10px;
    background: var(--bs-body-bg, #fff);
    color: var(--bs-body-color, inherit);
    cursor: pointer;
}
.icon-picker__none { font-size: 0.8rem; }
.icon-picker__opt.is-selected {
    border-color: var(--bs-primary, #0d6efd);
    box-shadow: inset 0 0 0 1px var(--bs-primary, #0d6efd);
    color: var(--bs-primary, #0d6efd);
}

/* .form-stack — mobile-first form layout: block labels, full-width controls,
   breathing room. Applied to dense multi-column forms that don't collapse. */
.form-stack .form-label { font-weight: 600; margin-bottom: var(--space-1); }
.form-stack .form-control,
.form-stack .form-select,
.form-stack textarea { width: 100%; }

/* .table-cards — responsive data table that becomes STACKED CARDS on phones,
   so the action/total columns are never clipped (theme 1). The table keeps its
   semantics at >=768px; below that each row is a labelled card.
   Convention: every <td> carries data-label="<column name>" mirroring its <th>
   (the "same column definitions" card fallback). Still wrap in .table-responsive.
   Flag a row's action cell with class="cell-actions" so it spans full width. */
@media (max-width: 767.98px) {
    .table-cards thead {
        position: absolute; width: 1px; height: 1px; padding: 0;
        margin: -1px; overflow: hidden; clip: rect(0 0 0 0);
        white-space: nowrap; border: 0;            /* visually-hidden, kept for a11y */
    }
    .table-cards tbody tr {
        display: block;
        margin-bottom: var(--space-3);
        border: 1px solid var(--bs-border-color, #dee2e6);
        border-radius: 12px;
        padding: var(--space-2) var(--space-3);
        background: var(--bs-body-bg, #fff);
    }
    .table-cards tbody td {
        display: flex;
        justify-content: space-between;
        gap: var(--space-3);
        align-items: baseline;
        padding: var(--space-1) 0;
        border: 0;
        text-align: right;
    }
    .table-cards tbody td::before {
        content: attr(data-label);
        font-weight: 600;
        color: var(--text-muted);
        text-align: left;
        margin-right: auto;
    }
    /* A cell flagged as the row's actions spans full width, left-aligned. */
    .table-cards tbody td.cell-actions { justify-content: flex-start; text-align: left; }
    .table-cards tbody td.cell-actions::before { content: none; }
}

/* ==================================================================
   A-TIER COMPONENT PRIMITIVES (2026-07 design language)
   Spec: docs/superpowers/specs/2026-07-09-a-tier-design-language.md
   Live reference: /private/components/ (staff-only gallery).
   Content-first building blocks: people render as chips + rows with
   secondary metadata, numbers render as stats, absence renders as an
   actionable empty state. Use THESE instead of bare link lists,
   bare <p class="text-muted"> empties, or ad-hoc big numbers.
   ================================================================== */

/* .avatar-chip — initials chip for a person. Neutral by default; -officer
   marks rank.officer=True (navy); -gold marks distinction (awards, life
   members, elected office). 32px default, 24px -sm for dense lists. */
.avatar-chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: #334155;
    background: #E2E8F0;
    flex: none;
    text-transform: uppercase;
}
.avatar-chip-sm { width: 24px; height: 24px; font-size: 0.62rem; }
.avatar-chip-officer { background: var(--avfd-navy, #1A2332); color: #F1F5F9; }
.avatar-chip-gold { background: var(--gold-fill); color: #3D2E09; }
[data-bs-theme="dark"] .avatar-chip { background: #2A3A52; color: #C7D0DB; }
[data-bs-theme="dark"] .avatar-chip-officer { background: #F1F5F9; color: #1A2332; }
[data-bs-theme="dark"] .avatar-chip-gold { background: var(--gold-fill); color: #3D2E09; }

/* .date-chip — a calendar-date tile for date-anchored rows (shifts, classes,
   calls). Fills the same leading slot as .avatar-chip in an .entity-row; the
   day numeral gets the Barlow Condensed display face like .stat-value.
   Token-driven surfaces, so dark mode needs no override. */
.date-chip {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 40px;
    min-height: 40px;
    border-radius: 8px;
    border: 1px solid var(--bs-border-color);
    background: var(--bs-tertiary-bg);
    line-height: 1;
    flex: none;
}
.date-chip-month {
    font-size: 0.55rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    margin-bottom: 2px;
}
.date-chip-day {
    font-family: 'Barlow Condensed', 'Montserrat', system-ui, sans-serif;
    font-size: 1.15rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* .entity-row — the standard "thing in a list" anatomy: leading chip/icon,
   body (primary line + muted secondary line), trailing meta (badge/action).
   Composes with .list-group-item (add both classes) or stands alone inside
   .entity-list. Whole-row links: make the row an <a class="entity-row ...">. */
.entity-list { display: flex; flex-direction: column; }
.entity-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: var(--tap-min);
    padding: var(--space-2) 0;
    color: inherit;
    text-decoration: none;
}
.entity-list > .entity-row + .entity-row { border-top: 1px solid var(--bs-border-color); }
/* Rows that carry a trailing control (e.g. the hub pin button) wrap the
   <a.entity-row> in a flex .entity-row-wrap; dividers move to the wrapper. */
.entity-list > .entity-row-wrap + .entity-row-wrap { border-top: 1px solid var(--bs-border-color); }
.entity-row-wrap { display: flex; align-items: center; }
.entity-row-wrap > .entity-row { flex: 1 1 auto; min-width: 0; }
.entity-row-wrap > form { flex: none; }
a.entity-row:hover .entity-row-primary { color: var(--bs-link-color); }
.entity-row-body { min-width: 0; flex: 1 1 auto; }
.entity-row-primary {
    font-weight: 600;
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.entity-row-secondary {
    font-size: var(--type-sm);
    color: var(--text-muted);
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.entity-row-meta {
    margin-left: auto;
    flex: none;
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

/* .stat-block — a number that matters. Barlow Condensed numeral (the brand's
   apparatus-lettering display face, same as the public home), uppercase label,
   optional context line. Group in a .stat-row (wraps on phones). */
.stat-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4) var(--space-6);
}
.stat-block { min-width: 0; }
.stat-value {
    font-family: 'Barlow Condensed', 'Montserrat', system-ui, sans-serif;
    font-size: clamp(1.9rem, 1.5rem + 1.8vw, 2.75rem);
    font-weight: 600;
    line-height: 1;
    letter-spacing: 0.01em;
    font-variant-numeric: tabular-nums;
}
.stat-value .stat-unit { font-size: 0.55em; font-weight: 500; color: var(--text-muted); }
.stat-label {
    font-size: var(--type-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    margin-top: 2px;
}
.stat-context { font-size: var(--type-sm); color: var(--text-muted); }
.stat-accent-gold .stat-value { color: var(--gold-text); }

/* .empty-state — absence with a next step. Never a bare muted <p>: say what's
   absent AND offer the action that fills it. -compact for inside cards. */
.empty-state {
    text-align: center;
    padding: var(--space-6) var(--space-4);
    color: var(--text-muted);
}
.empty-state-compact { padding: var(--space-4) var(--space-3); }
.empty-state .empty-state-icon {
    font-size: 1.6rem;
    opacity: 0.45;
    display: block;
    margin-bottom: var(--space-2);
}
.empty-state .empty-state-msg { margin-bottom: var(--space-3); }
.empty-state .btn { margin-top: 0; }

/* .page-eyebrow — small uppercase context line above .page-title (which
   section of the app am I in). Pure text; never a link bar or breadcrumb. */
.page-eyebrow {
    font-size: var(--type-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-muted);
    margin-bottom: var(--space-1);
}

/* .section-nav — in-page jump chips under the page header (the pipeline funnel's
   anti-scrolling-nightmare device: one tap to any card). A wrapping row of pill
   links so it never collides or overflows at 375px; a phone tap-target floor
   grows each chip to 44px. Token / Bootstrap-color-mode driven — no hardcoded
   surfaces, so the viewer's light/dark theme drives it with no per-mode override. */
.section-nav {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-bottom: var(--space-4);
}
.section-nav-link {
    display: inline-flex;
    align-items: center;
    padding: 0.3rem 0.8rem;
    border-radius: 999px;
    border: 1px solid var(--bs-border-color);
    background: var(--bs-tertiary-bg);
    color: var(--bs-body-color);
    font-size: var(--type-sm);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}
.section-nav-link:hover,
.section-nav-link:focus {
    border-color: var(--bs-primary);
    color: var(--bs-link-color);
}
/* A jumped-to card sits a little below the viewport top, not flush against it. */
.card[id^="funnel-"] { scroll-margin-top: var(--space-3); }
@media (max-width: 991.98px) {
    .section-nav-link { min-height: var(--tap-min); }
}

/* ── Activation board (journey funnel) ────────────────────────────
   Two-axis redesign (spec §6.5): per-apparatus activation columns of
   member chips — what each member is cleared to do RIGHT NOW, not their
   furthest county-qualification milestone. Replaces the retired stage-
   grouped pipeline board. Apparatus sections sit side-by-side on desktop
   and STACK below md; chips flow and wrap, so the board never scrolls
   the page sideways on a phone. All surfaces are token/Bootstrap-
   -variable driven, so the viewer's light/dark theme drives it with no
   per-mode override. */
.activation-apparatus-details {
    border-top: 1px solid var(--bs-border-color);
    padding-top: var(--space-3);
    margin-top: var(--space-3);
}
.activation-apparatus-details:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }
.activation-apparatus-summary {
    cursor: pointer;
    font-weight: 600;
    min-height: var(--tap-min);
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
.activation-columns {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-top: var(--space-2);
}
/* Path-board alert strip: the engine/ambulance "Qualified but not in the pipeline" margin cases,
   surfaced above the columns only when occupied (owner directive 2026-07-16). Warning-tinted, the
   same "done with the hard part but nobody moved forward" flag the retired first column carried. */
.path-alert {
    margin-top: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--bs-warning-border-subtle);
    border-radius: 8px;
    background: var(--bs-warning-bg-subtle);
}
.path-alert-head {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    font-weight: 600;
}
.path-alert-title { font-size: var(--type-sm); }
.path-alert-note {
    margin: var(--space-1) 0 var(--space-2);
    color: var(--text-muted);
    font-size: 0.8rem;
}
.activation-column {
    flex: 1 1 190px;
    min-width: 0;
    border: 1px solid var(--bs-border-color);
    border-radius: 8px;
    background: var(--bs-tertiary-bg);
    padding: var(--space-2) var(--space-3);
}
/* Qualified, not riding — the owner's "lost at the margins" column: full county
   qualification, zero local clearance progress. Warning-tinted so it visually
   flags a member who is done with the hard part but nobody has moved forward. */
.activation-column-qualified {
    border-color: var(--bs-warning-border-subtle);
    background: var(--bs-warning-bg-subtle);
}
.activation-column-cleared {
    border-color: var(--bs-success-border-subtle);
    background: var(--bs-success-bg-subtle);
}
.activation-column-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}
.activation-column-title { font-size: var(--type-sm); font-weight: 600; }
.activation-column-empty { color: var(--text-muted); }
/* Column-header "info" affordance (owner addition 2026-07-18, item 7): the whole header is a
   native <summary> tap target that reveals the bin's plain-language entry-requirements sentence.
   No new JS; ≥44px tap target; token-driven so dark mode needs no override. */
.col-criteria { margin-bottom: var(--space-2); }
.col-criteria-summary {
    list-style: none;
    cursor: pointer;
    min-height: var(--tap-min);
    align-items: center;
    justify-content: flex-start;
    margin-bottom: 0;
}
.col-criteria-summary::-webkit-details-marker { display: none; }
.col-criteria-icon { margin-left: auto; color: var(--text-muted); font-size: 0.8rem; }
.col-criteria[open] .col-criteria-icon { color: var(--bs-primary); }
.col-criteria-text {
    margin: var(--space-1) 0 var(--space-2);
    font-size: 0.8rem;
    line-height: 1.4;
    color: var(--bs-body-color);
}
.activation-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.activation-chip {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px;
    max-width: 100%;
    padding: 3px 10px;
    border-radius: 999px;
    border: 1px solid var(--bs-border-color);
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
    font-size: var(--type-sm);
    line-height: 1.35;
    text-decoration: none;
}
a.activation-chip:hover { border-color: var(--bs-primary); color: var(--bs-link-color); }
.activation-chip-more { color: var(--text-muted); background: transparent; border-style: dashed; }
.activation-chip-badge { font-size: 0.65rem; padding: 1px 6px; }
/* Days-in-phase suffix on a path-board chip ("· 32d") — muted, secondary to the name/badge. */
.activation-chip-days { color: var(--text-muted); font-size: 0.7rem; white-space: nowrap; }
/* Path-board grouping (Riding / Driver / Officer / Getting started). */
.path-group { margin-top: var(--space-4); }
.path-group:first-of-type { margin-top: 0; }
.path-group-title {
    font-size: var(--type-sm);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    margin-bottom: var(--space-2);
}
.activation-chip-drive { font-size: 0.7rem; }
/* Drive-in-training marker: same fa-car glyph, muted/faded so it reads as the
   "not yet" variant next to the solid success-colored cleared-to-drive icon. */
.activation-chip-drive-training { opacity: 0.45; }
@media (max-width: 767.98px) {
    .activation-column { flex-basis: 100%; }
}

/* ── Race track (journey funnel) ───────────────────────────────────
   Spec §11.8, owner directive 2026-07-17: each riding path is a vertical
   STACK of its main-line stage ROWS; each row is a horizontal time-in-
   stage axis. Every member currently at a stage is an initials chip
   absolutely positioned within that row's axis by real days-in-stage
   (server-computed percentage of the row's own 0→2×expected scale). The
   chip pattern `left: P%; translateX(-P%)` keeps a chip fully inside the
   axis at both extremes (0 = flush left, 100 = flush right). The axis is
   percentage-scaled so it compresses to any width; .race-scroll is the
   sanctioned horizontal-scroll backstop for the narrowest phones — the
   page body never scrolls sideways. The shaded left half is the 0→
   expected "on-track" band; the hatched right edge is the past-2×-
   expected "parked" zone. Everything is token / Bootstrap-5.3-color-mode
   driven, so dark mode needs no override. */
.race-lane-details,
.race-aging-details {
    border-top: 1px solid var(--bs-border-color);
    padding-top: var(--space-2);
    margin-top: var(--space-2);
}
.race-lane-details:first-of-type { border-top: 0; }
.race-lane-summary {
    cursor: pointer;
    font-weight: 600;
    min-height: var(--tap-min);
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
.race-scroll { overflow-x: auto; padding-bottom: var(--space-1); }
/* Now a vertical stack of stage rows. min-width keeps each axis wide enough for chips to
   separate on a narrow phone (scrolls inside .race-scroll below it). */
.race-lane { display: flex; flex-direction: column; gap: var(--space-2); min-width: 280px; }
.race-row { display: flex; flex-direction: column; gap: 2px; }
.race-row-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-1);
}
.race-row-title { font-size: var(--type-xs); font-weight: 600; }
.race-row-expect { font-size: var(--type-xs); margin-left: auto; }
/* The horizontal time axis for one stage: the shaded band + positioned chips live here. */
.race-axis {
    position: relative;
    min-height: 84px;
    border: 1px solid var(--bs-border-color);
    border-radius: 8px;
    background: var(--bs-tertiary-bg);
    padding: var(--space-1);
    overflow: hidden;
}
/* The 0→expected "on-track" band: the axis's left half (the scale is 0→2×expected). */
.race-band {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 50%;
    background: var(--bs-success-bg-subtle);
    border-right: 1px dashed var(--bs-success-border-subtle);
    pointer-events: none;
}
/* Hatched past-2×-expected "parked" zone at the axis's right edge. */
.race-axis::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;
    width: 13%;
    background: repeating-linear-gradient(45deg, var(--bs-border-color) 0 3px, transparent 3px 7px);
    opacity: 0.55;
    pointer-events: none;
}
/* Scale readout under the axis: left edge 0d, right edge the 2×expected span. */
.race-row-foot {
    display: flex;
    justify-content: space-between;
    font-size: var(--type-xs);
    color: var(--text-muted);
}
.race-chip {
    position: absolute;
    top: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 30px;
    height: 24px;
    padding: 0 6px;
    border-radius: 999px;
    border: 1px solid var(--bs-border-color);
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
    font-size: var(--type-xs);
    font-weight: 600;
    line-height: 1;
    text-decoration: none;
    z-index: 1;
}
a.race-chip:hover { border-color: var(--bs-primary); color: var(--bs-link-color); z-index: 3; }
/* Vertical jitter lanes so chips at similar positions read side by side, not stacked. */
.race-jit-1 { top: 28px; }
.race-jit-2 { top: 56px; }
/* Estimated (backfilled ≈) anchor: the hollow/dashed variant — same convention as the
   funnel's other ≈ markers. */
.race-chip-estimated { background: transparent; border-style: dashed; }
/* Parked (past 2× the stage's expected time): the distinct against-the-wall marker,
   danger-tinted via the color-mode subtle variables. */
.race-chip-parked {
    border-color: var(--bs-danger-border-subtle);
    background: var(--bs-danger-bg-subtle);
    color: var(--bs-danger-text-emphasis);
    z-index: 2;
}
.race-chip-more {
    left: auto;
    right: 0;
    top: auto;
    bottom: 0;
    background: transparent;
    border-style: dashed;
    color: var(--text-muted);
}
/* Static chips (the no-data row) flow and wrap instead of positioning. */
.race-chips-static { display: flex; flex-wrap: wrap; gap: 4px; align-content: flex-start; }
.race-chip-static { position: static; }
/* The "no timing data" row: members with no qualifying anchor event, grouped rather than
   fake-positioned — no axis, just a wrapped chip cluster. */
.race-row-nodata .race-chips-static { padding-top: 2px; }
/* Inline no-data cluster inside a per-board timing row (item 1): a labelled wrapped chip strip,
   for the bin's members with no derivable date. */
.race-nodata-cluster { align-items: center; padding-top: 2px; }

/* ── Per-board timing companion (journey funnel) ───────────────────
   Owner ruling 2026-07-18 (item 1): the retired standalone race-track
   card's view moved UNDER each path board — a nested, collapsible
   <details> reusing the .race-* row/axis/chip styles above. Subtly
   inset from the columns so it reads as "the timing for THIS board".
   Token/Bootstrap-variable driven — dark mode needs no override. */
.board-timing-details {
    margin-top: var(--space-2);
    border-top: 1px dashed var(--bs-border-color);
    padding-top: var(--space-2);
}
.board-timing-summary {
    cursor: pointer;
    font-weight: 600;
    font-size: var(--type-sm);
    color: var(--text-muted);
    min-height: var(--tap-min);
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

/* ── Clearance strip (My Journey) ──────────────────────────────────
   Two-axis redesign (spec §6.5): the "what am I cleared to do" summary
   that opens My Journey — six ride/drive chips (Engine/Ambulance/
   Squad), each CLEARED / IN_PROGRESS / NOT_CLEARED. Not-cleared chips
   are the SAME visual weight as cleared ones (owner directive:
   "highlight what they aren't cleared to do") — color is the only
   signal, never size or opacity. Chips wrap; each is a >=44px tap
   target since non-cleared ones link to the unlocking stage further
   down the page. Bootstrap 5.3 subtle color-mode variables throughout,
   so dark mode needs no separate override. */
.clearance-strip { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.clearance-chip {
    display: inline-flex;
    flex-direction: column;
    justify-content: center;
    min-height: var(--tap-min);
    padding: 6px 14px;
    border-radius: 10px;
    border: 1px solid var(--bs-border-color);
    text-decoration: none;
    font-size: var(--type-sm);
    font-weight: 600;
    line-height: 1.3;
}
.clearance-chip-label { display: flex; align-items: center; gap: 6px; }
.clearance-chip-hint { font-weight: 400; font-size: var(--type-xs); opacity: 0.85; }
.clearance-chip-cleared {
    border-color: var(--bs-success-border-subtle);
    background: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
}
.clearance-chip-progress {
    border-color: var(--bs-primary-border-subtle);
    background: var(--bs-primary-bg-subtle);
    color: var(--bs-primary-text-emphasis);
}
.clearance-chip-not-cleared {
    border-color: var(--bs-secondary-border-subtle);
    background: var(--bs-secondary-bg-subtle);
    color: var(--bs-secondary-text-emphasis);
}
a.clearance-chip-progress:hover,
a.clearance-chip-not-cleared:hover { border-color: var(--bs-primary); }

/* Clearance GRID (owner directive 2026-07-18): one row per apparatus, its
   ride + drive cells side by side. Layout-only wrapper around the same
   .clearance-chip states above, so their Bootstrap 5.3 subtle color-mode
   variables carry dark mode here with no extra override. Two columns hold
   at 375px (the chips wrap their own text and keep the 44px tap target). */
.journey-clearance-grid { display: flex; flex-direction: column; gap: var(--space-3); }
.journey-clearance-grid-apparatus {
    font-weight: 600;
    font-size: var(--type-sm);
    margin-bottom: var(--space-1);
}
.journey-clearance-grid-cells {
    display: grid;
    /* minmax(0, 1fr), not 1fr: the 1fr default floors each track at its content's
       min-content, so the verbose ride/drive labels would expand the tracks past the
       viewport at 375px. minmax(0, …) pins both columns to an exact half and lets the
       chip text wrap inside instead of overflowing the page. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--space-2);
}
.journey-clearance-grid-cells .clearance-chip { width: 100%; min-width: 0; }
.journey-clearance-grid-cells .clearance-chip-label { min-width: 0; }

/* ── Activation goal sections (My Journey stage rows) ──────────────
   Each stage's gating requirements, split by axis: Qualifications
   (county certs — earned, permanent), Clearance (station process —
   resettable), Standing (rank/tenure/age). Display-only grouping of
   the same rows the stage already gates on. */
.journey-req-section { margin-top: var(--space-2); }
.journey-req-heading {
    font-size: var(--type-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    margin-bottom: 2px;
}
.journey-req-list li { padding: 1px 0; }
/* Jumping here from a clearance-strip chip should clear the sticky nav. */
[id^="stage-"] { scroll-margin-top: 72px; }

/* ==================================================================
   DARK MODE — component surfaces (Bootstrap 5.3 color modes)
   Activated by data-bs-theme="dark" on <html> (set by the /private/
   theme toggle in private_base.html; public pages never set the
   attribute, so everything below is inert on the public site).
   Token-level dark values live next to their light definitions (the
   [data-bs-theme="dark"] body block at the top of this file, plus
   tokens.css). This section re-skins the component surfaces above
   that hardcode light colors; Bootstrap's own dark mode already
   covers cards/tables/forms/buttons. Keep additions grouped per
   component with a comment. In LIGHT mode nothing here applies.
   ================================================================== */

/* — Base text: form error text on the dark navy backdrop. (Headings inherit
     the dark body color now that the legacy light-mode h1/h2 colors are gone;
     the old #E05C5C h1 / #C7D0DB h3 counter-overrides went with them.) — */
[data-bs-theme="dark"] ul.errorlist { color: #EA868F; }

/* — Bootstrap utility remaps. `.bg-light` keeps its LIGHT background under
     data-bs-theme="dark" (Bootstrap does not flip it) while the text inside
     inherits the light dark-mode body color — unreadable. Remap the surface
     to the dark tertiary surface EXCEPT when the element pairs it with
     .text-dark (badge recipes like "badge bg-light text-dark border"), which
     stay a deliberate light-chip-with-dark-text in both modes, matching the
     .text-bg-light badges we leave alone. */
[data-bs-theme="dark"] .bg-light:not(.text-dark) {
    background-color: var(--bs-tertiary-bg) !important;
}
/* `.bg-white` likewise stays literally white under dark mode (membership
   dashboard card headers). The one deliberate white-in-both-modes surface is
   the campaign QR code <img> — a QR needs its white quiet zone to scan — so
   images are excluded. */
[data-bs-theme="dark"] .bg-white:not(img) {
    background-color: var(--bs-tertiary-bg) !important;
}
/* `.table-light` header rows likewise stay light in dark mode; remap the
   table-variant vars to the dark surface palette. */
[data-bs-theme="dark"] .table-light {
    --bs-table-color: var(--bs-body-color);
    --bs-table-bg: var(--bs-tertiary-bg);
    --bs-table-border-color: var(--bs-border-color);
    --bs-table-striped-bg: var(--bs-tertiary-bg);
    --bs-table-striped-color: var(--bs-body-color);
    --bs-table-active-bg: var(--bs-tertiary-bg);
    --bs-table-active-color: var(--bs-body-color);
    --bs-table-hover-bg: var(--bs-tertiary-bg);
    --bs-table-hover-color: var(--bs-body-color);
}

/* — Mini month calendar — */
[data-bs-theme="dark"] .mini-cal-nav { color: #A9B4C0; }
[data-bs-theme="dark"] .mini-cal-nav:hover,
[data-bs-theme="dark"] .mini-cal-day:not(.mini-cal-active):not(.mini-cal-blank):hover {
    background-color: #232F44;
}
[data-bs-theme="dark"] .mini-cal-dow { color: #8996A5; }
[data-bs-theme="dark"] .mini-cal-day:not(.mini-cal-active) { color: #C7D0DB; }
[data-bs-theme="dark"] .mini-cal-today { outline-color: #C7D0DB; }

/* — Duty calendar: today column, empty cells, legend —
   (posture-tinted today cells keep their posture color, same as light) */
[data-bs-theme="dark"] .duty-calendar th.today-col,
[data-bs-theme="dark"] .duty-calendar td.today-col:not([class*="posture-level-"]) {
    background-color: rgba(110, 168, 254, 0.10);
}
[data-bs-theme="dark"] .duty-calendar td .cell-empty,
[data-bs-theme="dark"] .mobile-position-content .cell-empty { color: #55627A; }
[data-bs-theme="dark"] .schedule-legend { color: var(--text-muted); }
[data-bs-theme="dark"] .schedule-legend .legend-label { color: #B6C0CB; }

/* — Collapsible shift group headers (bg comes from --accent-blue-light) — */
[data-bs-theme="dark"] .shift-group-header:hover { background-color: #232F44; }
[data-bs-theme="dark"] .shift-group-header td { border-top-color: var(--bs-border-color); }

/* — Inline sign-up button: hover flips to the lightened accent bg, so the
     label must go dark there (white-on-#6EA8FE fails contrast) — */
[data-bs-theme="dark"] .signup-inline-btn:hover { color: #0E1622; }

/* — Officer Mode action queue (pale amber panel in light) — */
[data-bs-theme="dark"] .officer-action-queue {
    background: #2E2812;
    border-color: #5C4E1E;
}
[data-bs-theme="dark"] .action-queue-item { border-bottom-color: #4A3F1A; }

/* — Gap Filler panel (header bg comes from --accent-blue-light) — */
[data-bs-theme="dark"] .gap-filler-panel {
    background: #1A2230;
    border-color: var(--bs-border-color);
}
[data-bs-theme="dark"] .gap-filler-header { border-bottom-color: var(--bs-border-color); }
[data-bs-theme="dark"] .gap-filler-slot { border-bottom-color: #2A3547; }
[data-bs-theme="dark"] .gap-filler-slot-header { color: #B6C0CB; }
[data-bs-theme="dark"] .gap-filler-position-label { color: var(--text-muted); }

/* — Crew chip-density grid. The saturated role-chip fills (.cs-chip-*) are
     identical in both modes; only the muted-grey text/affordances lighten and
     the white sign-up pill gets a dark equivalent. Cell washes come from the
     dark --posture-wash-* tokens at the top of the file. — */
[data-bs-theme="dark"] .cs-grid thead th { color: var(--text-muted); }
[data-bs-theme="dark"] .cs-grid th.cs-shift-label { color: #DDE3EA; }
[data-bs-theme="dark"] .cs-pbadge { color: #C7D0DB; }
[data-bs-theme="dark"] .cs-chip-observer { color: #B0BAC5; border-color: #6C7889; }
[data-bs-theme="dark"] .cs-chip.cs-me { box-shadow: 0 0 0 2px #8FC0FF; }
[data-bs-theme="dark"] .cs-chip.cs-pending { color: #E3B341; }
[data-bs-theme="dark"] .cs-more,
[data-bs-theme="dark"] .cs-cell-footer { color: var(--text-muted); }
[data-bs-theme="dark"] .cs-add { color: #7E8CA0; }
[data-bs-theme="dark"] .cs-signup-btn {
    background: #101B2E;
    color: #8FB4FE;
    border-color: #3D5A8F;
}
[data-bs-theme="dark"] .cs-signup-btn:hover { background: #1D2C49; }

/* — Crew roster peek / hover pop-up — */
[data-bs-theme="dark"] .peek-lane-label { color: var(--text-muted); }
[data-bs-theme="dark"] .peek-row { border-bottom-color: #263041; }
[data-bs-theme="dark"] .peek-pos { color: #C7D0DB; background: #2A3547; }
[data-bs-theme="dark"] .cs-hovercard-head {
    color: #C7D0DB;
    border-bottom-color: var(--bs-border-color);
}

/* — Mobile crew cards — */
[data-bs-theme="dark"] .cs-mcard { border-color: var(--bs-border-color); }
[data-bs-theme="dark"] .cs-mcard-head { border-bottom-color: rgba(255, 255, 255, 0.10); }

/* — "Your week" hero (bg comes from --accent-blue-light) — */
[data-bs-theme="dark"] .myweek-hero { border-color: var(--accent-blue-border); }
[data-bs-theme="dark"] .myweek-title { color: #8FA7FF; }
[data-bs-theme="dark"] .myweek-chip { background: #101B2E; border-color: #35507F; }
[data-bs-theme="dark"] .myweek-chip strong { color: #A9C3FF; }
[data-bs-theme="dark"] .myweek-nudge { color: #B6C0CB; }
[data-bs-theme="dark"] .myweek-nudge i { color: #8FA7FF; }
[data-bs-theme="dark"] .myweek-ring {
    background: conic-gradient(#2fb344 calc(var(--pct) * 1%), #2A3A52 0);
}
[data-bs-theme="dark"] .myweek-ring-inner { background: #131C2B; }
[data-bs-theme="dark"] .myweek-ring-num { color: #DDE3EA; }
[data-bs-theme="dark"] .myweek-ring-den { color: var(--text-muted); }
[data-bs-theme="dark"] .myweek-meta { color: #B6C0CB; }

/* — Invite autocomplete — */
[data-bs-theme="dark"] .invite-search-results {
    background: #1A2230;
    border-color: var(--bs-border-color);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
}
[data-bs-theme="dark"] .invite-tag { background: #2A3547; }
[data-bs-theme="dark"] .invite-tag-remove { color: var(--text-muted); }
[data-bs-theme="dark"] .invite-tag-remove:hover { color: #EA868F; }

/* — Shift entry list bits — */
[data-bs-theme="dark"] .shift-entry + .shift-entry { border-top-color: #2A3547; }
[data-bs-theme="dark"] .shift-notes { color: var(--text-muted); }

/* — A+ treatment (2026-07-12) additions — */
[data-bs-theme="dark"] .shift-pending-badge { background-color: #3A3222; color: #E3BE66; }
[data-bs-theme="dark"] .cell-approve-link { border-color: #4ADE80; color: #4ADE80; }
[data-bs-theme="dark"] .cell-approve-link:hover { background-color: #1F5230; color: #E6F4EA; }
[data-bs-theme="dark"] .myweek-next { color: #B6C0CB; }
[data-bs-theme="dark"] .myweek-next i { color: #8FA7FF; }
[data-bs-theme="dark"] .myweek-chip-station { background: #253453; color: #A9C3FF; }
[data-bs-theme="dark"] .cs-signup-btn { color: #93A8BD; }
[data-bs-theme="dark"] .cs-cell:hover .cs-signup-btn,
[data-bs-theme="dark"] .cs-signup-btn:hover,
[data-bs-theme="dark"] .cs-signup-btn:focus-visible,
[data-bs-theme="dark"] .cs-mcard .cs-signup-btn {
    background: #101B2E;
    color: #A9C3FF;
    border-color: #35507F;
}
[data-bs-theme="dark"] .readiness-segment { background-color: #1A2230; }

/* — Mobile card schedule (day tabs + shift cards) — */
[data-bs-theme="dark"] .mobile-day-tabs { border-bottom-color: var(--bs-border-color); }
[data-bs-theme="dark"] .mobile-day-tabs .day-tab { color: var(--text-muted); }
[data-bs-theme="dark"] .mobile-day-tabs .day-tab.active {
    color: #E05C5C;
    border-bottom-color: #E05C5C;
}
[data-bs-theme="dark"] .mobile-shift-card { border-color: var(--bs-border-color); }
[data-bs-theme="dark"] .mobile-shift-card-header { border-bottom-color: var(--bs-border-color); }
[data-bs-theme="dark"] .mobile-position-row { border-bottom-color: #263041; }
[data-bs-theme="dark"] .mobile-position-label { color: #B6C0CB; }

/* — Colored stat boxes (blue box bg/border come from the accent tokens) — */
[data-bs-theme="dark"] .stat-box-blue .stat-label { color: #A9C3FF; }
[data-bs-theme="dark"] .stat-box-green { background-color: #12291A; border-color: #1F5230; }
[data-bs-theme="dark"] .stat-box-green .stat-value { color: #4ADE80; }
[data-bs-theme="dark"] .stat-box-green .stat-label { color: #86EFAC; }
[data-bs-theme="dark"] .stat-box-red { background-color: #2C171B; border-color: #5A2830; }
[data-bs-theme="dark"] .stat-box-red .stat-value { color: #F87171; }
[data-bs-theme="dark"] .stat-box-red .stat-label { color: #F1A8A8; }

/* — Select2 (loaded per-page from a CDN AFTER this file; the
     [data-bs-theme] prefix out-specifies its default theme). Colors mirror
     Bootstrap 5.3's dark form controls so mixed forms look uniform. — */
[data-bs-theme="dark"] .select2-container--default .select2-selection--single,
[data-bs-theme="dark"] .select2-container--default .select2-selection--multiple {
    background-color: var(--bs-body-bg);
    border-color: var(--bs-border-color);
}
[data-bs-theme="dark"] .select2-container--default .select2-selection--single .select2-selection__rendered {
    color: var(--bs-body-color);
}
[data-bs-theme="dark"] .select2-container--default .select2-selection--single .select2-selection__placeholder,
[data-bs-theme="dark"] .select2-container--default .select2-selection--multiple .select2-selection__placeholder {
    color: var(--text-muted);
}
[data-bs-theme="dark"] .select2-container--default .select2-selection--multiple .select2-selection__choice {
    background-color: var(--bs-tertiary-bg);
    border-color: var(--bs-border-color);
    color: var(--bs-body-color);
}
[data-bs-theme="dark"] .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
    color: var(--text-muted);
}
[data-bs-theme="dark"] .select2-container--default.select2-container--disabled .select2-selection--single,
[data-bs-theme="dark"] .select2-container--default.select2-container--disabled .select2-selection--multiple {
    background-color: var(--bs-secondary-bg);
}
[data-bs-theme="dark"] .select2-dropdown {
    background-color: var(--bs-body-bg);
    border-color: var(--bs-border-color);
    color: var(--bs-body-color);
}
[data-bs-theme="dark"] .select2-container--default .select2-search--dropdown .select2-search__field,
[data-bs-theme="dark"] .select2-container--default .select2-search--inline .select2-search__field {
    background-color: var(--bs-body-bg);
    border-color: var(--bs-border-color);
    color: var(--bs-body-color);
}
/* Non-highlighted "already selected" option (the highlight itself stays
   Select2's saturated blue — readable in both modes). */
[data-bs-theme="dark"] .select2-container--default .select2-results__option[aria-selected="true"] {
    background-color: var(--bs-tertiary-bg);
}

/* — FullCalendar 6 (station calendar). FC exposes --fc-* custom properties;
     setting them ON .fc beats the :root defaults from its CDN stylesheet
     regardless of load order. No JS re-init needed. — */
[data-bs-theme="dark"] .fc {
    --fc-page-bg-color: #0E1622;  /* match the dark body backdrop (opaque: list view
                                     slides rows under sticky day headers) */
    --fc-neutral-bg-color: #1A2230;
    --fc-neutral-text-color: #9DA8B3;
    --fc-border-color: #2A3A52;
    --fc-today-bg-color: rgba(110, 168, 254, 0.14);
    --fc-highlight-color: rgba(110, 168, 254, 0.20);
    --fc-non-business-color: rgba(255, 255, 255, 0.03);
    --fc-list-event-hover-bg-color: #1D2C49;
    --fc-more-link-bg-color: #2A3547;
    --fc-more-link-text-color: #DDE3EA;
}

/* ── Station calendar page (stationcalendar/calendar.html) ──
   Layout: month grid in a card with an Up Next + filter rail. The rail sits
   beside the grid ≥ lg and stacks Up Next → calendar → filters below it
   (source order is the mobile order). */
#calendar-layout {
    display: grid;
    gap: 1rem;
    grid-template-columns: minmax(0, 1fr) 300px;
    grid-template-rows: auto auto 1fr;
    grid-template-areas:
        "main upnext"
        "main filter"
        "main .";
    align-items: start;
}
#calendar-main { grid-area: main; }
.upnext-card  { grid-area: upnext; }
.filter-card  { grid-area: filter; }
@media (max-width: 991.98px) {
    /* align-items must revert to stretch: the grid's `start` would otherwise
       shrink each card to its content width in the stacked column. */
    #calendar-layout { display: flex; flex-direction: column; align-items: stretch; }
}
#calendar-main > .card-body { padding: 0.75rem; }

/* FC chrome, both themes: quiet the link styling FC puts on day numbers and
   weekday headers, give the toolbar the display face, restyle FC's buttons
   toward the site's neutral/accent language via FC's own custom properties. */
.fc {
    --fc-button-text-color: var(--bs-body-color);
    --fc-button-bg-color: var(--bs-tertiary-bg);
    --fc-button-border-color: var(--bs-border-color);
    --fc-button-hover-bg-color: var(--bs-secondary-bg);
    --fc-button-hover-border-color: var(--bs-border-color);
    --fc-button-active-bg-color: var(--accent-blue, #0d6efd);
    --fc-button-active-border-color: var(--accent-blue, #0d6efd);
    --fc-event-border-color: transparent;
}
.fc a { text-decoration: none; }
.fc .fc-button-primary:not(:disabled).fc-button-active,
.fc .fc-button-primary:not(:disabled):active { color: #fff; }
.fc .fc-toolbar-title {
    font-family: 'Barlow Condensed', 'Montserrat', system-ui, sans-serif;
    font-weight: 600;
    letter-spacing: 0.02em;
    font-size: 1.55rem;
}
.fc .fc-button { font-size: 0.85em; }
.fc .fc-col-header-cell-cushion {
    color: var(--text-muted, var(--bs-secondary-color));
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 6px 4px;
}
.fc .fc-daygrid-day-number {
    color: var(--bs-body-color);
    font-weight: 600;
    font-size: 0.85rem;
    padding: 4px 6px;
}
.fc .fc-day-other .fc-daygrid-day-number { opacity: 0.45; }
.fc .fc-day-today .fc-daygrid-day-number {
    background: var(--accent-blue, #0d6efd);
    color: #fff;
    border-radius: 999px;
    margin: 2px;
    padding: 2px 7px;
}

/* Event blocks: readable colored chips. Text color arrives per event from the
   API (luminance-derived); the dark-mode inset ring keeps near-black category
   colors (prod's Meeting navy) visible against the dark page. */
.fc .fc-daygrid-event {
    border-radius: 4px;
    font-size: 0.78rem;
    line-height: 1.3;
}
.fc .fc-daygrid-block-event .fc-event-title { padding: 1px 4px; font-weight: 500; }
.fc .fc-h-event .fc-event-title-container { padding: 0 2px; }
[data-bs-theme="dark"] .fc .fc-daygrid-event,
[data-bs-theme="dark"] .fc .fc-timegrid-event {
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.16);
}

/* Stack the FC toolbar on phones (prev/next, title, views each on a row). */
@media (max-width: 767.98px) {
    .fc .fc-toolbar.fc-header-toolbar {
        flex-direction: column;
        align-items: stretch;
        gap: 0.5rem;
        margin-bottom: 1rem;
    }
    .fc .fc-toolbar-chunk { display: flex; justify-content: center; }
    .fc .fc-toolbar-title { font-size: 1.3rem; text-align: center; }
    .fc .fc-button { font-size: 0.95em; padding: 0.5em 0.7em; }
}

/* Filter rail rows: whole row is the checkbox label. */
.legend-filter-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0;
    margin: 0;
    cursor: pointer;
    font-size: 0.9rem;
}
.legend-filter-row .form-check-input { margin: 0; flex: none; }
.legend-filter-name { min-width: 0; }
.legend-filter-row.is-hidden-key .legend-filter-name {
    text-decoration: line-through;
    opacity: 0.55;
}
.legend-swatch {
    display: inline-block;
    width: 14px;
    height: 14px;
    border-radius: 4px;
    flex: none;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2);
}
[data-bs-theme="dark"] .legend-swatch { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.28); }
.cal-dot {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2);
}
[data-bs-theme="dark"] .cal-dot { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.28); }

/* Event preview popover (fixed-position card; bottom sheet on phones). */
.cal-popover {
    position: fixed;
    z-index: 1070;
    width: 320px;
    max-width: calc(100vw - 24px);
    background: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: 0.5rem;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
    padding: 0.75rem 0.9rem;
}
[data-bs-theme="dark"] .cal-popover { box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55); }
/* The container takes programmatic focus (keyboard context for the dialog);
   the browser focus ring stays on the buttons inside, not the card itself. */
.cal-popover:focus { outline: none; }
.cal-popover-sm { width: auto; padding: 0.5rem; }
.cal-popover-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.25rem;
    min-height: 1.25rem;
}
.cal-popover-title { margin: 0 0 0.4rem; font-weight: 600; }
.cal-popover-meta {
    display: flex;
    gap: 0.45rem;
    align-items: baseline;
    font-size: 0.85rem;
    color: var(--text-muted, var(--bs-secondary-color));
    margin-bottom: 0.2rem;
}
.cal-popover-desc {
    font-size: 0.85rem;
    margin: 0.5rem 0 0;
    white-space: pre-line;
}
.cal-popover-actions { display: flex; gap: 0.5rem; margin-top: 0.75rem; }
.cal-popover-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.35);
    z-index: 1065;
}
@media (max-width: 767.98px) {
    .cal-popover:not(.cal-popover-sm) {
        left: 12px !important;
        right: 12px;
        top: auto !important;
        bottom: calc(12px + env(safe-area-inset-bottom));
        width: auto;
        max-height: 70vh;
        overflow: auto;
    }
}

/* — Highcharts (report pages): the SVG paints a white background rect and
     inline #333 label styles that ignore the page theme. Fill overrides need
     !important to beat Highcharts' inline element styles. — */
[data-bs-theme="dark"] .highcharts-background { fill: transparent; }
[data-bs-theme="dark"] .highcharts-title,
[data-bs-theme="dark"] .highcharts-subtitle,
[data-bs-theme="dark"] .highcharts-axis-title,
[data-bs-theme="dark"] .highcharts-axis-labels text,
[data-bs-theme="dark"] .highcharts-legend-item text {
    fill: #C7D0DB !important;
    color: #C7D0DB !important;
}
[data-bs-theme="dark"] .highcharts-grid-line { stroke: #2A3A52; }
[data-bs-theme="dark"] .highcharts-axis-line { stroke: #3A4A62; }
[data-bs-theme="dark"] .highcharts-tick { stroke: #3A4A62; }
[data-bs-theme="dark"] .highcharts-tooltip-box { fill: #1A2230; stroke: #2A3A52; }
[data-bs-theme="dark"] .highcharts-tooltip text { fill: #DDE3EA !important; }

/* ── Duty grid posture CELLS in dark mode ──
   The saturated --posture-0..5 badge ramp is pinned to LEVEL_COLORS and never
   forked per theme (see the token comment + test_dark_mode_contract) — but the
   grid CELLS wearing the pale low end (L0 baseline grey, L1/L2 pastels) as
   full backgrounds glare on a dark page. Remap just those cells onto the dark
   wash ramp; the legend badges keep the canonical colors as the color key, and
   the saturated L3–L5 cells already read correctly on dark. */
[data-bs-theme="dark"] .duty-calendar td.posture-level-0,
[data-bs-theme="dark"] .duty-calendar td.today-col.posture-level-0 {
    background-color: #2B3035;
    color: var(--text-muted);
}
[data-bs-theme="dark"] .duty-calendar td.posture-level-1,
[data-bs-theme="dark"] .duty-calendar td.today-col.posture-level-1 {
    background-color: var(--posture-wash-1);
    color: #B7DFC0;
}
[data-bs-theme="dark"] .duty-calendar td.posture-level-2,
[data-bs-theme="dark"] .duty-calendar td.today-col.posture-level-2 {
    background-color: var(--posture-wash-4);
    color: #C9EBD1;
}
/* #714: the light-mode content rules pin anchors to #155724 and sign-up
   buttons to #1E40AF — both near-invisible (~1.2-1.6:1) on the dark washes
   the cells above just remapped to. Match each level's own text ramp for
   links (>=7:1) and use the dark accent for the sign-up affordance (~6.6:1).
   L3-5 keep their light-mode white content rules — those cells are the same
   saturated greens in both themes. */
[data-bs-theme="dark"] .duty-calendar td.posture-level-0 a { color: var(--text-muted); }
[data-bs-theme="dark"] .duty-calendar td.posture-level-1 a { color: #B7DFC0; }
[data-bs-theme="dark"] .duty-calendar td.posture-level-2 a { color: #C9EBD1; }
[data-bs-theme="dark"] .duty-calendar td.posture-level-0 .signup-inline-btn,
[data-bs-theme="dark"] .duty-calendar td.posture-level-1 .signup-inline-btn {
    color: var(--accent-blue);
    border-color: var(--accent-blue);
}
/* L2's wash (--posture-wash-4) is lighter — the dark accent lands at 4.19:1
   there; the hover stop #8BB9FE clears AA (4.5+). */
[data-bs-theme="dark"] .duty-calendar td.posture-level-2 .signup-inline-btn {
    color: #8BB9FE;
    border-color: #8BB9FE;
}
