:root {
    /* P3 refresh: refined palette, more depth. Existing names are kept
       (used throughout the CSS); values are adjusted + new tokens added.
       "example theme" reskin: hero accent moved green -> orange, near-black
       base (#0a0c12), glass surfaces + orange glow. Green kept as --success
       for semantic states (online / signal). */
    --bg-color: #0a0c12;
    --bg-elev: #0e131f;          /* slightly raised background (login, banners) */
    --sidebar-bg: #10151f;
    --card-bg: rgba(255, 255, 255, 0.04);     /* glass surface (blurred over bg-moto) */
    --card-bg-solid: #161e30;
    --border-color: rgba(255, 255, 255, 0.08);
    --border-strong: rgba(255, 255, 255, 0.14);

    /* Hero accent = orange (Tailwind orange-500), matching example theme */
    --primary: #f97316;
    --primary-hover: #ea580c;
    --primary-glow: rgba(249, 115, 22, 0.15);

    /* Semantic green (online status, signal, positive) — was the old accent */
    --success: #10b981;
    --success-glow: rgba(16, 185, 129, 0.15);

    /* Glass + glow design tokens (from example theme index.css) */
    --glass-bg: rgba(255, 255, 255, 0.04);
    --glass-bg-strong: rgba(255, 255, 255, 0.06);
    --glass-border: rgba(255, 255, 255, 0.08);
    --glass-blur: 12px;
    --glow-primary: 0 0 24px rgba(249, 115, 22, 0.45);

    --text-color: #f3f4f6;
    --text-dim: #9aa4b2;         /* secondary text, more legible than --text-muted */
    --text-muted: #6b7280;

    --orange: #f97316;
    --sky: #0ea5e9;
    --blue: #3b82f6;
    --purple: #8b5cf6;
    --violet: #8b5cf6;
    --emerald: #10b981;
    --rose: #f43f5e;
    --amber: #f59e0b;
    --red: #ef4444;

    /* Spacing scale (base 4/8) + radii + shadows + focus ring */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --radius-sm: 8px;
    --radius: 12px;
    --radius-lg: 16px;
    --shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
    --shadow: 0 6px 20px rgba(0,0,0,0.35);
    --shadow-lg: 0 16px 40px rgba(0,0,0,0.45);
    --focus-ring: 0 0 0 3px var(--primary-glow);
}

* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

body {
    /* S1 FIX: the Google Fonts font was removed (CDN dependency) - the
       system font stack covers the same aesthetic well with no external request */
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    background-color: var(--bg-color);
    color: var(--text-color);
    overflow-x: hidden;
    /* min-height, NOT height. `overflow-x: hidden` forces overflow-y to
       compute to `auto`, so a fixed 100vh height turned BODY ITSELF into the
       scroll container. On desktop that is harmless (.dashboard-container is
       explicitly 100vh and .main-content does the scrolling), but on a phone
       the shell switches to height:auto and the content grows past the
       viewport - leaving 2300px of page inside a 100vh scroller. Desktop
       Chrome copes; iOS Safari is the known-bad case, because 100vh there
       excludes the collapsing toolbars and momentum scrolling does not
       propagate out of a body-level scroller, so the page reads as stuck.
       With min-height the document scrolls natively, which is what the
       mobile layout already assumed. */
    min-height: 100vh;
}

/* LOGIN WRAPPER */
.login-wrapper {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh;
    background-color: var(--bg-color);
}

.login-card {
    background-color: var(--sidebar-bg);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 30px;
    width: 100%;
    max-width: 400px;
    box-shadow: 0 10px 25px rgba(0, 0, 0, 0.4);
}

.brand-header {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
    margin-bottom: 25px;
}

.login-error {
    color: var(--red);
    font-size: 12px;
    text-align: center;
    margin-top: 10px;
    font-weight: 600;
}

/* #10 FIX: default-password banner */
.insecure-banner {
    background: rgba(239, 68, 68, 0.15);
    border: 1px solid var(--red);
    color: var(--red);
    padding: 12px 16px;
    border-radius: 8px;
    font-weight: 600;
    font-size: 13px;
    margin-bottom: 16px;
}

/* #15/#14: banner for unacknowledged safety alerts - orange, distinct
   from the red default-password banner */
.insecure-banner.safety-banner {
    background: rgba(249, 115, 22, 0.15);
    border-color: var(--orange);
    color: var(--orange);
}

/* #14: mobile hamburger menu - hidden by default, shown only below 600px */
.mobile-menu-toggle {
    display: none;
    width: 40px;
    height: 40px;
    align-items: center;
    justify-content: center;
    background: var(--sidebar-bg);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    color: var(--text-color);
    font-size: 18px;
    cursor: pointer;
    margin-bottom: 12px;
}

.mobile-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 1199;
}

.mobile-backdrop.active {
    display: block;
}

/* MAIN CONTAINER GRID */
.dashboard-container {
    display: grid;
    grid-template-columns: 280px 1fr;
    height: 100vh;
}

/* Sidebar Navigation */
.sidebar {
    background-color: var(--sidebar-bg);
    border-right: 1px solid var(--border-color);
    padding: 24px;
    display: flex;
    flex-direction: column;
    gap: 25px;
}

.brand {
    display: flex;
    align-items: center;
    gap: 12px;
}

.logo-box {
    background-color: var(--primary-glow);
    padding: 8px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.logo-icon {
    color: var(--primary);
    width: 24px;
    height: 24px;
}

.brand h2 {
    font-size: 16px;
    font-weight: 800;
    letter-spacing: -0.5px;
}

.status-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 20px;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    background: rgba(239, 68, 68, 0.1);
    color: var(--red);
    margin-top: 2px;
    transition: all 0.3s;
}

.status-badge.online {
    background: var(--success-glow);
    color: var(--success);
}

/* The header badge carries the motorcycle's state, one colour per state so
   it reads at a glance. The rule: a colour means information, grey means we
   have none.
     riding  - app accent, in motion
     online  - green (above), awake and connected
     parked  - sky blue, the normal healthy resting state. Deliberately NOT
               red (the base style) and not grey: it is the state shown most
               of the time, and it is good news
     offline - amber, the device is offline but we cannot tell it is simply
               parked, so it earns a glance
     (base)  - red, a real problem: signal lost mid-ride, or no broker link
     neutral - grey, only for "no data yet" (unknown / connecting) */
.status-badge.riding {
    background: var(--primary-glow);
    color: var(--primary);
}

.status-badge.parked {
    background: rgba(14, 165, 233, 0.15);
    color: var(--sky);
}

.status-badge.offline {
    background: rgba(245, 158, 11, 0.15);
    color: var(--amber);
}

.status-badge.neutral {
    background: rgba(255, 255, 255, 0.06);
    color: var(--text-dim);
}

/* Stage 26: level badges for the Logs tab (error/warn/info/debug) - same
   shape as .status-badge, different color per severity. */
.log-level-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 20px;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
}
.log-level-badge.log-level-error { background: rgba(239, 68, 68, 0.15); color: var(--red); }
.log-level-badge.log-level-warn  { background: rgba(245, 158, 11, 0.15); color: #f59e0b; }
.log-level-badge.log-level-info  { background: rgba(255, 255, 255, 0.06); color: var(--text-muted); }
.log-level-badge.log-level-debug { background: rgba(255, 255, 255, 0.04); color: var(--text-muted); }

/* Etapa 30: severity color on the whole row (left border), so errors/warnings
   pop at a glance without scanning the badge column. */
.log-row { border-left: 3px solid transparent; }
.log-row-error { border-left-color: var(--red); background: rgba(239, 68, 68, 0.05); }
.log-row-warn  { border-left-color: #f59e0b; background: rgba(245, 158, 11, 0.045); }
.log-row-info  { border-left-color: rgba(255, 255, 255, 0.10); }
.log-row-debug { border-left-color: rgba(255, 255, 255, 0.05); }
.log-source-tag {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11px;
    color: var(--text-muted);
    background: rgba(255, 255, 255, 0.05);
    padding: 1px 6px;
    border-radius: 5px;
    white-space: nowrap;
}

/* Etapa 30: System Log / Device Timeline view toggle */
.logs-view-toggle {
    display: inline-flex;
    gap: 4px;
    background: rgba(0, 0, 0, 0.2);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 3px;
    margin: 0 0 12px 0;
}
.logs-view-btn {
    background: transparent;
    border: none;
    color: var(--text-muted);
    font-size: 13px;
    font-weight: 600;
    padding: 6px 14px;
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.15s;
}
.logs-view-btn:hover { color: var(--text-color); }
.logs-view-btn.active { background: var(--primary); color: #fff; }

/* Etapa 30: quick severity chips (system log) */
.logs-chips { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
.logs-chip {
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid var(--border-color);
    color: var(--text-muted);
    font-size: 12px;
    font-weight: 600;
    padding: 5px 12px;
    border-radius: 20px;
    cursor: pointer;
    transition: all 0.15s;
}
.logs-chip:hover { color: var(--text-color); border-color: var(--border-strong); }
.logs-chip.active { background: rgba(249, 115, 22, 0.15); border-color: var(--primary); color: var(--primary); }

/* Etapa 31: JS-driven sliding switch (toggles an .active class, does NOT rely
   on the checkbox :checked hack - that one wouldn't slide in the card-header
   context for reasons that resisted diagnosis; this is bulletproof). */
.js-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    background: transparent;
    border: none;
    padding: 4px 2px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
    user-select: none;
}
.js-toggle .js-toggle-track {
    position: relative;
    display: inline-block;
    width: 40px;
    height: 22px;
    border-radius: 22px;
    background: rgba(255, 255, 255, 0.15);
    transition: background-color 0.2s;
    flex-shrink: 0;
}
.js-toggle .js-toggle-knob {
    position: absolute;
    top: 3px;
    left: 3px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #fff;
    transition: transform 0.2s;
}
.js-toggle.active { color: var(--text-color); }
.js-toggle.active .js-toggle-track { background: var(--primary); }
.js-toggle.active .js-toggle-knob { transform: translateX(18px); }
.js-toggle:focus-visible .js-toggle-track { outline: 2px solid var(--primary); outline-offset: 2px; }

/* Etapa 30: per-source log level matrix (Settings -> Platform) */
.log-levels-matrix { display: flex; flex-direction: column; gap: 8px; }
.log-levels-row {
    display: flex;
    align-items: center;
    gap: 10px;
}
.log-levels-name {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    color: var(--text-color);
}
.log-levels-select {
    width: auto;
    min-width: 110px;
    background-color: rgba(0, 0, 0, 0.2);
    border: 1px solid var(--border-color);
    padding: 6px 10px;
    border-radius: 6px;
    color: var(--text-color);
    font-size: 13px;
}

/* Navigation Menu */
.nav-menu {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.nav-item {
    background: transparent;
    border: none;
    padding: 12px 16px;
    border-radius: 8px;
    color: var(--text-muted);
    font-size: 13px;
    font-weight: 600;
    text-align: left;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 12px;
    transition: all 0.2s;
    width: 100%;
}

.nav-item svg {
    width: 18px;
    height: 18px;
}

.nav-item:hover, .nav-item.active {
    color: var(--text-color);
    background-color: rgba(255, 255, 255, 0.05);
}

.nav-item.active {
    border-left: 3px solid var(--primary);
}

.config-section h3, .quick-actions h3 {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--text-muted);
    margin-bottom: 12px;
    display: flex;
    align-items: center;
    gap: 6px;
}

.config-section h3 svg, .quick-actions h3 svg {
    width: 14px;
    height: 14px;
}

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

.form-group label {
    display: block;
    font-size: 10px;
    text-transform: uppercase;
    font-weight: 700;
    color: var(--text-muted);
    margin-bottom: 6px;
}

.form-group input {
    width: 100%;
    background-color: rgba(0, 0, 0, 0.2);
    border: 1px solid var(--border-color);
    padding: 10px 12px;
    border-radius: 6px;
    color: var(--text-color);
    font-size: 13px;
    outline: none;
    transition: all 0.2s;
}

.form-group input:focus {
    border-color: var(--primary);
}

/* .form-group input only ever targeted <input> - a bare <textarea> (e.g. the
   SMS compose box) fell back to the browser's tiny default sizing. */
.form-group textarea {
    width: 100%;
    background-color: rgba(0, 0, 0, 0.2);
    border: 1px solid var(--border-color);
    padding: 10px 12px;
    border-radius: 6px;
    color: var(--text-color);
    font-size: 13px;
    font-family: inherit;
    outline: none;
    resize: vertical;
    min-height: 90px;
    box-sizing: border-box;
    transition: all 0.2s;
}

.form-group textarea:focus {
    border-color: var(--primary);
}

/* .btn defaults to width:100% - correct in the narrow Settings/login forms it
   was designed for, but the SMS tab's card spans the full wide content
   column, where a full-width submit button rendered as an oversized slab. */
#sms-send-form button[type="submit"] {
    width: auto;
    padding-left: 32px;
    padding-right: 32px;
}

/* Ride Analysis panel's title/notes editor - these two fields were never
   wrapped in .form-group, so they fell back to the browser's unstyled white
   input/textarea on this dark theme. Own layout: labeled fields stacked
   vertically, each capped short of the card's full width, save button below
   both (not squeezed into a row with the title input). */
.ride-meta-edit {
    margin-top: 14px;
    border-top: 1px solid var(--border-color);
    padding-top: 14px;
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.ride-meta-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-width: 420px;
}

.ride-meta-field-notes {
    max-width: 640px;
}

.ride-meta-field label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.ride-meta-field input,
.ride-meta-field textarea {
    background-color: rgba(0, 0, 0, 0.2);
    border: 1px solid var(--border-color);
    padding: 10px 12px;
    border-radius: 6px;
    color: var(--text-color);
    font-size: 13px;
    font-family: inherit;
    outline: none;
    transition: all 0.2s;
}

.ride-meta-field input:focus,
.ride-meta-field textarea:focus {
    border-color: var(--primary);
    box-shadow: var(--focus-ring);
}

.ride-meta-field textarea {
    resize: vertical;
    min-height: 90px;
    box-sizing: border-box;
}

/* Same oversized-slab problem as the SMS form above, same fix - .btn
   defaults to width:100%, correct in narrow forms but not here. */
.ride-meta-edit button[type="button"] {
    width: auto;
    align-self: flex-start;
    padding-left: 24px;
    padding-right: 24px;
}

.checkbox-group {
    display: flex;
    align-items: center;
}

.btn {
    width: 100%;
    padding: 12px;
    border-radius: 6px;
    border: none;
    font-weight: 700;
    font-size: 13px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    transition: all 0.2s;
}

.btn-connect {
    background-color: var(--primary);
    color: var(--bg-color);
}

.btn-connect:hover {
    background-color: #059669;
}

.btn-action {
    background-color: rgba(255, 255, 255, 0.05);
    color: var(--text-color);
    border: 1px solid var(--border-color);
    margin-bottom: 10px;
}

.btn-action:hover {
    background-color: rgba(255, 255, 255, 0.08);
}

.btn-action svg {
    width: 14px;
    height: 14px;
}

.btn-logout {
    background-color: rgba(239, 68, 68, 0.1);
    border-color: rgba(239, 68, 68, 0.2);
    color: var(--red);
}

.btn-logout:hover {
    background-color: rgba(239, 68, 68, 0.15);
}

.quick-action-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 10px;
}

.quick-action-row .btn {
    margin-bottom: 0;
    flex: 1;
}

/* small "i" info-icon with a tooltip - hover on desktop, click/tap on mobile */
.info-icon {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.08);
    color: var(--text-muted);
    font-size: 12px;
    cursor: pointer;
}

.info-tooltip {
    display: none;
    position: absolute;
    bottom: 130%;
    right: 0;
    width: 280px;
    /* OPAQUE, not the glass token. --card-bg is rgba(255,255,255,0.04) - 4%
       white, which works for a card because the page background sits behind it
       and gets blurred. A tooltip floats ABOVE arbitrary content, so at 4% the
       text underneath showed straight through and white-on-transparent was
       unreadable. --card-bg-solid exists for exactly this case. */
    background: var(--card-bg-solid);
    border: 1px solid var(--border-strong);
    border-radius: 8px;
    padding: 10px 12px;
    font-size: 11px;
    font-weight: 500;
    line-height: 1.5;
    color: var(--text-color);
    text-transform: none;
    letter-spacing: normal;
    z-index: 50;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.65);
}

.info-icon:hover .info-tooltip,
.info-icon:focus .info-tooltip,
.info-icon.info-open .info-tooltip {
    display: block;
}

/* Reusable on/off toggle switch (checkbox-hack: real <input> stays
   accessible/keyboard-operable, visually replaced by the pill+knob). */
.toggle-switch {
    position: relative;
    display: inline-block;
    width: 40px;
    height: 22px;
    flex-shrink: 0;
}
.toggle-switch input {
    position: absolute;
    opacity: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    cursor: pointer;
}
.toggle-slider {
    position: absolute;
    inset: 0;
    background: rgba(255, 255, 255, 0.15);
    border-radius: 22px;
    transition: background-color 0.2s;
    pointer-events: none;
}
.toggle-slider::before {
    content: "";
    position: absolute;
    height: 16px;
    width: 16px;
    left: 3px;
    top: 3px;
    background: #fff;
    border-radius: 50%;
    transition: transform 0.2s;
}
.toggle-switch input:checked + .toggle-slider {
    background: var(--primary);
}
.toggle-switch input:checked + .toggle-slider::before {
    transform: translateX(18px);
}
.toggle-switch input:focus-visible + .toggle-slider {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

.last-updated {
    font-size: 10px;
    color: var(--text-muted);
    text-align: center;
    margin-top: 10px;
}

/* Main Panel Styling */
.main-content {
    padding: 24px;
    display: flex;
    flex-direction: column;
    gap: 24px;
    height: 100vh;
    overflow-y: auto;
}

/* Tabs active logic */
.main-tab-content {
    display: none;
}

.main-tab-content.active-tab {
    display: flex;
    flex-direction: column;
    gap: 24px;
    /* min-height, NOT height: with a hard height the tab can never be taller
       than the viewport, so every card inside it had to fit - and flex items
       shrink by default, so they were squashed instead of the page scrolling.
       Cards carry overflow:hidden, which turned "squashed" into "content
       clipped away": the rides list vanished when the analysis panel opened,
       the maps went flat, and the alerts table showed a header with no rows.
       min-height lets the tab grow and .main-content (overflow-y:auto) scroll. */
    min-height: 100%;
}

/* The other half of the same fix: a card must never be compressed below its
   content to make the column fit. Anything that should fill leftover space
   opts in with flex-grow instead. */
.main-tab-content.active-tab > * {
    flex-shrink: 0;
}

/* Status Cards Row */
.status-cards {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 16px;
}

.card {
    position: relative;
    background-color: var(--card-bg);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    padding: var(--space-4);
    display: flex;
    gap: 14px;
    align-items: center;
    box-shadow: var(--shadow);
    backdrop-filter: blur(6px);
    transition: border-color 0.2s, box-shadow 0.2s;
}

.card:hover {
    border-color: var(--border-strong);
}

/* Stage 26 (PLAN_WIDGET_HISTORY.md): only widgets backed by telemetry_log
   (battery, tracker temp/pressure/humidity) get this - the visual cue that
   distinguishes "clickable, opens history" from the static tiles. */
.widget-clickable {
    cursor: pointer;
}
.widget-clickable:hover {
    border-color: var(--primary);
    box-shadow: 0 0 0 1px var(--primary);
}

/* #13f: customize toolbar + drag&drop for widgets */
.widget-toolbar {
    display: flex;
    justify-content: flex-end;
}

.widget-drag-handle, .widget-visibility-toggle {
    display: none;
    position: absolute;
    top: 6px;
    z-index: 2;
    font-size: 13px;
    color: var(--text-muted);
}

.widget-drag-handle {
    left: 8px;
    cursor: grab;
}

.widget-visibility-toggle {
    right: 8px;
    background: transparent;
    border: none;
    cursor: pointer;
    padding: 4px;
}

.widget-grid.customize-mode .card {
    border-style: dashed;
    padding-top: 26px;
}

.widget-grid.customize-mode .widget-drag-handle,
.widget-grid.customize-mode .widget-visibility-toggle {
    display: block;
}

.widget-grid .widget-user-hidden {
    display: none;
}

.widget-grid.customize-mode .widget-user-hidden {
    display: flex;
    opacity: 0.4;
}

.card-icon-box {
    padding: 12px;
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.card-icon-box svg {
    width: 22px;
    height: 22px;
}

.card-icon-box.orange { background-color: rgba(249, 115, 22, 0.1); color: var(--orange); }
.card-icon-box.green { background-color: rgba(16, 185, 129, 0.1); color: var(--primary); }
.card-icon-box.blue { background-color: rgba(59, 130, 246, 0.1); color: var(--blue); }
.card-icon-box.purple { background-color: rgba(139, 92, 246, 0.1); color: var(--purple); }
.card-icon-box.red { background-color: rgba(239, 68, 68, 0.1); color: var(--red); }

.card-data .label {
    display: block;
    font-size: 11px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
}

.card-data .value {
    font-size: 20px;
    font-weight: 800;
    margin-top: 4px;
}

.card-data .value small {
    font-size: 12px;
    color: var(--text-muted);
    font-weight: 500;
}

.card-data .sub-value {
    display: block;
    font-size: 10px;
    color: var(--text-muted);
    margin-top: 3px;
}

/* #13b/c/d: live status, graphical battery, signal bars, generic gauge */
.battery-graphic-box, .signal-bars-box {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 10px 14px;
    background: rgba(0, 0, 0, 0.15);
    border-radius: 10px;
}

.signal-bar {
    fill: rgba(255, 255, 255, 0.12);
    transition: fill 0.3s;
}

.signal-bar.filled {
    fill: var(--success);
}

.gauge-card {
    flex-direction: column;
    align-items: stretch;
}

.gauge-box {
    position: relative;               /* anchor for the centered readout overlay */
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-top: 4px;
}

/* Digital readout: centered inside the arc opening (no needle to overlap) */
.gauge-readout {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    padding-bottom: 8px;
    pointer-events: none;
}

.gauge-value {
    font-family: 'SF Mono', 'Menlo', 'Consolas', monospace;
    font-size: 30px;
    font-weight: 800;
    line-height: 1;
    letter-spacing: 0.5px;
    color: var(--primary);
    text-shadow: var(--glow-primary);
    font-variant-numeric: tabular-nums;
}

.gauge-value small {
    font-family: inherit;
    font-size: 12px;
    color: var(--text-muted);
    font-weight: 500;
    text-shadow: none;
}

.gauge-label {
    font-size: 10px;
    color: var(--text-muted);
    text-transform: uppercase;
    font-weight: 700;
    letter-spacing: 0.5px;
    margin-top: 5px;
}

.gauge-peak-label {
    letter-spacing: 0.5px;
}

/* Grid Layout */
.dashboard-grid {
    display: grid;
    grid-template-columns: 1fr 340px;
    gap: 20px;
    flex-grow: 1;
}

/* No height here on purpose. This card used to be sized by the leftover
   height of a flex/grid parent, which made it collapse to a flat strip as
   soon as anything above it grew (the widget row gained tiles). The maps now
   carry explicit, per-breakpoint heights instead - predictable at any width,
   and independent of what else is on the page. */
.map-card {
    display: flex;
    flex-direction: column;
    padding: 0;
    overflow: hidden;
}

/* Ride History: rides list and route map side by side on the top row, with
   the analysis charts full width underneath. The map is a stretched grid item
   (no height of its own), so it always matches the height of the list next to
   it however many rows per page are shown - which is what makes it a usable
   map instead of the flat strip it was in the old 340px side column. */
.rides-and-map {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px;
    align-items: stretch;
}

/* Fills the height the grid row gives it (the list's height), with a floor so
   a single-row list can't reduce the map to nothing. height:auto is explicit
   because the generic "#map, #history-map" breakpoint rules set a fixed one,
   and here the flex box is what should govern. */
.rides-and-map #history-map {
    height: auto;
    flex: 1 1 auto;
    min-height: 460px;
}

/* Stretched by the grid row, so the map is as tall as the rides list beside
   it - deliberately NOT align-self:start, which would shrink it back to its
   content height. The cap stops a 50-row list from producing an absurdly
   tall map, and because the grid AREA stays that tall, position:sticky then
   has room to keep the map in view while the list scrolls past it. */
.rides-and-map .map-card {
    position: sticky;
    top: 0;
    max-height: calc(100vh - 48px);
    z-index: 1;
}

@media (max-width: 1024px) {
    /* Stacked: list first, then the map, then the charts. The map keeps a
       real height here rather than matching a sibling that is no longer
       beside it, and sticky is dropped - pinning a map on a phone screen
       costs more than it gives. */
    .rides-and-map {
        grid-template-columns: 1fr;
    }
    .rides-and-map .map-card {
        position: static;
        max-height: none;
    }
    .rides-and-map #history-map {
        min-height: 320px;
    }
}

@media (max-width: 600px) {
    .rides-and-map #history-map {
        min-height: 260px;
    }
}

.card-header {
    padding: 16px 20px;
    border-bottom: 1px solid var(--border-color);
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
}

.card-header h3 {
    font-size: 14px;
    font-weight: 700;
    display: flex;
    align-items: center;
    gap: 8px;
}

.card-header h3 svg {
    width: 16px;
    height: 16px;
    color: var(--primary);
}

.gps-coords {
    font-size: 11px;
    font-weight: 600;
    color: var(--text-muted);
    font-family: monospace;
}

/* Explicit height, not "whatever is left over" - the live map is the main
   thing on this tab and used to end up a flat strip whenever the widget row
   above it grew. */
#map {
    width: 100%;
    height: 560px;
    background-color: #1a2230;
}

/* "Where you ride the most" - same story as the maps above. */
#stats-heatmap {
    height: 480px;
}

@media (max-width: 1024px) {
    #stats-heatmap { height: 360px; }
}

/* Leaflet Map adjustments to match dark mode */
.leaflet-container {
    background: #0f172a !important;
}
/* Dark map is now opt-in via the per-map toggle (adds .map-dark on the
   container). Default = natural tiles, matching the example theme's LiveMap. */
.leaflet-container.map-dark .leaflet-tile {
    filter: invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%);
}
.map-style-toggle { display: inline-flex; align-items: center; gap: 5px; }
.card-header-right { display: flex; align-items: center; gap: 10px; }

/* --- Themed <select> (Motorcycle make etc.) matching the dark inputs --- */
.form-group select,
select.themed-select {
    width: 100%;
    background-color: rgba(0, 0, 0, 0.25);
    border: 1px solid var(--glass-border);
    padding: 10px 36px 10px 12px;
    border-radius: var(--radius-sm);
    color: var(--text-color);
    font-size: 13px;
    outline: none;
    cursor: pointer;
    color-scheme: dark;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%239aa4b2' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    transition: border-color 0.2s, box-shadow 0.2s;
}
.form-group select:focus, select.themed-select:focus {
    border-color: var(--primary);
    box-shadow: var(--focus-ring);
}
.form-group select option { background: var(--card-bg-solid); color: var(--text-color); }

/* --- Reusable table pagination + page-size selector (rides, alerts) --- */
.table-pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 14px;
    padding: 12px 16px;
    font-size: 12px;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}
.table-pagination .btn-table-action:disabled { opacity: 0.4; cursor: not-allowed; }
.page-size-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color: var(--text-muted);
}
.page-size-label select {
    width: auto;
    padding: 4px 26px 4px 8px;
    font-size: 12px;
    font-weight: 700;
    background-color: rgba(0, 0, 0, 0.25);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    color: var(--text-color);
    color-scheme: dark;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    cursor: pointer;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%239aa4b2' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 8px center;
}
.page-size-label select option { background: var(--card-bg-solid); }

/* A tile that is one column wide (like every other) but spans two rows, for
   content that needs the height - currently the lean graphic and its two
   readouts. */
.widget-grid .widget-tall {
    grid-row: span 2;
    flex-direction: column;
    align-items: stretch;
    justify-content: center;
}

.lean-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    width: 100%;
}

/* Stacked, not side by side: in a single-column tile the two readouts have
   vertical room to spare and no horizontal room at all. */
.widget-tall .lean-values {
    flex-direction: column;
    gap: 10px;
    margin-top: 4px;
}

/* .lean-widget-box is gone with the standalone lean card - the lean graphic
   is a widget tile now (.lean-tile above) and needs no inset box of its own,
   the tile IS the box. */
.lean-display {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
}

.lean-values {
    display: flex;
    gap: 24px;
    margin-top: 8px;
}

.lean-val-item {
    text-align: center;
}

.lean-val-item .label {
    display: block;
    font-size: 9px;
    font-weight: 700;
    text-transform: uppercase;
    color: var(--text-muted);
}

.lean-val-item .value {
    font-size: 16px;
    font-weight: 800;
    color: var(--primary);
}

/* .diag-details-list is gone with the "Live Telemetry" card (#7). The ROW
   style below outlived it: the firmware panel still uses .diag-detail-item
   for its version rows. */
.diag-detail-item {
    display: flex;
    justify-content: space-between;
    padding: 10px 14px;
    background-color: rgba(0, 0, 0, 0.15);
    border: 1px solid var(--border-color);
    border-radius: 6px;
    font-size: 12px;
}

.diag-detail-item .label {
    color: var(--text-muted);
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 6px;
}

.diag-detail-item .label svg {
    width: 14px;
    height: 14px;
}

.diag-detail-item .value {
    font-weight: 700;
}

/* HISTORIC TAB & MAINTENANCE TAB TABLE */
.list-card {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    padding: 0;
    overflow: hidden;
}

.table-wrapper {
    overflow-x: auto;
    width: 100%;
}

.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
    text-align: left;
}

.data-table th, .data-table td {
    padding: 12px 18px;
    border-bottom: 1px solid var(--border-color);
}

.data-table th {
    background-color: rgba(0, 0, 0, 0.15);
    font-weight: 700;
    text-transform: uppercase;
    font-size: 10px;
    color: var(--text-muted);
    letter-spacing: 0.5px;
}

.data-table tbody tr {
    cursor: pointer;
    transition: background 0.2s;
}

.data-table tbody tr:hover {
    background-color: rgba(255, 255, 255, 0.02);
}

.btn-table-action {
    background-color: var(--primary-glow);
    color: var(--primary);
    border: 1px solid rgba(249, 115, 22, 0.25);
    padding: 4px 10px;
    border-radius: 4px;
    font-size: 11px;
    font-weight: 700;
    cursor: pointer;
    transition: all 0.2s;
}

.btn-table-action:hover {
    background-color: var(--primary);
    color: var(--bg-color);
}

.btn-table-action.btn-maint-reset {
    background-color: rgba(59, 130, 246, 0.1);
    color: var(--blue);
    border-color: rgba(59, 130, 246, 0.2);
}

.btn-table-action.btn-maint-reset:hover {
    background-color: var(--blue);
    color: #fff;
}

.form-card {
    display: flex;
    flex-direction: column;
    align-items: stretch;
}

/* #13a / Settings restructure: vertical section nav + a content pane of
   section cards. Classes stay distinct from .nav-item/.main-tab-content so
   they don't interfere with switchMainTab().

   Replaces the old horizontal .settings-subnav + a single 520px-wide
   .settings-card. That max-width was why Settings rendered as a narrow
   ribbon with half the screen empty while every other tab was full-width. */
.settings-shell {
    display: grid;
    /* The content column is capped rather than 1fr: on a 2800px screen an
       uncapped column stretched every card until its few fields looked lost,
       and a one-card section (Firmware, Integrations, Account) spanned the
       whole display. */
    grid-template-columns: 200px minmax(0, 1400px);
    justify-content: start;
    gap: var(--space-5);
    align-items: start;
}

.settings-nav {
    display: flex;
    flex-direction: column;
    gap: 2px;
    position: sticky;
    top: 0;
}

/* Same typography as the old .settings-subnav-item (12px uppercase 700), but
   a vertical rail: active state moves from border-bottom to border-left. */
.settings-nav-item {
    background: transparent;
    border: none;
    border-left: 2px solid transparent;
    padding: 10px var(--space-3);
    text-align: left;
    color: var(--text-muted);
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    cursor: pointer;
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    transition: all 0.2s;
}

.settings-nav-item:hover {
    color: var(--text-color);
    background: rgba(255, 255, 255, 0.03);
}

.settings-nav-item.active {
    color: var(--primary);
    border-left-color: var(--primary);
    background: var(--primary-glow);
}

.settings-pane {
    display: none;
}

.settings-pane.active-pane {
    display: block;
}

.settings-pane-title {
    font-size: 18px;
    font-weight: 700;
    margin: 0 0 var(--space-2);
}

.settings-pane-intro {
    margin: 0 0 var(--space-4);
    max-width: 70ch;
}

/* Only ever visible on the phone drill-down; see the 600px block below. */
.settings-back {
    display: none;
    background: transparent;
    border: none;
    color: var(--primary);
    font-size: 13px;
    font-weight: 700;
    padding: var(--space-2) 0;
    cursor: pointer;
}

/* Explicit column counts, NOT auto-fit: the number of tracks has to be fixed
   per width band, otherwise cards of very different field counts land in a
   varying number of columns and the rows read as ragged. align-items:start
   keeps each card at its natural height instead of stretching it to the
   tallest one in its row. */
/* Two columns maximum, and cards in a row share a height.
   Panes hold 1, 2, 3 or 4 cards; two columns divides all four cases evenly
   (4 = 2x2, 3 = 2+1, 2 = one row), while three columns turned the two
   four-card panes into 3 + a lone orphan with two empty columns beside it.
   Stretch (the default) is what actually aligns the card bottoms - with
   align-items:start every card kept its own height, so a row was as tall as
   its tallest member and left a void under each shorter one. */
.settings-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-4);
}

@media (min-width: 1100px) {
    .settings-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Card look borrowed from .card, but a COLUMN: .card itself is display:flex
   in ROW direction, so reusing it here would lay the form fields out
   side by side. */
.settings-section {
    display: flex;
    flex-direction: column;
    background-color: var(--card-bg);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    padding: var(--space-4);
    box-shadow: var(--shadow);
    backdrop-filter: blur(6px);
}

/* The per-source log matrix is a wide table; let it take the full row. */
.settings-section-wide {
    grid-column: 1 / -1;
}

.settings-section-title {
    font-size: 14px;
    font-weight: 700;
    margin: 0 0 var(--space-3);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--border-color);
}

/* There was no helper-text class: every hint in Settings carried the same
   inline font-size/colour by hand. One class instead. */
.settings-hint {
    display: block;
    font-size: 12px;
    line-height: 1.5;
    color: var(--text-muted);
    margin-top: 6px;
}

/* Boolean settings use the existing .toggle-switch component instead of a
   native checkbox: the OS default renders bright blue, which was the single
   most jarring thing on a dark orange-accented page. The id stays on the
   real <input>, so every saver keeps finding it. */
.settings-toggle {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

/* Sentence case, not the 10px uppercase used for field labels. These run
   long ("Show live ECU data (RPM, gear, temps, throttle, fault codes...)")
   and uppercase turned them into a shouting two-line block that pushed the
   switch out of line with its own text. Specificity has to beat
   `.form-group label`. */
.form-group .settings-toggle-label {
    display: block;
    margin-bottom: 0;
    font-size: 13px;
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
    line-height: 1.45;
    color: var(--text-color);
    cursor: pointer;
}

/* A changed-but-unsaved field marks itself, so "2 unsaved changes" is not
   a riddle. The bar still names them too - a dirty field can be inside a
   collapsed <details> and therefore invisible no matter how it is styled. */
.settings-panes .form-group.is-dirty {
    position: relative;
    margin-left: calc(-1 * var(--space-3));
    padding-left: calc(var(--space-3) - 2px);
    border-left: 2px solid var(--primary);
}

.settings-panes .form-group.is-dirty > label,
.settings-panes .form-group.is-dirty > .settings-toggle-label {
    color: var(--primary);
}

.settings-savebar-text {
    line-height: 1.4;
}

.settings-range-scale {
    display: flex;
    justify-content: space-between;
    font-size: 11px;
    color: var(--text-muted);
    margin-top: 2px;
}

/* Sticky save bar. Lives at .settings-shell level, so there is exactly one
   regardless of how many cards the active pane has, and one editing session
   becomes one MQTT publish instead of one per card. */
.settings-savebar {
    grid-column: 2;
    position: sticky;
    bottom: 0;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-top: var(--space-4);
    padding: var(--space-3) var(--space-4);
    background: var(--glass-bg-strong);
    backdrop-filter: blur(var(--glass-blur));
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    z-index: 5;
}

.settings-savebar-text {
    flex: 1;
    font-size: 13px;
    font-weight: 700;
}

/* .btn is width:100% by default, so both buttons would fill the bar. */
.settings-savebar-btn {
    width: auto;
    margin-bottom: 0;
    padding: 10px var(--space-4);
}

/* Sliders had no styling at all - the generic `.form-group input` rule gave
   them a text-input background, border and padding, which is why they looked
   broken. Reset that, then draw a real track and thumb. */
.form-group input[type="range"] {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    background: transparent;
    border: none;
    padding: 0;
    margin: 6px 0 2px;
    height: 20px;
    cursor: pointer;
}

.form-group input[type="range"]::-webkit-slider-runnable-track {
    height: 4px;
    border-radius: 2px;
    background: var(--border-strong);
}

.form-group input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 20px;
    height: 20px;
    margin-top: -8px;
    border-radius: 50%;
    background: var(--primary);
    border: 2px solid var(--card-bg-solid);
    box-shadow: var(--shadow-sm);
}

.form-group input[type="range"]::-moz-range-track {
    height: 4px;
    border-radius: 2px;
    background: var(--border-strong);
}

.form-group input[type="range"]::-moz-range-thumb {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--primary);
    border: 2px solid var(--card-bg-solid);
    box-shadow: var(--shadow-sm);
}

.form-group input[type="range"]:focus-visible::-webkit-slider-thumb {
    box-shadow: var(--focus-ring);
}

.form-group input[type="range"]:focus-visible::-moz-range-thumb {
    box-shadow: var(--focus-ring);
}

/* Phone: the nav column becomes the drill-down list. Tapping an item puts
   .drilled on the shell, which hides the list and reveals the pane plus its
   back button. Crucially this is the SAME markup - there is no phone-only
   copy of any field, because every id in here is read by getElementById
   from app.js and a duplicate would silently split the reads and writes. */
@media (max-width: 600px) {
    .settings-shell {
        display: block;
    }

    .settings-nav {
        position: static;
        gap: var(--space-1);
    }

    .settings-nav-item {
        border-left: none;
        border-radius: var(--radius-sm);
        border: 1px solid var(--border-color);
        background: var(--card-bg);
        padding: 14px var(--space-4);
        min-height: 44px;
        font-size: 13px;
    }

    .settings-nav-item::after {
        content: '›';
        float: right;
        color: var(--text-muted);
    }

    .settings-nav-item.active {
        border-left: 1px solid var(--primary);
    }

    .settings-shell.drilled .settings-nav {
        display: none;
    }

    .settings-shell:not(.drilled) .settings-pane {
        display: none;
    }

    .settings-back {
        display: block;
        min-height: 44px;
    }

    /* 16px is the touch-legibility floor. (Note: iOS zoom-on-focus is
       already suppressed by user-scalable=no on the viewport meta, so this
       is about readability, not about preventing the zoom.) */
    .settings-section .form-group input,
    .settings-section .form-group select {
        font-size: 16px;
        padding: 12px;
    }

    /* The switch itself is 40x22, but its label is a click target too, so
       the row as a whole clears the 44px minimum. */
    .settings-toggle {
        min-height: 44px;
    }

    .form-group input[type="range"] {
        height: 44px;
    }

    .form-group input[type="range"]::-webkit-slider-thumb {
        width: 28px;
        height: 28px;
        margin-top: -12px;
    }

    .form-group input[type="range"]::-moz-range-thumb {
        width: 28px;
        height: 28px;
    }

    .settings-savebar {
        margin-left: calc(-1 * var(--space-3));
        margin-right: calc(-1 * var(--space-3));
        border-radius: var(--radius) var(--radius) 0 0;
        flex-wrap: wrap;
    }

    .settings-savebar-text {
        flex-basis: 100%;
    }

    .settings-savebar-btn {
        flex: 1;
    }
}

/* #15: Logs tab - filter, pagination, raw JSON detail */
.logs-filter-bar {
    display: flex;
    gap: 14px;
    align-items: flex-end;
    flex-wrap: wrap;
    padding: 16px 20px;
    border-bottom: 1px solid var(--border-color);
}

.logs-filter-bar .form-group {
    margin-bottom: 0;
    min-width: 140px;
}

.logs-pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 16px;
    padding: 14px;
    font-size: 12px;
    color: var(--text-muted);
}

.logs-details-short {
    font-family: monospace;
    font-size: 11px;
    color: var(--text-muted);
    margin-right: 8px;
}

.logs-raw-json {
    white-space: pre-wrap;
    word-break: break-all;
    font-size: 11px;
    background: rgba(0, 0, 0, 0.2);
    padding: 8px;
    border-radius: 6px;
    margin-top: 6px;
    max-width: 500px;
}

.toast {
    position: fixed;
    bottom: 20px;
    left: 50%;
    transform: translateX(-50%);
    background: var(--primary);
    color: #0f172a;
    padding: 10px 20px;
    border-radius: 30px;
    font-weight: 600;
    font-size: 14px;
    display: none;
    z-index: 1000;
}

.toast.toast-error {
    background: var(--red);
    color: #fff;
}

/* Confirmation modal (replaces the native confirm()) */
.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.6);
    display: none;
    align-items: center;
    justify-content: center;
    z-index: 1100;
}

.modal-overlay.active {
    display: flex;
}

.modal-box {
    background-color: var(--sidebar-bg);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 24px;
    width: 100%;
    max-width: 380px;
    box-shadow: 0 10px 25px rgba(0, 0, 0, 0.4);
}

.modal-box p {
    font-size: 14px;
    line-height: 1.5;
    margin-bottom: 20px;
}

.modal-actions {
    display: flex;
    gap: 10px;
}

.modal-actions .btn {
    flex: 1;
    width: auto;
}

@media (max-width: 1024px) {
    .dashboard-container {
        grid-template-columns: 1fr;
        height: auto;
    }
    .sidebar {
        border-right: none;
        border-bottom: 1px solid var(--border-color);
        height: auto;
    }
    .main-content {
        height: auto;
        /* Not a scroller on a phone - the DOCUMENT scrolls here. On desktop
           .main-content is the one scroll container (100vh, overflow-y:auto)
           and that is right. This block reset the height but left
           overflow-y:auto in place, so on a phone there were TWO nested
           scrollers: the page, and .main-content with a few dozen pixels of
           internal range. iOS latches a swipe onto the innermost scroller
           under the finger and does not hand it on to the page once that
           tiny range runs out - so any swipe starting on content went
           nowhere. Swiping on the open menu's backdrop worked, because that
           overlay sits outside .main-content and scrolls the page directly;
           closing the menu put the finger back on content and the page
           "locked" again. */
        overflow-y: visible;
    }
    .status-cards {
        grid-template-columns: 1fr 1fr;
    }
    .dashboard-grid {
        grid-template-columns: 1fr;
    }
    #map, #history-map {
        height: 420px;
    }
}

/* #14: real-phone breakpoint - the sidebar becomes a drawer over the content,
   opened via the hamburger menu, instead of just collapsed like at 1024px */
@media (max-width: 600px) {
    .mobile-menu-toggle {
        display: flex;
    }

    .sidebar {
        position: fixed;
        top: 0;
        left: 0;
        height: 100vh;
        width: 260px;
        z-index: 1200;
        transform: translateX(-100%);
        transition: transform 0.3s ease;
    }

    .sidebar.mobile-open {
        transform: translateX(0);
    }

    .status-cards {
        grid-template-columns: 1fr;
    }
}

/* ==========================================================================
   P3 — VISUAL REFRESH (a cohesive layer: depth, button system, sidebar
   footer/grouping, focus states). Added at the end so it overrides via cascade.
   ========================================================================== */

/* Background: "bg-moto" radial gradient from example theme (orange + sky glow
   over near-black), replacing the old green/blue depth wash */
/* The `hidden` attribute is only a user-agent `display:none`, so ANY author
   rule that sets `display` on the same element silently beats it and the
   element never hides. That is exactly what happened to the settings save
   bar: `.settings-savebar { display:flex }` kept it on screen permanently,
   an empty "Discard / Save changes" strip on every pane, while the JS
   dutifully set `.hidden = true` and nothing changed. !important so the
   attribute wins over any class, now and for whatever uses it next. */
[hidden] {
    display: none !important;
}

body {
    background-image:
        radial-gradient(1200px 600px at 80% -10%, rgba(249,115,22,0.12), transparent 60%),
        radial-gradient(900px 500px at -10% 110%, rgba(56,189,248,0.10), transparent 60%);
    background-attachment: fixed;
}

/* Buttons: depth on the primary one, a visible focus ring on everything interactive */
.btn-connect {
    background-image: linear-gradient(180deg, #fb923c, var(--primary));
    box-shadow: 0 2px 8px rgba(249,115,22,0.28);
    color: #0a0c12;
}
.btn-connect:hover { background-image: linear-gradient(180deg, var(--primary), var(--primary-hover)); }
.btn:active, .btn-table-action:active, .nav-item:active { transform: translateY(1px); }

.btn:focus-visible,
.btn-table-action:focus-visible,
.nav-item:focus-visible,
.settings-nav-item:focus-visible,
.form-group input:focus-visible,
input:focus-visible, select:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}

/* Input: a glowing focus ring, not just a border */
.form-group input:focus, input:focus, select:focus {
    border-color: var(--primary);
    box-shadow: var(--focus-ring);
}

/* Sidebar: footer pinned to the bottom + group separators */
.sidebar { justify-content: flex-start; }
.quick-actions { margin-top: auto; display: flex; flex-direction: column; }

.sidebar-group-label {
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 1.2px;
    color: var(--text-muted);
    margin-bottom: var(--space-3);
}

.sidebar-footer {
    margin-top: var(--space-4);
    padding-top: var(--space-4);
    border-top: 1px solid var(--border-color);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.sidebar-user {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: 13px;
    font-weight: 700;
    color: var(--text-color);
}
.sidebar-user svg { color: var(--primary); flex-shrink: 0; }

.sidebar-footer .last-updated { font-size: 11px; color: var(--text-muted); }
.sidebar-footer .btn-logout { margin-top: var(--space-2); }

/* Brand: more legible title + aligned badge */
.brand h2 { color: var(--text-color); }

/* Inline code in banners (ADMIN_PASSWORD etc.) */
.insecure-banner code {
    background: rgba(0,0,0,0.25);
    padding: 1px 5px;
    border-radius: 4px;
    font-size: 0.92em;
}

/* ---- P6/P7 charts ---- */
.charts-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: var(--space-4);
}
.chart-card {
    background: var(--card-bg);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    padding: var(--space-4);
    box-shadow: var(--shadow-sm);
}
.chart-title {
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-dim);
    margin-bottom: var(--space-2);
}
.chart-box { width: 100%; min-height: 120px; }
.chart-svg { display: block; overflow: visible; }
.chart-grid { stroke: rgba(255,255,255,0.06); stroke-width: 1; }
.chart-axis-label { fill: var(--text-muted); font-size: 11px; }
.chart-crosshair { stroke: rgba(255,255,255,0.25); stroke-width: 1; stroke-dasharray: 3 3; }
.chart-empty {
    display: flex; align-items: center; justify-content: center;
    height: 120px; color: var(--text-muted); font-size: 13px; font-style: italic;
}
.chart-tooltip {
    position: fixed; z-index: 3000; pointer-events: none;
    background: var(--card-bg-solid); border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm); padding: 6px 10px; box-shadow: var(--shadow);
    font-size: 12px; display: flex; flex-direction: column; gap: 1px;
}
.chart-tooltip strong { color: var(--text-color); font-size: 13px; }
.chart-tooltip span { color: var(--text-muted); font-size: 11px; }

/* ---- P7 stat tiles ---- */
.stat-tile { flex-direction: column; align-items: flex-start; }
.stat-tile .card-data { display: flex; flex-direction: column; gap: 2px; }
.stat-tile .value { font-size: 26px; font-weight: 800; color: var(--text-color); letter-spacing: -0.5px; }
.stat-tile .value small { font-size: 14px; color: var(--text-dim); font-weight: 600; }
.stat-tile .label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-muted); font-weight: 700; }
.stat-tile .sub-value { font-size: 12px; color: var(--text-dim); margin-top: 2px; }

/* ==========================================================================
   EXAMPLE THEME RESKIN — cohesive glass/glow layer ported from the base44
   "example theme" (src/index.css, Layout.jsx, Gauge.jsx, StatTile.jsx).
   Appended last so it wins the cascade. No markup/id changes — presentation
   only. No external deps (CSP-safe): CSS + inline SVG only.
   ========================================================================== */

/* --- Glass surfaces: cards float over the bg-moto gradient with blur --- */
.card {
    background-color: var(--glass-bg);
    border: 1px solid var(--glass-border);
    -webkit-backdrop-filter: blur(var(--glass-blur));
    backdrop-filter: blur(var(--glass-blur));
    box-shadow: var(--shadow), inset 0 1px 0 rgba(255, 255, 255, 0.04);
    transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1),
                border-color 0.25s, box-shadow 0.25s;
}
.card:hover {
    border-color: var(--border-strong);
    transform: translateY(-2px);
    box-shadow: var(--shadow-lg), 0 0 0 1px rgba(249, 115, 22, 0.06);
}
/* Insets inside cards (diag rows, battery/signal boxes) read better with a
   touch more contrast now that the card itself is translucent glass */
.diag-detail-item,
.battery-graphic-box, .signal-bars-box {
    background: rgba(0, 0, 0, 0.22);
}

/* --- Sidebar: translucent glass panel, radial bg shows through --- */
.sidebar {
    background-color: rgba(12, 15, 23, 0.72);
    -webkit-backdrop-filter: blur(16px);
    backdrop-filter: blur(16px);
    border-right: 1px solid var(--glass-border);
}

/* --- Nav: orange pill for the active item, icon lift on hover --- */
.nav-item {
    border-radius: var(--radius);
}
.nav-item svg {
    transition: transform 0.2s ease;
}
.nav-item:hover {
    color: var(--text-color);
    background-color: rgba(255, 255, 255, 0.05);
}
.nav-item:hover svg {
    transform: scale(1.12);
}
.nav-item.active {
    border-left: none;               /* replace the old left-bar with a pill */
    background-color: rgba(249, 115, 22, 0.15);
    color: var(--primary);
    box-shadow: inset 0 0 0 1px rgba(249, 115, 22, 0.18);
}
.nav-item.active svg {
    color: var(--primary);
}

/* --- User footer chip (Layout.jsx glass card) --- */
.sidebar-footer {
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
    margin-top: auto;
}

/* --- Brand logo tile: subtle orange glow --- */
.logo-box {
    box-shadow: 0 0 18px rgba(249, 115, 22, 0.18);
}

/* --- Stat tiles / status cards: tinted gradient + glow per accent color,
       replicating StatTile.jsx's `from-<c>/20 to-<c>/5` look. The colored
       icon box drives the tint via the shared card. --- */
.card-icon-box {
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.05);
    transition: transform 0.25s ease;
}
.card:hover .card-icon-box {
    transform: scale(1.06);
}
.card-icon-box.orange { background: linear-gradient(160deg, rgba(249,115,22,0.22), rgba(249,115,22,0.06)); color: var(--orange); }
.card-icon-box.green  { background: linear-gradient(160deg, rgba(16,185,129,0.22), rgba(16,185,129,0.06)); color: var(--success); }
.card-icon-box.blue   { background: linear-gradient(160deg, rgba(14,165,233,0.22), rgba(14,165,233,0.06)); color: var(--sky); }
.card-icon-box.purple { background: linear-gradient(160deg, rgba(139,92,246,0.22), rgba(139,92,246,0.06)); color: var(--violet); }
.card-icon-box.red    { background: linear-gradient(160deg, rgba(244,63,94,0.22),  rgba(244,63,94,0.06));  color: var(--rose); }

/* Big values pick up tabular figures + a soft glow like the example gauges */
.card-data .value, .stat-tile .value, .gauge-value {
    font-variant-numeric: tabular-nums;
}

/* --- Card header icons + accents follow the orange hero --- */
.card-header h3 svg { color: var(--primary); }
.lean-val-item .value { color: var(--primary); }

/* --- Gauge readout: stronger orange glow (matches Gauge.jsx text-glow) --- */
.gauge-value {
    text-shadow: var(--glow-primary);
}

/* --- Chart cards inherit the glass treatment --- */
.chart-card {
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    -webkit-backdrop-filter: blur(var(--glass-blur));
    backdrop-filter: blur(var(--glass-blur));
}

/* --- Login: bg-moto backdrop + glass card, coherent entry point --- */
.login-wrapper {
    background-color: var(--bg-color);
    background-image:
        radial-gradient(1200px 600px at 80% -10%, rgba(249,115,22,0.14), transparent 60%),
        radial-gradient(900px 500px at -10% 110%, rgba(56,189,248,0.12), transparent 60%);
}
.login-card {
    background-color: var(--glass-bg-strong);
    border: 1px solid var(--glass-border);
    -webkit-backdrop-filter: blur(16px);
    backdrop-filter: blur(16px);
    box-shadow: var(--shadow-lg), 0 0 60px rgba(249, 115, 22, 0.08);
    border-radius: var(--radius-lg);
}
.login-card .logo-box {
    background-color: var(--primary-glow);
}

/* --- Thin scrollbar (example theme .scrollbar-thin) --- */
.main-content::-webkit-scrollbar,
.table-wrapper::-webkit-scrollbar { width: 6px; height: 6px; }
.main-content::-webkit-scrollbar-thumb,
.table-wrapper::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.12);
    border-radius: 9999px;
}
.main-content { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.12) transparent; }

/* --- Toast: success confirmations read green, errors already red --- */
.toast { background: var(--success); }

/* --- Ride history filter bar (quick chips + custom calendar range) --- */
.rides-count {
    font-size: 11px;
    font-weight: 700;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.3px;
}
.rides-filter-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    padding: 12px 20px;
    border-bottom: 1px solid var(--border-color);
}
.rides-filter-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.ride-chip {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid var(--glass-border);
    color: var(--text-dim);
    padding: 5px 12px;
    border-radius: 9999px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    cursor: pointer;
    transition: all 0.2s;
}
.ride-chip:hover { color: var(--text-color); border-color: var(--border-strong); }
.ride-chip.active {
    background: rgba(249, 115, 22, 0.15);
    border-color: rgba(249, 115, 22, 0.35);
    color: var(--primary);
}
.rides-filter-dates { display: flex; align-items: center; gap: 6px; }
.rides-filter-dates input[type="date"] {
    background: rgba(0, 0, 0, 0.25);
    border: 1px solid var(--glass-border);
    color: var(--text-color);
    border-radius: var(--radius-sm);
    padding: 5px 8px;
    font-size: 12px;
    color-scheme: dark;
}
.rides-filter-dates input[type="date"]:focus { border-color: var(--primary); outline: none; box-shadow: var(--focus-ring); }
.rides-filter-dates input::-webkit-calendar-picker-indicator { filter: invert(0.7); opacity: 0.7; cursor: pointer; }
.rides-date-sep { color: var(--text-muted); font-size: 12px; }

/* --- Ride history actions bar (summary + export + bulk delete) --- */
.rides-actions-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    padding: 10px 20px;
    border-bottom: 1px solid var(--border-color);
}
.rides-summary { font-size: 12px; font-weight: 600; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.rides-actions-right { display: flex; gap: 8px; flex-wrap: wrap; }

/* Destructive action styling (bulk + per-row delete) */
.btn-table-action.btn-danger {
    background-color: rgba(239, 68, 68, 0.12);
    color: var(--red);
    border-color: rgba(239, 68, 68, 0.28);
}
.btn-table-action.btn-danger:hover:not(:disabled) { background-color: var(--red); color: #fff; }
.btn-table-action:disabled { opacity: 0.45; cursor: not-allowed; }

/* Checkbox + actions columns */
.data-table th.col-check, .data-table td.col-check { width: 34px; text-align: center; padding-right: 0; }
.data-table td.col-actions { white-space: nowrap; display: flex; gap: 6px; align-items: center; }
.ride-check, #rides-select-all { width: 15px; height: 15px; accent-color: var(--primary); cursor: pointer; }
.data-table tbody tr.row-unack { background-color: rgba(239, 68, 68, 0.06); }
.data-table tbody tr.row-selected { background-color: rgba(249, 115, 22, 0.10); }

/* Stage 26: brief pulse on unacknowledged rows when jumping in from the
   safety-alert banner (see handleSafetyBannerClick/highlightUnackedAlertRows) */
.data-table tbody tr.row-unack-highlight { animation: row-unack-pulse 2.5s ease-out; }
@keyframes row-unack-pulse {
    0%   { background-color: rgba(239, 68, 68, 0.35); }
    100% { background-color: rgba(239, 68, 68, 0.06); }
}

/* Empty-state cell (icon + message) */
.table-empty {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 8px; padding: 32px 12px; color: var(--text-muted); font-size: 13px;
}
.table-empty svg { opacity: 0.5; }

/* --- Ride scrubber under the history map --- */
.map-scrub {
    /* .card centres its children; the bar spans the map's full width and
       never gives up its height to the flex-grown map above it. */
    align-self: stretch;
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 10px 12px 12px;
    border-top: 1px solid var(--border-color);
    background: var(--glass-bg);
}
.map-scrub[hidden] { display: none; }
.map-scrub input[type="range"] { width: 100%; accent-color: var(--primary); height: 28px; margin: 0; cursor: pointer; }
.map-scrub-readout { display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: 13px; font-variant-numeric: tabular-nums; min-height: 20px; }
.msr { white-space: nowrap; color: var(--text-color); font-weight: 600; }
.msr-k { color: var(--text-dim); font-weight: 400; margin-right: 5px; }
.msr-k:empty { display: none; }

/* --- Ride summary tiles + time distributions (renderRideSummary) --- */
.ride-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 10px;
    margin: 4px 0 16px;
}
.ride-tiles:empty { display: none; }
.ride-tile {
    background: var(--glass-bg);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    padding: 10px 12px;
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}
.ride-tile .rt-k { font-size: 12px; color: var(--text-dim); }
.ride-tile .rt-v { font-size: 20px; font-weight: 650; font-variant-numeric: tabular-nums; }
.ride-tile .rt-v small { font-size: 12px; color: var(--text-dim); font-weight: 500; margin-left: 2px; }
.ride-tile .rt-d { font-size: 12px; color: var(--text-muted); overflow-wrap: anywhere; }
.ride-tile.warn { border-color: rgba(245, 158, 11, 0.45); background: rgba(245, 158, 11, 0.08); }
.rt-pill { align-self: flex-start; font-size: 11px; font-weight: 600; border-radius: 999px; padding: 2px 8px; }
.rt-pill.ok { color: var(--emerald); background: var(--success-glow); }
.rt-pill.warn { color: var(--amber); background: rgba(245, 158, 11, 0.14); }

.ride-dist {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 20px;
    margin-top: 16px;
    padding-top: 14px;
    border-top: 1px solid var(--border-color);
}
.ride-dist-block h4 { margin: 0 0 8px; font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-dim); font-weight: 600; }
.rd-row { display: grid; grid-template-columns: 56px 1fr 56px; gap: 10px; align-items: center; font-size: 13px; margin-bottom: 6px; }
.rd-n { color: var(--text-dim); }
.rd-bar { height: 10px; background: rgba(255, 255, 255, 0.05); border-radius: 4px; overflow: hidden; }
.rd-bar i { display: block; height: 100%; background: var(--primary); border-radius: 0 4px 4px 0; }
.rd-s { text-align: right; font-variant-numeric: tabular-nums; }
.rd-note { font-size: 12px; color: var(--text-muted); margin-top: 4px; }

/* --- Ride analysis: metric chips + synchronized scrubbable charts --- */
.ride-metrics {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    padding: 4px 0 14px;
    border-bottom: 1px solid var(--border-color);
    margin-bottom: 12px;
}
.ride-metrics-empty { font-size: 12px; color: var(--text-muted); font-style: italic; }
.metric-chip {
    --chip-color: var(--primary);
    display: inline-flex;
    align-items: center;
    gap: 7px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid var(--glass-border);
    color: var(--text-dim);
    padding: 5px 12px;
    border-radius: 9999px;
    font-size: 12px;
    font-weight: 700;
    cursor: pointer;
    transition: all 0.18s;
}
.metric-chip .metric-dot {
    width: 9px; height: 9px; border-radius: 50%;
    background: var(--chip-color); opacity: 0.4; transition: opacity 0.18s;
}
.metric-chip:hover { color: var(--text-color); border-color: var(--border-strong); }
.metric-chip.active {
    color: var(--text-color);
    /* Fallback for browsers without color-mix(): solid accent border + faint
       tint. The color-mix lines below override where supported. */
    border-color: var(--chip-color);
    background: rgba(255, 255, 255, 0.07);
    border-color: color-mix(in srgb, var(--chip-color) 45%, transparent);
    background: color-mix(in srgb, var(--chip-color) 14%, transparent);
}
.metric-chip.active .metric-dot { opacity: 1; box-shadow: 0 0 8px var(--chip-color); }

.ride-scrub-readout {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    font-weight: 700;
    color: var(--primary);
    font-variant-numeric: tabular-nums;
    margin-bottom: 8px;
    min-height: 18px;
}
.ride-analysis-row { margin-bottom: 6px; }
.ride-row-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    padding: 0 2px 2px;
}
.ride-row-label { font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.4px; }
.ride-row-val { font-size: 13px; font-weight: 800; color: var(--text-color); font-variant-numeric: tabular-nums; }

/* --- Subtle tab entrance animation (whole panel fades/rises in) --- */
@keyframes tabIn {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: no-preference) {
    .main-tab-content.active-tab { animation: tabIn 0.32s cubic-bezier(0.4, 0, 0.2, 1); }

    /* Staggered entrance for the dashboard status tiles when the tab opens.
       Only the card wrappers animate (live value updates mutate their inner
       text, not the elements), so this doesn't re-fire on telemetry ticks. */
    @keyframes cardRise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
    .main-tab-content.active-tab .status-cards > .card { animation: cardRise 0.4s cubic-bezier(0.4, 0, 0.2, 1) both; }
    .main-tab-content.active-tab .status-cards > .card:nth-child(1) { animation-delay: 0.02s; }
    .main-tab-content.active-tab .status-cards > .card:nth-child(2) { animation-delay: 0.06s; }
    .main-tab-content.active-tab .status-cards > .card:nth-child(3) { animation-delay: 0.10s; }
    .main-tab-content.active-tab .status-cards > .card:nth-child(4) { animation-delay: 0.14s; }
    .main-tab-content.active-tab .status-cards > .card:nth-child(5) { animation-delay: 0.18s; }
    .main-tab-content.active-tab .status-cards > .card:nth-child(n+6) { animation-delay: 0.22s; }
}

/* --- Skeleton shimmer (loading placeholders) --- */
@keyframes skShimmer { 0% { background-position: -200px 0; } 100% { background-position: calc(200px + 100%) 0; } }
.skeleton {
    display: inline-block;
    height: 12px;
    border-radius: 6px;
    background: linear-gradient(90deg, rgba(255,255,255,0.05) 25%, rgba(255,255,255,0.12) 37%, rgba(255,255,255,0.05) 63%);
    background-size: 400px 100%;
    animation: skShimmer 1.2s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .skeleton { animation: none; } }

/* ==========================================================================
   MOBILE OPTIMIZATION — phone-first refinements (appended last; wins cascade)
   ========================================================================== */
@media (max-width: 600px) {
    .main-content { padding: 14px; gap: 16px; }
    .mobile-menu-toggle {
        background: var(--glass-bg-strong);
        -webkit-backdrop-filter: blur(12px);
        backdrop-filter: blur(12px);
        border: 1px solid var(--glass-border);
        border-radius: var(--radius);
    }

    /* Compact 2-up tiles instead of one tall column */
    .status-cards { grid-template-columns: 1fr 1fr; gap: 12px; }
    .card { padding: 14px; gap: 10px; }
    .card-icon-box { padding: 9px; }
    .card-icon-box svg { width: 18px; height: 18px; }
    .card-data .value { font-size: 17px; }
    .card-data .label { font-size: 10px; }

    /* Stack the map + telemetry, give the maps a sensible height */
    .dashboard-grid { grid-template-columns: 1fr; gap: 14px; }
    #map, #history-map { height: 320px; min-height: 320px; }
    /* The heatmap is a map too - without this it kept a desktop height that
       eats a whole phone screen. */
    #stats-heatmap { height: 260px; }

    /* Headers + tables breathe less, scroll horizontally when needed */
    .card-header { padding: 14px 16px; }
    .card-header h3 { font-size: 13px; }
    .data-table th, .data-table td { padding: 10px 12px; }
    .rides-filter-bar { padding: 10px 14px; }
    .rides-filter-dates { flex: 1 1 100%; }
    .rides-filter-dates input[type="date"] { flex: 1; min-width: 0; }

    /* Gauge readout a touch smaller on tiny screens */
    .gauge-value { font-size: 26px; }
}

@media (max-width: 380px) {
    .status-cards { grid-template-columns: 1fr; }
}

/* ===== Stage 24: records, dual-value labels, backups ===== */
.records-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 12px;
    padding: 4px;
}
.record-tile {
    background: var(--card-bg);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 14px;
    text-align: center;
    transition: transform .15s ease, border-color .15s ease;
}
.record-tile[onclick]:hover { transform: translateY(-2px); border-color: var(--accent, #f97316); }
.record-icon { font-size: 22px; }
.record-val { font-size: 20px; font-weight: 700; margin-top: 4px; font-variant-numeric: tabular-nums; }
.record-label { font-size: 12px; color: var(--text-muted, #8b93a7); margin-top: 2px; }
.record-sub { font-size: 11px; color: var(--text-dim, #9aa4b2); margin-top: 4px; }
small.dual { color: var(--text-muted, #8b93a7); font-weight: 400; }
.ride-title { color: var(--accent, #f97316); font-weight: 600; }

/* Firmware upload: theme the native file-input "Choose File" button so it
   matches the orange theme instead of the browser's default gray control. */
input[type="file"] {
    color: var(--text-muted, #8b93a7);
    font-size: 13px;
}
input[type="file"]::file-selector-button {
    background: var(--accent, #f97316);
    color: #0a0c12;
    border: none;
    padding: 9px 14px;
    border-radius: 8px;
    font-weight: 600;
    cursor: pointer;
    margin-right: 12px;
    transition: filter .15s ease;
}
input[type="file"]::file-selector-button:hover { filter: brightness(1.08); }

/* ===== Live dashboard: parked vs riding =====================================
   applyDashMode() puts .mode-parked / .mode-riding on #tab-dash, and
   applyEcuVisibility() puts .ecu-hidden there while no OBD-II link is up.
   Everything below is keyed off those classes. Customize mode is exempt from
   all the hiding, so every tile can still be seen and arranged. */

.mobile-topbar { display: none; }
.dash-actions { display: none; }

/* Engine tiles only while the tracker is actually reading the bike. */
#tab-dash.ecu-hidden #widget-container:not(.customize-mode) [data-ecu] { display: none; }

/* Parked: tiles that only mean something in motion. */
#tab-dash.mode-parked #widget-container:not(.customize-mode) [data-widget-id="speed"],
#tab-dash.mode-parked #widget-container:not(.customize-mode) [data-widget-id="lean-angle"],
#tab-dash.mode-parked #widget-container:not(.customize-mode) [data-widget-id="gps-satellites"] {
    display: none;
}

/* Parked: where the bike is and the ride that just ended come first.
   #tab-dash is a flex column, so `order` rearranges without moving markup. */
#tab-dash.mode-parked .widget-toolbar  { order: -5; }
#tab-dash.mode-parked .dash-actions    { order: -4; }
#tab-dash.mode-parked .map-card        { order: -3; }
#tab-dash.mode-parked .last-ride-card  { order: -2; }
/* "Engine off" is obvious when parked; the note is only useful while riding. */
#tab-dash.mode-parked .ecu-summary     { display: none; }
#tab-dash.mode-riding .last-ride-card  { display: none; }

.last-ride-card {
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
}
.last-ride-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.last-ride-head .label {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-muted);
}
.last-ride-when { font-size: 13px; color: var(--text-dim); }
.last-ride-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 18px;
    font-size: 13px;
    color: var(--text-dim);
}
.last-ride-stats b { color: var(--text-color); font-size: 18px; }

.ecu-summary {
    padding: 12px 16px;
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius);
    color: var(--text-dim);
    font-size: 13px;
}

@media (max-width: 600px) {
    /* Burger + bike name + live status: the phone's only view of the state,
       since the sidebar that shows it is hidden behind the burger. */
    .mobile-topbar {
        display: flex;
        align-items: center;
        gap: 12px;
        margin-bottom: 12px;
    }
    .mobile-topbar .mobile-menu-toggle { margin-bottom: 0; flex-shrink: 0; }
    .mobile-topbar-title { display: flex; align-items: center; gap: 10px; min-width: 0; }
    .mobile-bike-name {
        font-weight: 700;
        font-size: 16px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .dash-actions { display: flex; flex-wrap: wrap; gap: 8px; }
    .dash-actions .btn-table-action { flex: 1 1 auto; min-height: 40px; }

    /* Customize is rarely used: out of the way at the bottom on a phone. */
    #tab-dash.mode-parked .widget-toolbar,
    #tab-dash.mode-riding .widget-toolbar { order: 10; }
}

/* Carrier credit warning (classifyCarrierSms). Amber, not red: it is a
   warning with time to act, not a failure yet. */
.sim-notice {
    padding: 12px 16px;
    border: 1px solid rgba(245, 158, 11, 0.45);
    background: rgba(245, 158, 11, 0.08);
    border-radius: var(--radius);
    color: var(--text-color);
    font-size: 13px;
}
.sim-notice b { color: var(--amber); }
.sim-notice-when { color: var(--text-muted); font-size: 12px; margin-left: 6px; }
.sim-notice-body { margin-top: 6px; color: var(--text-dim); }
.sim-notice-hint { margin-top: 6px; font-size: 12px; color: var(--text-muted); }
#tab-dash.mode-parked .sim-notice { order: -4; }

/* Maintenance: the edit form moved into a modal, so the table takes the full
   width instead of sharing it with an empty 340px column. */
.dashboard-grid.maintenance-grid { grid-template-columns: 1fr; }

.maint-remaining { white-space: nowrap; }
.maint-remaining.soon { color: var(--amber); }
.maint-remaining.over { color: var(--red); font-weight: 700; }
.maint-bar {
    height: 4px;
    margin-top: 6px;
    width: 120px;
    max-width: 100%;
    border-radius: 2px;
    background: rgba(255, 255, 255, 0.08);
    overflow: hidden;
}
.maint-bar span { display: block; height: 100%; background: var(--success); }
.maint-bar.soon span { background: var(--amber); }
.maint-bar.over span { background: var(--red); }

.maint-modal-actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    margin-top: 8px;
}
.maint-modal-actions .maint-save { width: auto; padding: 10px 24px; }

/* Device settings sync state (renderConfigSync). */
.config-sync {
    padding: 12px 16px;
    margin-bottom: var(--space-4);
    border-radius: var(--radius);
    font-size: 13px;
    line-height: 1.5;
}
.config-sync.pending { border: 1px solid rgba(245, 158, 11, 0.45); background: rgba(245, 158, 11, 0.08); }
.config-sync.pending b { color: var(--amber); }
.config-sync.applied { border: 1px solid rgba(16, 185, 129, 0.4); background: var(--success-glow); }
.config-sync.applied b { color: var(--success); }
/* A field whose new value the tracker has not picked up yet. */
.settings-panes .form-group.is-pending > label::after,
.settings-panes .form-group.is-pending > .settings-toggle-label::after {
    content: ' ⏳ pending';
    color: var(--amber);
    font-weight: 700;
    text-transform: none;
    letter-spacing: 0;
}

/* --- Sign in with Google (login screen + Settings -> Account) --- */
.login-google { margin-top: 16px; }
.login-divider { display: flex; align-items: center; gap: 10px; color: var(--text-muted); font-size: 12px; margin-bottom: 12px; }
.login-divider::before, .login-divider::after { content: ""; flex: 1; height: 1px; background: var(--border-color); }
.btn-google {
    display: inline-flex; align-items: center; justify-content: center; gap: 10px;
    width: 100%; padding: 10px 14px; border-radius: var(--radius-sm);
    background: #ffffff; color: #1f1f1f; border: 1px solid #dadce0;
    font-weight: 600; font-size: 14px; text-decoration: none; cursor: pointer;
}
.btn-google:hover { background: #f7f8f8; }
.btn-google:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.account-google-actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-top: 14px; }
.account-google-actions .btn-google { width: auto; }

/* --- Settings -> Users (2.6.0) --- */
.users-section { grid-column: 1 / -1; }
.users-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.users-table th { text-align: left; color: var(--text-dim); font-weight: 600; font-size: 12px; padding: 6px 8px; border-bottom: 1px solid var(--border-color); }
.users-table td { padding: 8px; border-bottom: 1px solid var(--border-color); vertical-align: middle; }
.users-table tr.user-disabled td { opacity: 0.55; }
.users-actions { white-space: nowrap; text-align: right; }
.temp-password { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 20px; letter-spacing: .06em;
    padding: 12px; border-radius: var(--radius-sm); background: rgba(255,255,255,0.06); text-align: center; user-select: all; }
.table-scroll { overflow-x: auto; }
.users-table select { background: var(--card-bg-solid); color: var(--text-color); border: 1px solid var(--border-strong);
    border-radius: 6px; padding: 4px 8px; font: inherit; font-size: 13px; }
.users-table select:disabled { opacity: 0.6; }

/* --- Google invitations (2.6.2) --- */
.login-invite { margin-bottom: 16px; }
.login-invite p { color: var(--text-color); font-size: 14px; line-height: 1.45; margin: 0 0 12px; }
.invite-link { font-size: 13px; word-break: break-all; text-align: left; letter-spacing: 0; }
.invite-share { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; align-items: center; }
.invite-share a.btn-table-action { text-decoration: none; display: inline-block; }
.invites-title { margin: 14px 0 8px; font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-dim); }
.invite-row { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 6px 0; border-bottom: 1px solid var(--border-color); font-size: 13px; }
.login-invite .login-divider { margin-top: 18px; }
