        /* ================================================================
           DESIGN SYSTEM
           ================================================================ */
        *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

        :root {
            --bg: #060a13;
            --surface: #0d1321;
            --surface-2: #141c2e;
            --surface-3: #1a2540;
            /* Translucent, not an opaque colour: row hover must composite
               harmlessly over light document surfaces (contract preview, signed
               viewer, invoice paper), not just the dark shell. Lands on ~#171d2b
               over --surface, matching the old opaque --surface-2 lift. */
            --row-hover: rgba(255, 255, 255, 0.043);
            --border: #1c2a42;
            --border-subtle: #152035;
            --text: #e8edf5;
            --text-secondary: #8b9dc3;
            /* Lifted from #4a5e80 (~2.8:1) to pass WCAG AA (≥4.5:1) on every dark
               surface, while staying dimmer than --text-secondary. Audit UX-H6. */
            --text-muted: #7a8fb0;
            --accent: #3b82f6;
            --accent-dim: #2563eb;
            --accent-glow: rgba(59, 130, 246, 0.15);
            --green: #22c55e;
            --green-dim: rgba(34, 197, 94, 0.12);
            --amber: #f59e0b;
            --amber-dim: rgba(245, 158, 11, 0.12);
            --red: #ef4444;
            --red-dim: rgba(239, 68, 68, 0.12);
            --blue: #3b82f6;
            --blue-dim: rgba(59, 130, 246, 0.12);
            --purple: #a855f7;
            --purple-dim: rgba(168, 85, 247, 0.12);
            --radius: 10px;
            --radius-lg: 16px;
            --sidebar-w: 240px;
            --topbar-h: 56px;
            --transition: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
            /* z-index scale — unifies the ad-hoc 1000/1005/1100/9999 mix. */
            --z-modal: 1005;
            --z-modal-stacked: 1100;
            --z-viewer: 9000;
            --z-toast: 10000;
        }

        html, body { height: 100%; overflow: hidden; }

        body {
            font-family: 'Inter', -apple-system, sans-serif;
            background: var(--bg);
            color: var(--text);
            display: flex;
            -webkit-tap-highlight-color: transparent;
            -webkit-touch-callout: none;
        }

        /* Keyboard focus visibility (audit UX — :focus-visible). :focus-visible
           fires only for keyboard navigation, so mouse/touch taps stay clean. */
        :focus-visible {
            outline: 2px solid var(--accent);
            outline-offset: 2px;
        }

        /* ================================================================
           SIDEBAR
           ================================================================ */
        .sidebar {
            width: var(--sidebar-w);
            height: 100%;
            background: var(--surface);
            border-right: 1px solid var(--border);
            display: flex;
            flex-direction: column;
            flex-shrink: 0;
            z-index: 10;
        }

        .sidebar-logo {
            padding: 20px 24px;
            border-bottom: 1px solid var(--border);
        }

        .sidebar-logo h1 {
            font-size: 20px;
            font-weight: 800;
            letter-spacing: -0.5px;
        }

        .sidebar-logo h1 span { color: var(--accent); }

        .sidebar-logo p {
            font-size: 11px;
            color: var(--text-muted);
            margin-top: 2px;
            text-transform: uppercase;
            letter-spacing: 1px;
        }

        .sidebar-nav {
            flex: 1;
            padding: 12px;
            overflow-y: auto;
        }

        .nav-item {
            display: flex;
            align-items: center;
            gap: 12px;
            padding: 12px 14px;
            border-radius: var(--radius);
            color: var(--text-secondary);
            font-size: 14px;
            font-weight: 500;
            cursor: pointer;
            transition: all var(--transition);
            text-decoration: none;
            margin-bottom: 2px;
        }

        .nav-item:hover { background: var(--surface-2); color: var(--text); }
        .nav-item.active { background: var(--accent-glow); color: var(--accent); font-weight: 600; }
        .nav-item .icon { font-size: 18px; width: 24px; text-align: center; }

        .nav-section {
            font-size: 10px;
            font-weight: 600;
            text-transform: uppercase;
            letter-spacing: 1.5px;
            color: var(--text-muted);
            padding: 16px 14px 8px;
        }

        .sidebar-footer {
            padding: 16px;
            border-top: 1px solid var(--border);
        }

        .app-version {
            font-size: 11px;
            font-weight: 500;
            color: var(--text-muted);
            margin-bottom: 8px;
            letter-spacing: 0.5px;
            opacity: 0.7;
            display: flex;
            align-items: center;
            gap: 6px;
        }

        .backup-badge {
            font-size: 13px;
            line-height: 1;
            cursor: help;
            opacity: 0.9;
            /* iPad: ensure the dots register as an interactive tap target */
            min-width: 44px;
            min-height: 22px;
            display: inline-flex;
            align-items: center;
            gap: 6px;
        }
        .backup-badge-hidden { display: none; }
        .backup-dot-wrap {
            display: inline-flex;
            align-items: center;
            gap: 2px;
        }
        .backup-dot-label {
            font-size: 9px;
            font-weight: 600;
            color: var(--text-muted, #9ca3af);
            text-transform: uppercase;
            letter-spacing: 0.3px;
        }
        .backup-dot {
            font-size: 14px;
            line-height: 1;
        }

        .user-info {
            display: flex;
            align-items: center;
            gap: 10px;
            font-size: 13px;
        }

        .user-avatar {
            width: 32px; height: 32px;
            border-radius: 8px;
            background: var(--accent-glow);
            color: var(--accent);
            display: flex; align-items: center; justify-content: center;
            font-weight: 700;
            font-size: 13px;
        }

        .user-name { font-weight: 600; }
        .user-role { font-size: 11px; color: var(--text-muted); }

        /* ================================================================
           MAIN CONTENT
           ================================================================ */
        .main { flex: 1; display: flex; flex-direction: column; overflow: hidden; }

        .topbar {
            height: var(--topbar-h);
            background: var(--surface);
            border-bottom: 1px solid var(--border);
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 0 24px;
            flex-shrink: 0;
        }

        .topbar-title {
            font-size: 16px;
            font-weight: 600;
        }

        .topbar-actions { display: flex; gap: 10px; align-items: center; }

        .btn {
            display: inline-flex;
            align-items: center;
            gap: 8px;
            padding: 10px 18px;
            border-radius: var(--radius);
            font-size: 13px;
            font-weight: 600;
            font-family: inherit;
            cursor: pointer;
            border: none;
            transition: all var(--transition);
            /* Audit 2026-07-23 (I): Apple HIG minimum tap target is 44×44px. The app
               is used on iPad in the field, so every button floors at 44×44. The
               min-width/min-height also override the small inline width/height on the
               photo-tile ✕/🔍/Retake overlays (24px/20px) so they meet the target. */
            min-height: 44px;
            min-width: 44px;
        }

        .btn-primary { background: var(--accent); color: #fff; }
        .btn-primary:hover { box-shadow: 0 0 20px var(--accent-glow); }
        .btn-primary:active { transform: scale(0.97); }
        .btn-ghost { background: transparent; color: var(--text-secondary); border: 1px solid var(--border); }
        .btn-ghost:hover { background: var(--surface-2); color: var(--text); }
        .btn-sm { padding: 8px 14px; font-size: 12px; min-height: 44px; min-width: 44px; }
        .btn-danger { background: var(--red); color: #fff; }
        /* Secondary (surface-filled, bordered) — replaces ~5 inline variants. */
        .btn-secondary { background: var(--surface); color: var(--text-secondary); border: 1px solid var(--border); }
        .btn-secondary:hover { background: var(--surface-2); color: var(--text); }
        /* Warning (amber) — workshop defer / "pay later" buttons. */
        .btn-warning { background: rgba(255,165,0,0.15); color: #e67e00; border: 1px solid rgba(255,165,0,0.3); }
        .btn-warning:hover { background: rgba(255,165,0,0.25); }
        /* Danger ghost — delete/remove outlines (uses --red token, not hex). */
        .btn-danger-ghost { background: transparent; color: var(--red); border: 1px solid var(--red); }
        .btn-danger-ghost:hover { background: rgba(239,68,68,0.1); }
        /* Full-width CTA — repeated ~8× inline before this. */
        .btn-block { width: 100%; padding: 12px; font-size: 14px; }
        /* Save-feedback states (driven by RFMS.withSaveFeedback) */
        .btn-saving { opacity: 0.75; cursor: wait; }
        .btn-saving:hover { box-shadow: none; }
        .btn-saved { background: var(--green, #2e7d32); color: #fff; cursor: default; }
        .btn-saved:hover { box-shadow: none; transform: none; }
        .btn-error { background: var(--red, #c62828); color: #fff; }
        .btn-error:hover { box-shadow: none; }
        /* D21 — ensure all interactive table-row buttons meet 44px touch target */
        td .btn-ghost.btn-sm, td .btn.btn-sm { min-height: 44px; min-width: 44px; display: inline-flex; align-items: center; justify-content: center; }

        .content {
            flex: 1;
            overflow-y: auto;
            padding: 24px;
            -webkit-overflow-scrolling: touch;
        }

        /* ================================================================
           CARDS & GRID
           ================================================================ */
        .grid { display: grid; gap: 16px; }
        .grid-4 { grid-template-columns: repeat(4, 1fr); }
        .grid-3 { grid-template-columns: repeat(3, 1fr); }
        .grid-2 { grid-template-columns: repeat(2, 1fr); }

        .card {
            background: var(--surface);
            border: 1px solid var(--border);
            border-radius: var(--radius-lg);
            padding: 20px;
        }

        .stat-card {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }

        .stat-label {
            font-size: 12px;
            font-weight: 500;
            color: var(--text-muted);
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }

        .stat-value {
            font-size: 28px;
            font-weight: 700;
            letter-spacing: -1px;
        }

        .stat-sub {
            font-size: 12px;
            color: var(--text-secondary);
        }

        /* ================================================================
           STATUS BADGES
           ================================================================ */
        .badge {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            padding: 4px 10px;
            border-radius: 6px;
            font-size: 11px;
            font-weight: 600;
            text-transform: uppercase;
            letter-spacing: 0.3px;
        }

        .badge-green { background: var(--green-dim); color: var(--green); }
        .badge-blue { background: var(--blue-dim); color: var(--blue); }
        .badge-amber { background: var(--amber-dim); color: var(--amber); }
        .badge-red { background: var(--red-dim); color: var(--red); }
        .badge-purple { background: var(--purple-dim); color: var(--purple); }
        .badge-muted { background: var(--surface-3); color: var(--text-muted); }

        /* Provenance pill — marks a record that came from a bulk import
           (e.g. DreamCarz). Informational, not a warning: muted palette so it
           sits quietly next to the name without drawing alarm. */
        .imported-pill {
            display: inline-block;
            margin-left: 6px;
            padding: 2px 7px;
            border-radius: 10px;
            font-size: 10px;
            font-weight: 600;
            letter-spacing: 0.3px;
            text-transform: uppercase;
            background: var(--surface-3);
            color: var(--text-muted);
            border: 1px solid var(--border);
            vertical-align: middle;
            line-height: 1.4;
        }

        /* ================================================================
           TABLE
           ================================================================ */
        .table-wrap {
            overflow-x: auto;
            -webkit-overflow-scrolling: touch;
        }

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

        th {
            text-align: left;
            padding: 10px 14px;
            color: var(--text-muted);
            font-weight: 600;
            font-size: 11px;
            text-transform: uppercase;
            letter-spacing: 0.5px;
            border-bottom: 1px solid var(--border);
            white-space: nowrap;
        }

        td {
            padding: 12px 14px;
            border-bottom: 1px solid var(--border-subtle);
            white-space: nowrap;
        }

        tr:hover td { background: var(--row-hover); }
        tr { cursor: pointer; transition: background var(--transition); }

        /* Contract document (wizard preview + signed viewer) is paper, not a list. */
        #contract-preview tr, #contract-preview tr:hover td, #contract-preview tr:hover th, #signed-doc tr, #signed-doc tr:hover td, #signed-doc tr:hover th { background: transparent; }
        #contract-preview tr, #signed-doc tr { cursor: default; }

        /* ================================================================
           CONTRACT WIZARD (5-STEP)
           ================================================================ */
        .wizard-steps {
            display: flex;
            gap: 4px;
            margin-bottom: 28px;
            padding: 0;
        }

        .wizard-step {
            flex: 1;
            padding: 14px 16px;
            background: var(--surface-2);
            border: 1px solid var(--border);
            border-radius: var(--radius);
            text-align: center;
            position: relative;
            transition: all var(--transition);
        }

        .wizard-step .step-num {
            font-size: 11px;
            font-weight: 700;
            color: var(--text-muted);
            margin-bottom: 4px;
        }

        .wizard-step .step-label {
            font-size: 12px;
            font-weight: 600;
            color: var(--text-secondary);
        }

        .wizard-step.active {
            background: var(--accent-glow);
            border-color: var(--accent);
        }

        .wizard-step.active .step-num { color: var(--accent); }
        .wizard-step.active .step-label { color: var(--text); }

        .wizard-step.done {
            background: var(--green-dim);
            border-color: rgba(34, 197, 94, 0.3);
            cursor: pointer;
        }

        .wizard-step.done:hover {
            border-color: var(--green);
            transform: translateY(-1px);
        }

        .wizard-step.done .step-num { color: var(--green); }
        .wizard-step.done .step-label { color: var(--green); }

        .wizard-step.locked {
            opacity: 0.4;
            pointer-events: none;
        }

        .wizard-body { min-height: 400px; }

        /* ================================================================
           FORM ELEMENTS
           ================================================================ */
        .form-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }

        /* Modal form grids — used by inline-styled modals (workshop, return,
         * settlement, violation, edit-contract). Unlike raw inline
         * grid-template-columns, these collapse on iPad/narrow viewports so
         * fields don't overlap. See app.css @media (max-width:900px). */
        .modal-grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px 20px; }
        .modal-grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px 20px; }
        .modal-grid-2 .col-span-2, .modal-grid-3 .col-span-2 { grid-column: span 2; }
        .modal-grid-2 .col-full, .modal-grid-3 .col-full { grid-column: 1 / -1; }

        .form-group { margin-bottom: 16px; }

        .form-group label {
            display: block;
            font-size: 12px;
            font-weight: 500;
            color: var(--text-muted);
            margin-bottom: 6px;
            text-transform: uppercase;
            letter-spacing: 0.3px;
        }

        .form-group input, .form-group select, .form-group textarea {
            width: 100%;
            padding: 12px 14px;
            background: var(--surface-2);
            border: 1px solid var(--border);
            border-radius: var(--radius);
            color: var(--text);
            font-size: 14px;
            font-family: inherit;
            outline: none;
            transition: border 0.2s, box-shadow 0.2s;
            -webkit-appearance: none;
            appearance: none;
        }

        .form-group input:focus, .form-group select:focus {
            border-color: var(--accent);
            box-shadow: 0 0 0 3px var(--accent-glow);
        }

        .form-group select {
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%234a5e80' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
            background-repeat: no-repeat;
            background-position: right 14px center;
            padding-right: 36px;
        }

        /* ================================================================
           ALERTS PANEL
           ================================================================ */
        .alert {
            display: flex;
            align-items: center;
            gap: 12px;
            padding: 14px 16px;
            border-radius: var(--radius);
            margin-bottom: 8px;
            font-size: 13px;
        }

        .alert-amber { background: var(--amber-dim); border-left: 3px solid var(--amber); }
        .alert-red { background: var(--red-dim); border-left: 3px solid var(--red); }

        /* ================================================================
           PAGE SECTIONS
           ================================================================ */
        .page { display: none; }
        .page.active { display: block; }

        /* ================================================================
           MODAL OVERLAY
           ================================================================ */
        .modal-overlay {
            display: none;
            position: fixed;
            inset: 0;
            z-index: var(--z-modal);
            background: rgba(0,0,0,0.6);
            backdrop-filter: blur(4px);
            overflow-y: auto;
            -webkit-overflow-scrolling: touch;
        }
        .modal-overlay.open { display: block; }
        .modal-overlay.stacked { z-index: var(--z-modal-stacked); }
        .modal-box {
            max-width: min(1500px, 96vw);
            width: 96vw;
            margin: 20px auto;
            background: var(--surface);
            border-radius: var(--radius-lg);
            border: 1px solid var(--border);
            padding: 24px 28px;
            animation: slideIn 0.25s ease;
        }
        /* Vehicle detail modal: the body (incl. the Life Log timeline) can grow
           long for vehicles with extensive history. Scroll the body in place so
           the header stays fixed and nothing is cut off — scoped to this one
           modal rather than the global .modal-box to avoid touching others. */
        #vehicle-modal-body {
            max-height: 85vh;
            overflow-y: auto;
            -webkit-overflow-scrolling: touch;
        }
        @media (max-width: 1024px) {
            .modal-box {
                width: 97vw;
                max-width: 100%;
                margin: 12px auto;
                padding: 20px;
            }
        }
        @media (max-width: 640px) {
            .modal-box {
                width: 98vw;
                margin: 8px auto;
                padding: 14px;
            }
        }
        /* Sortable table headers */
        th[data-sortkey] { cursor: pointer; user-select: none; }
        th[data-sortkey]:hover { color: var(--accent); }
        th[data-sortkey] .sort-ind {
            margin-left: 4px; font-size: 0.8em; opacity: 0.5; font-weight: 400;
        }
        .modal-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }
        .modal-header h3 { font-size: 17px; font-weight: 700; }
        .modal-close {
            background: none; border: none; color: var(--text-secondary);
            font-size: 22px; cursor: pointer; padding: 4px;
            /* iPad HIG: 44×44px minimum tap target (was ~22px — glyph height only). */
            min-width: 44px; min-height: 44px;
            display: inline-flex; align-items: center; justify-content: center;
            border-radius: 8px;
        }
        .modal-close:hover { color: var(--text); background: var(--surface); }

        /* ================================================================
           PRINT MEDIA QUERIES (FOR INVOICE)
           ================================================================ */
        @media print {
            body * { visibility: hidden; }
            #invoice-modal, #invoice-modal * { visibility: visible; }
            #invoice-modal { position: absolute; left: 0; top: 0; background: white; margin: 0; padding: 0; width: 100%; }
            #invoice-modal > div { border: none; box-shadow: none; max-width: 100%; margin: 0; }
            .d-print-none { display: none !important; }
            .print-text-dark { color: #000 !important; }
        }

        /* ================================================================
           DRIVER CARDS (inside client detail)
           ================================================================ */
        .driver-card {
            background: var(--surface-2);
            border: 1px solid var(--border);
            border-radius: var(--radius);
            padding: 14px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .driver-card + .driver-card { margin-top: 8px; }

        .section-title {
            font-size: 18px;
            font-weight: 700;
            margin-bottom: 20px;
            letter-spacing: -0.3px;
        }

        .page-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }

        .profit-positive { color: var(--green); }
        .profit-negative { color: var(--red); }

        /* ================================================================
           SEARCH BAR
           ================================================================ */
        .search-bar {
            position: relative;
            margin-bottom: 16px;
        }

        .search-bar input {
            width: 100%;
            padding: 12px 14px 12px 40px;
            background: var(--surface-2);
            border: 1px solid var(--border);
            border-radius: var(--radius);
            color: var(--text);
            font-size: 14px;
            font-family: inherit;
            outline: none;
        }

        .search-bar input:focus {
            border-color: var(--accent);
            box-shadow: 0 0 0 3px var(--accent-glow);
        }

        .search-bar::before {
            content: '🔍';
            position: absolute;
            left: 14px;
            top: 50%;
            transform: translateY(-50%);
            font-size: 14px;
        }

        .search-clear {
            position: absolute;
            right: 14px;
            top: 50%;
            transform: translateY(-50%);
            cursor: pointer;
            font-size: 14px;
            color: var(--text-secondary);
            opacity: 0.7;
            transition: opacity 0.2s;
            display: none;
            z-index: 10;
        }
        .search-clear:hover {
            opacity: 1;
            color: var(--text-primary);
        }

        /* ================================================================
           EMPTY STATE
           ================================================================ */
        .empty-state {
            text-align: center;
            padding: 60px 20px;
            color: var(--text-muted);
        }

        .empty-state .emoji { font-size: 48px; margin-bottom: 16px; }
        .empty-state p { font-size: 14px; }

        /* ================================================================
           TOAST
           ================================================================ */
        .toast-container {
            position: fixed;
            bottom: 24px;
            right: 24px;
            z-index: var(--z-toast);
        }

        .toast {
            background: var(--surface-2);
            border: 1px solid var(--border);
            border-radius: var(--radius);
            padding: 14px 20px;
            margin-top: 8px;
            font-size: 14px;
            box-shadow: 0 10px 40px rgba(0,0,0,0.4);
            animation: slideIn 0.3s ease;
        }

        .toast-success { border-left: 3px solid var(--green); }
        .toast-error { border-left: 3px solid var(--red); }
        .toast-info { border-left: 3px solid var(--accent, #2563eb); }

        @keyframes slideIn {
            from { opacity: 0; transform: translateY(20px); }
            to { opacity: 1; transform: translateY(0); }
        }

        /* ================================================================
           LOADING SPINNER
           ================================================================ */
        .spinner {
            width: 24px; height: 24px;
            border: 2px solid var(--border);
            border-top-color: var(--accent);
            border-radius: 50%;
            animation: spin 0.6s linear infinite;
            margin: 40px auto;
        }

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

        /* ================================================================
           SKELETON ROWS — first-load placeholder for list tables
           ================================================================
           A spinner says "something is happening"; a skeleton says "a table
           of about this shape is arriving", which reads as faster even at
           identical latency. Used only where nothing is cached yet — a warm
           tab paints real rows immediately and shows the refreshing chip
           instead (RFMS.swrEnter).

           .skeleton is the marker the smoke harness excludes when it waits
           for a settled table (frontend_tests/conftest.py SETTLED_ROW) —
           renaming it means updating that selector too. */
        .skeleton {
            height: 12px;
            border-radius: 6px;
            background: linear-gradient(90deg,
                var(--surface-2) 25%, var(--border) 37%, var(--surface-2) 63%);
            background-size: 400% 100%;
            animation: shimmer 1.4s ease-in-out infinite;
        }

        @keyframes shimmer {
            from { background-position: 100% 50%; }
            to   { background-position: 0 50%; }
        }

        /* Rows keep the real table's cell padding so the shimmer lands where
           the text will, and the row height doesn't jump when data replaces
           it. */
        tr.skeleton-row td { padding: 14px 12px; }

        /* prefers-reduced-motion: hold the mid-tone still rather than
           animating. The placeholder is still visible, just not moving. */
        @media (prefers-reduced-motion: reduce) {
            .skeleton { animation: none; background: var(--surface-2); }
        }

        /* ================================================================
           REFRESHING CHIP — background revalidation indicator (SWR)
           ================================================================
           Deliberately small and non-blocking: the table below it holds real
           (cached) rows the whole time, so this must never read as "wait".
           Sits in the topbar next to the page title. */
        .refresh-chip {
            display: none;
            align-items: center;
            gap: 6px;
            margin-left: 10px;
            padding: 3px 10px;
            border-radius: 999px;
            background: var(--surface-2);
            border: 1px solid var(--border);
            color: var(--text-secondary);
            font-size: 11px;
            font-weight: 600;
            vertical-align: middle;
        }
        .refresh-chip.on { display: inline-flex; }
        .refresh-chip::before {
            content: '';
            width: 8px; height: 8px;
            border-radius: 50%;
            background: var(--accent);
            animation: chip-pulse 1.2s ease-in-out infinite;
        }
        @keyframes chip-pulse { 50% { opacity: 0.25; } }
        @media (prefers-reduced-motion: reduce) {
            .refresh-chip::before { animation: none; }
        }

        /* ================================================================
           RESPONSIVE — Stack on narrower iPad / portrait
           ================================================================ */
        @media (max-width: 900px) {
            .grid-4 { grid-template-columns: repeat(2, 1fr); }
            .form-grid { grid-template-columns: 1fr; }
            /* Modal grids collapse on iPad portrait so fields never overlap. */
            .modal-grid-3 { grid-template-columns: repeat(2, 1fr); }
            .modal-grid-2 { grid-template-columns: 1fr; }
        }
        /* ================================================================
           PHOTO CAPTURE
           ================================================================ */
        .photo-capture {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 12px;
            padding: 20px;
            background: var(--surface-2);
            border: 2px dashed var(--border);
            border-radius: var(--radius-lg);
            cursor: pointer;
            transition: all var(--transition);
        }

        .photo-capture:hover { border-color: var(--accent); background: var(--accent-glow); }

        .photo-capture .capture-icon { font-size: 32px; }
        .photo-capture .capture-label { font-size: 13px; color: var(--text-secondary); font-weight: 500; }
        .photo-capture img {
            max-width: 100%;
            max-height: 160px;
            border-radius: var(--radius);
            object-fit: cover;
        }

        /* ================================================================
           TOGGLE BUTTON GROUP (Existing / New hirer)
           ================================================================ */
        .toggle-group {
            display: flex;
            gap: 0;
            border-radius: var(--radius);
            overflow: hidden;
            border: 1px solid var(--border);
            margin-bottom: 20px;
        }

        .toggle-btn {
            flex: 1;
            padding: 12px 20px;
            background: var(--surface-2);
            color: var(--text-secondary);
            font-size: 13px;
            font-weight: 600;
            font-family: inherit;
            border: none;
            cursor: pointer;
            transition: all var(--transition);
            text-align: center;
        }

        .toggle-btn.active {
            background: var(--accent);
            color: #fff;
        }

        .toggle-btn:not(.active):hover {
            background: var(--surface-3);
            color: var(--text);
        }

        .hirer-panel { display: none; }
        .hirer-panel.active { display: block; }

/* ================================================================
   EXPIRY / RE-SIGN POPUP (dashboard reminder banners)
   ================================================================ */
/* Summary stat tiles — lift on hover so the header feels alive. */
.expiry-stat {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 14px 18px;
    text-align: center;
    transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}
.expiry-stat:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,0,0,0.35); border-color: var(--border); }
.expiry-stat--red    { background: var(--red-dim); }
.expiry-stat--amber  { background: var(--amber-dim); }
.expiry-stat--blue   { background: var(--blue-dim); }
.expiry-stat--muted  { background: var(--surface-3); }
.expiry-stat__num  { font-size: 26px; font-weight: 800; line-height: 1; letter-spacing: -0.5px; }
.expiry-stat--red   .expiry-stat__num { color: var(--red); }
.expiry-stat--amber .expiry-stat__num { color: var(--amber); }
.expiry-stat--blue  .expiry-stat__num { color: var(--accent); }
.expiry-stat--muted .expiry-stat__num { color: var(--text-secondary); }
.expiry-stat__label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.6px; color: var(--text-secondary); margin-top: 6px; font-weight: 600; }

/* Bucket section header with a count chip. */
.expiry-bucket {
    display: flex; align-items: center; gap: 10px;
    margin: 22px 0 10px; font-size: 12px; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.6px;
}
.expiry-bucket:first-of-type { margin-top: 8px; }

/* Per-row urgency accent stripe (left edge of first cell) — encodes
   urgency in the row itself, not just the badge column. */
.expiry-row--holdover td:first-child { box-shadow: inset 3px 0 0 var(--red); }
.expiry-row--expiring td:first-child { box-shadow: inset 3px 0 0 var(--amber); }
.expiry-row--watch    td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
.expiry-row--open     td:first-child { box-shadow: inset 3px 0 0 var(--text-muted); }

/* Gentle staggered reveal of rows when the popup opens. */
@keyframes expiryRowIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
#expiry-modal tbody tr { animation: expiryRowIn 0.3s ease both; }
#expiry-modal tbody tr:nth-child(1)  { animation-delay: 0.02s; }
#expiry-modal tbody tr:nth-child(2)  { animation-delay: 0.05s; }
#expiry-modal tbody tr:nth-child(3)  { animation-delay: 0.08s; }
#expiry-modal tbody tr:nth-child(4)  { animation-delay: 0.11s; }
#expiry-modal tbody tr:nth-child(5)  { animation-delay: 0.14s; }
#expiry-modal tbody tr:nth-child(n+6){ animation-delay: 0.17s; }

/* Keep wide tables readable on iPad without inline min-width. */
#expiry-modal .table-wrap table { min-width: 560px; }



/* =============================================================================
   Signature pads — ONE shape, everywhere
   =============================================================================
   Pads used to be 130 / 160 / 180 / 200 / 240px tall with width:100%, so the
   captured aspect ratio depended on which modal you were in and how wide the
   device was. The stored SVG bakes that ratio in, so the same person's
   signature came out a different shape on every document.

   3:1 matches the standard 600x200 viewBox in rfms_wizard.js
   (window.RFMS.SIG_VIEWBOX). The two MUST agree: the exporter scales the pad's
   CSS rect into that box, which is only distortion-free while the pad is 3:1.
   Change one, change the other.

   max-width stops the pad becoming absurdly tall inside a wide desktop modal
   (a 1400px pad at 3:1 would be 466px tall); min-height keeps it usable on a
   narrow phone. Both clamp the BOX, not the ratio — aspect-ratio still governs
   in between, which is the range every real device falls into.
*/
.sig-pad {
    display: block;
    width: 100%;
    max-width: 720px;
    aspect-ratio: 3 / 1;
    min-height: 110px;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 8px;
    touch-action: none;          /* required: the pad handles its own pointers */
    cursor: crosshair;
}

/* Where a saved signature is displayed back (locked pads, timelines, previews).
   Sized by the same ratio so a signature does not jump shape between being
   drawn and being reviewed. */
.sig-display {
    width: 100%;
    max-width: 720px;
    aspect-ratio: 3 / 1;
    min-height: 110px;
    background: #fff;
    border: 2px solid var(--green, #2ecc71);
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 8px;
}
.sig-display svg { max-width: 100%; max-height: 100%; width: auto; height: auto; }

/* ---------------------------------------------------------------------------
   Uppercase text entry (owner request 2026-08-12, docs/PLAN_UPPERCASE_ALL_TEXT.md)

   Everything staff type is STORED uppercase; this is the matching display, so
   the field shows what will actually be saved instead of contradicting it.
   The value itself is uppercased by the input handler in rfms_core.js — CSS
   alone only paints, and a form posting lowercase while showing uppercase is
   worse than no rule at all.

   Selecting by input TYPE is what keeps the exclusions honest: email, password,
   url, number, date, time and search inputs are never matched, so the owner's
   "leave email as typed" decision and the case-sensitive login are safe by
   construction rather than by remembering to add a class.

   Opt out of a specific field with data-no-uppercase (the username field uses
   it — login is case-sensitive, so showing uppercase while storing what was
   typed would lock staff out of their own accounts).
   --------------------------------------------------------------------------- */
input[type="text"]:not([data-no-uppercase]),
input:not([type]):not([data-no-uppercase]),
textarea:not([data-no-uppercase]) {
    text-transform: uppercase;
}

/* ---------------------------------------------------------------------------
   BOARDS — shared client-block spine (cb-) + Billing Board (bb-)  [BB-2]

   Design contract: ~/agent-runs/boards/mockup.html (2026-08-16). Layout,
   sizing and state palette follow it; panel/border/text surfaces use this
   app's dark tokens. The cb- chrome (toolbar, chips, client-block frame,
   pager) is shared with the upcoming Contracts Board, so state colours
   live ONLY on bb- classes. Every state also carries a glyph — colour is
   never the only signal (SHARED_CONTEXT rule 8). Touch targets are ≥44px
   even where the drawing is smaller.
   --------------------------------------------------------------------------- */

/* Board state palette — the mockup's dark scheme, verbatim.
   Scoped to BOTH board roots (2026-08-16): it was on #billing-board-root
   alone, so every --bb-* reference inside the contracts board resolved to
   nothing — the overrun bar, the state pills and the today tick all rendered
   invisible while the CSS looked perfectly correct in isolation. The two
   boards share a spine, so they must share the palette that spine's children
   assume. A third board adds its root here. */
#billing-board-root,
#contracts-board-root {
    --bb-paid: #4ade80;      --bb-paid-bg: #123021;
    --bb-sent: #fbbf24;      --bb-sent-bg: #3a2a08;
    --bb-overdue: #f472b6;   --bb-overdue-bg: #3a0f26;
    --bb-bad: #f87171;       --bb-bad-bg: #3a1212;
    --bb-unsent: #93c5fd;    --bb-unsent-bg: #10254d;
    --bb-void: #9aa3af;      --bb-void-bg: #20242b;
    --bb-future-line: #39414c;
    --bb-today: #e11d48;
    /* LEGACY — a hire running past its expected return date. Owner, 2026-08-17:
       "nothing is wrong, and it's not urgent enough to warrant a reddish tone
       warning" — staff should read it as "this one can be renewed", not as an
       alarm. Deliberately NOT --bb-void (that grey is for VOIDED invoices, and
       these contracts are very much alive) and NOT --text-muted at .55 like
       completed/superseded, which reads as finished. A neutral, present grey. */
    --cb-legacy: #a8b0bb;    --cb-legacy-bg: #262a31;
}

/* --- Spine chrome (cb-) --------------------------------------------------- */
.cb-toolbar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 12px; }
.cb-search { flex: 1 1 220px; min-width: 180px; min-height: 44px; padding: 8px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text); font-size: 13px; }
.cb-search::placeholder { color: var(--text-muted); }
.cb-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.cb-chip { min-height: 44px; display: inline-flex; align-items: center; gap: 6px; padding: 7px 14px; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); color: var(--text-secondary); font-size: 12px; font-weight: 600; cursor: pointer; transition: all var(--transition); }
.cb-chip--on { background: var(--accent); border-color: var(--accent); color: #fff; }
.cb-n { opacity: .75; font-weight: 500; }
.cb-legend { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; padding: 10px 12px; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; margin-bottom: 14px; font-size: 12px; color: var(--text-secondary); }
.cb-legend > span { display: inline-flex; align-items: center; gap: 6px; }
.cb-client { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; margin-bottom: 12px; overflow: hidden; }
.cb-chead { display: flex; align-items: center; gap: 10px; padding: 11px 13px; flex-wrap: wrap; border-bottom: 1px solid var(--border); cursor: pointer; }
.cb-client--folded .cb-chead { border-bottom-color: transparent; }
.cb-caret { color: var(--text-muted); width: 12px; flex: none; }
.cb-cname { font-weight: 700; letter-spacing: .01em; }
.cb-code { font: 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text-muted); border: 1px solid var(--border); padding: 3px 6px; border-radius: 5px; }
.cb-roll { margin-left: auto; display: flex; gap: 14px; align-items: center; font-size: 12.5px; flex-wrap: wrap; }
.cb-btns { display: flex; gap: 6px; }
.cb-btn { border: 1px solid var(--border); background: transparent; color: var(--text); border-radius: 8px; padding: 7px 10px; font-size: 12px; font-weight: 600; cursor: pointer; min-height: 44px; }
.cb-btn--pri { background: var(--accent); border-color: var(--accent); color: #fff; }
.cb-btn[disabled] { opacity: .38; cursor: not-allowed; }
.cb-client--folded .cb-body { display: none; }
.cb-fold { display: none; padding: 10px 13px; border-top: 1px solid var(--border); font-size: 12px; color: var(--text-muted); cursor: pointer; }
.cb-client--folded .cb-fold { display: block; }
.cb-pager { display: flex; gap: 10px; align-items: center; justify-content: center; margin: 14px 0 4px; }
.cb-pager-info { font-size: 12px; color: var(--text-muted); }
.cb-empty { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 40px 16px; text-align: center; color: var(--text-muted); font-size: 13px; }
.cb-empty-q { display: block; margin-top: 6px; }
.cb-client--skel .cb-chead { cursor: default; }
.cb-tip { position: absolute; z-index: var(--z-toast); max-width: 280px; background: var(--surface-3); color: var(--text); border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; font-size: 12px; line-height: 1.4; box-shadow: 0 4px 16px rgba(0, 0, 0, .4); pointer-events: none; display: none; }

/* --- Billing Board rows (bb-) ---------------------------------------------- */
.bb-pain { border-color: var(--bb-overdue); }
.bb-warn { color: var(--bb-overdue); font-weight: 700; }
.bb-ok { color: var(--bb-paid); font-weight: 600; }
.bb-btn--wa:not([disabled]) { color: #4ade80; }

.bb-row { display: flex; align-items: center; gap: 10px; padding: 9px 13px; border-top: 1px solid var(--border); }
.bb-row:first-child { border-top: none; }
.bb-rmeta { width: 190px; flex: none; }
/* Tappable — opens the contract. It has to LOOK tappable without shouting:
   this sits beside the cells, and two competing affordances in one row is
   how you get staff opening the wrong thing. Cursor + a hairline on hover,
   nothing more. */
.bb-rmeta--tappable { cursor: pointer; border-radius: 4px; margin: -2px -4px; padding: 2px 4px; }
.bb-rmeta--tappable:hover { background: var(--surface-3); }
.bb-rmeta--tappable:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.bb-plate { font-weight: 700; letter-spacing: .02em; }
.bb-rno { font: 12px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text-muted); }
.bb-freq { font-size: 11px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .06em; }

/* The strip scrolls; the row never stretches (long strips stay inside). */
/* padding-top 13px, not 3px: headroom for the TODAY caption to sit ABOVE the
   boxes rather than across the one beside it. */
.bb-strip { flex: 1; min-width: 0; display: flex; gap: 5px; overflow-x: auto; padding: 13px 2px 7px; scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch; }
.bb-strip::-webkit-scrollbar { height: 6px; }
.bb-strip::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }

.bb-cell { flex: none; width: 62px; height: 44px; border-radius: 7px; border: 1px solid transparent; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px; cursor: pointer; scroll-snap-align: end; position: relative; padding: 2px; }
.bb-cell-d { font-size: 10.5px; font-weight: 700; line-height: 1.1; }
.bb-cell-a { font-size: 9.5px; opacity: .85; font-variant-numeric: tabular-nums; }
.bb-cell-g { position: absolute; top: 2px; right: 4px; font-size: 9px; font-weight: 900; opacity: .9; }

.bb-cell--paid    { background: var(--bb-paid-bg);    color: var(--bb-paid);    border-color: var(--bb-paid); }
.bb-cell--sent    { background: var(--bb-sent-bg);    color: var(--bb-sent);    border-color: var(--bb-sent); }
.bb-cell--overdue { background: var(--bb-overdue-bg); color: var(--bb-overdue); border-color: var(--bb-overdue); }
.bb-cell--bad     { background: var(--bb-bad-bg);     color: var(--bb-bad);     border-color: var(--bb-bad); }
.bb-cell--unsent  { background: var(--bb-unsent-bg);  color: var(--bb-unsent);  border-color: var(--bb-unsent); }
.bb-cell--void    { background: var(--bb-void-bg);    color: var(--bb-void);    border-color: var(--bb-void); text-decoration: line-through; }
.bb-cell--future  { background: transparent; color: var(--text-muted); border: 1px dashed var(--bb-future-line); cursor: default; }

/* Part-payment is a MODIFIER (change order 2026-08-16): .bb-cell--haspart
   rides ON TOP of whatever state class the cell already has, so an overdue
   half-paid invoice keeps its overdue colour and gains a paid-so-far fill.
   It must not restyle background or border — those belong to the state.
   .bb-cell--partial is kept for the legend swatch only. */
.bb-cell--haspart { position: relative; }
/* The fill sits BEHIND the text (z-index 0 vs the raised children below) and
   stops short of the bottom, so the paid/total figure is never cut through by
   the divider line — it reads as a progress bar under the numbers rather than
   a wipe across them (visual pass, 2026-08-16). */
.bb-cell--haspart::before { content: ""; position: absolute; inset: 0 auto 0 0; width: var(--fill, 50%); background: var(--bb-paid-bg); border-right: 2px solid var(--bb-paid); border-radius: 6px 0 0 6px; opacity: .55; z-index: 0; }
.bb-cell--haspart .bb-cell-d, .bb-cell--haspart .bb-cell-a, .bb-cell--haspart .bb-cell-g { position: relative; z-index: 1; }
/* paid/total needs more room than a single figure — drop a little size and
   let it use the full cell width rather than wrapping or clipping. */
.bb-cell--haspart .bb-cell-a { font-size: 8.5px; letter-spacing: -.2px; white-space: nowrap; }
.bb-cell--partial { position: relative; background: var(--bb-sent-bg); color: var(--bb-sent); border-color: var(--bb-paid); }
.bb-cell--partial::before { content: ""; position: absolute; inset: 0 auto 0 0; width: var(--fill, 50%); background: var(--bb-paid-bg); border-right: 2px solid var(--bb-paid); border-radius: 6px 0 0 6px; }
.bb-cell--partial .bb-cell-d, .bb-cell--partial .bb-cell-a, .bb-cell--partial .bb-cell-g { position: relative; }

/* The divider is a zero-width flex item, so its "TODAY" caption is absolutely
   positioned and therefore free to overlap the cell to its right — which it
   did, on any row where today falls mid-strip (visual pass, 2026-08-16).
   Fixed by giving the strip a little top padding and lifting the caption into
   it, above the boxes, instead of sitting across them. `pointer-events:none`
   so the caption can never eat a tap meant for the cell underneath. */
.bb-todayline { flex: none; width: 0; border-left: 2px dashed var(--bb-today); margin: 0 3px; align-self: stretch; position: relative; }
.bb-todayline::after { content: "TODAY"; position: absolute; top: -11px; left: 50%; transform: translateX(-50%); font-size: 8px; font-weight: 800; color: var(--bb-today); letter-spacing: .08em; line-height: 1; pointer-events: none; }

.bb-tail { width: 104px; flex: none; text-align: right; font-size: 12px; color: var(--text-muted); }
.bb-batch { font-size: 11px; }

.bb-sw { width: 15px; height: 15px; border-radius: 4px; display: inline-grid; place-items: center; font-size: 9px; font-weight: 800; line-height: 1; font-style: normal; }
.bb-legend-today { border-left: 2px dashed var(--bb-today); padding-left: 6px; }

@media (max-width: 900px) {
    .bb-rmeta { width: 130px; }
    .bb-tail { width: 84px; }
}

/* =========================================================================
   CONTRACTS BOARD (CB-2) — lifecycle rows inside the shared client spine.
   The spine's own chrome (.cb-toolbar/.cb-chips/.cb-list/.cb-client/…) is
   styled with the billing board above; only the ROW is board-specific, and
   every position here comes from a server-computed percentage.
   ========================================================================= */
.cb-row { display: flex; align-items: center; gap: 10px; padding: 9px 13px; border-top: 1px solid var(--border); }
.cb-row:first-child { border-top: none; }
.cb-meta { width: 190px; flex: none; }
.cb-plate { font-weight: 700; letter-spacing: .02em; }
.cb-no { font: 12px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text-muted); }

/* A renewal reads as ONE relationship: the successor is indented under its
   predecessor with a gutter elbow, rather than sitting as an unrelated row. */
.cb-row--chained .cb-meta { padding-left: 16px; position: relative; }
.cb-row--chained .cb-meta::before { content: ""; position: absolute; left: 5px; top: -18px; bottom: 14px; border-left: 2px solid var(--border); }
.cb-row--chained .cb-meta::after { content: ""; position: absolute; left: 5px; top: 16px; width: 8px; border-top: 2px solid var(--border); }

.cb-life { flex: 1; position: relative; height: 38px; min-width: 0; }
.cb-track { position: absolute; left: 0; right: 0; top: 14px; height: 10px; border-radius: 5px; background: var(--surface-3); border: 1px solid var(--border); }
.cb-bar { position: absolute; top: 14px; height: 10px; border-radius: 5px; }
.cb-bar--active  { background: var(--accent); opacity: .85; }
.cb-bar--ws      { background: #7c3aed; opacity: .9; }
.cb-bar--done    { background: var(--text-muted); opacity: .55; }
.cb-bar--overrun { background: var(--cb-legacy); opacity: .9; }
/* Legacy is drawn as a hatched EXTENSION past the bar end — a length you can
   see from across the counter, not a hue you have to stop and read. That is
   why the colour can go quiet without the state becoming invisible: the bar
   still grows every day the vehicle stays out. */
.cb-bar--overrun-ext { background: repeating-linear-gradient(45deg, var(--cb-legacy), var(--cb-legacy) 5px, transparent 5px, transparent 10px); border: 1px solid var(--cb-legacy); }
.cb-tick { position: absolute; top: 6px; bottom: 6px; border-left: 2px dashed var(--bb-today); }
.cb-lbl { position: absolute; top: 26px; font-size: 10.5px; color: var(--text-muted); white-space: nowrap; }
.cb-lbl--r { right: 0; }

.cb-tail { width: 120px; flex: none; text-align: right; font-size: 12px; color: var(--text-muted); }
.cb-rate { font-variant-numeric: tabular-nums; }
.cb-renew { min-height: 44px; }          /* iPad target (AGENTS.md) */

.cb-pill { display: inline-block; font-size: 10px; font-weight: 800; letter-spacing: .05em; padding: 2px 7px; border-radius: 999px; text-transform: uppercase; margin-top: 3px; }
.cb-pill--active  { background: var(--bb-paid-bg);    color: var(--bb-paid); }
.cb-pill--overrun { background: var(--cb-legacy-bg); color: var(--cb-legacy); }
.cb-pill--soon    { background: var(--bb-sent-bg);    color: var(--bb-sent); }
.cb-pill--ws      { background: rgba(124,58,237,.18); color: #a78bfa; }
.cb-pill--done    { background: var(--surface-3);     color: var(--text-muted); }

.cb-history { padding: 8px 13px; border-top: 1px solid var(--border); font-size: 12px; color: var(--text-muted); cursor: pointer; min-height: 44px; display: flex; align-items: center; }
.cb-empty-rows { padding: 12px 13px; font-size: 12.5px; color: var(--text-muted); }
.cb-warn { color: var(--bb-overdue); font-weight: 700; }
.cb-sw { width: 15px; height: 12px; border-radius: 3px; display: inline-block; vertical-align: middle; }

@media (max-width: 1100px) {
    .cb-meta { width: 150px; }
    .cb-tail { width: 96px; }
}

/* Kept for a future genuine alert. Nothing sets it now: it used to ring a
   whole client block in pink because one hire was past its return date, and
   that is exactly the false alarm the owner called out on 2026-08-17. */
.cb-client--attention { border-color: var(--bb-overdue); }
