/* Glovebox — night garage design system.
   Dark-first: warm charcoal surfaces, selectable accent color (blue by default).

   Structure:
     1. @font-face: self-hosted Latin fonts (no Google Fonts, no CDN)
     2. Design tokens (:root) — type scale, spacing, radius, colors
     3. Theme variants (light/dark) via [data-theme] on <html>
     4. Accent variants (amber/teal/blue/crimson/violet) via [data-accent]
     5. Global resets + base element styles
     6. Shell (topbar, tabs, bottom-nav, page-head)
     7. Components (cards, tables, badges, buttons, forms, modals, toasts, charts, lists)
     8. Responsive overrides (@media max-width / pointer:coarse)

   The CSS uses custom properties for everything theme-able, so light/dark
   and the five accents are all just variable swaps — no rule duplication.
   Numbers stay in CSS variables (not Tailwind-style utilities) so the
   rendering code in app.js stays declarative.
*/

@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 500 800;
  font-display: swap;
  src: url('../fonts/archivo-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('../fonts/inter-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('../fonts/jetbrainsmono-latin.woff2') format('woff2');
}

/* Default link color */
a {
  color: var(--accent);
}

/* Design tokens — shared across themes. The clamp() values fluidly
   scale with the viewport so text is readable on a phone without being
   huge on a desktop. Spacing, radius, and the font stack are fixed; colors
   are per-theme (see the :root, [data-theme='light'] block below). */
:root {
  /* type */
  --text-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
  --text-sm: clamp(0.875rem, 0.8rem + 0.35vw, 1rem);
  --text-base: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
  --text-lg: clamp(1.125rem, 1rem + 0.75vw, 1.5rem);
  --text-xl: clamp(1.5rem, 1.2rem + 1.25vw, 2.25rem);

  /* spacing */
  --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 0.75rem; --space-4: 1rem;
  --space-5: 1.25rem; --space-6: 1.5rem; --space-8: 2rem; --space-10: 2.5rem;
  --space-12: 3rem; --space-16: 4rem;

  --radius-sm: 0.375rem; --radius-md: 0.5rem; --radius-lg: 0.75rem; --radius-xl: 1rem;
  --radius-full: 999px;
  --transition: 180ms cubic-bezier(0.16, 1, 0.3, 1);

  --font-display: 'Archivo', 'Inter', sans-serif;
  --font-body: 'Inter', 'Helvetica Neue', sans-serif;
  --font-mono: 'JetBrains Mono', 'SFMono-Regular', monospace;
}

/* Light theme colors. The base :root selector makes these the defaults
   if no [data-theme] is set; [data-theme='light'] picks them explicitly
   when the user has chosen light while the OS prefers dark. */
:root, [data-theme='light'] {
  --bg: #f4f3ef;
  --surface: #fdfcf9;
  --surface-2: #efede6;
  --border: #ddd9cf;
  --border-strong: #c9c4b6;
  --text: #28271f;
  --text-muted: #6e6c60;
  --text-faint: #a3a093;
  --success: #3f7a24;
  --success-soft: #dfe8d3;
  --danger: #a83434;
  --danger-soft: #f0dcdc;
  --warn: #9a6a1c;
  --warn-soft: #f1e6cf;
  --shadow-sm: 0 1px 2px rgb(40 38 30 / 0.06);
  --shadow-md: 0 4px 14px rgb(40 38 30 / 0.08);
  --shadow-lg: 0 16px 40px rgb(40 38 30 / 0.14);
  --chart-grid: #e4e1d7;
}

/* Dark theme colors. Warmer than pure black to match the night-garage
   palette — soft charcoal surfaces with warm undertones. */
[data-theme='dark'] {
  --bg: #141413;
  --surface: #1c1b1a;
  --surface-2: #242322;
  --border: #31302e;
  --border-strong: #454341;
  --text: #e9e7e2;
  --text-muted: #a09d94;
  --text-faint: #6e6c65;
  --success: #8bc46a;
  --success-soft: #26301f;
  --danger: #e57070;
  --danger-soft: #382222;
  --warn: #d9a455;
  --warn-soft: #33291a;
  --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.3);
  --shadow-md: 0 4px 14px rgb(0 0 0 / 0.35);
  --shadow-lg: 0 16px 40px rgb(0 0 0 / 0.5);
  --chart-grid: #2b2a29;
}

/* OS-prefers-dark fallback: when the user hasn't explicitly chosen a
   theme, follow the OS. This duplicates the dark values from above so the
   first paint is correct before app.js sets [data-theme]. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --bg: #141413; --surface: #1c1b1a; --surface-2: #242322; --border: #31302e;
    --border-strong: #454341; --text: #e9e7e2; --text-muted: #a09d94; --text-faint: #6e6c65;
    --success: #8bc46a; --success-soft: #26301f; --danger: #e57070; --danger-soft: #382222;
    --warn: #d9a455; --warn-soft: #33291a;
    --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.3); --shadow-md: 0 4px 14px rgb(0 0 0 / 0.35);
    --shadow-lg: 0 16px 40px rgb(0 0 0 / 0.5); --chart-grid: #2b2a29;
  }
}

/* Accent colors — instance-wide, chosen in Settings (default: blue).
   Each accent has three shades: --accent (base), --accent-strong (used
   for text on accent-soft backgrounds in dark mode, where the base
   accent is too light), --accent-soft (the soft tint for badges / KPI
   backgrounds), --on-accent (the readable text color on top of --accent).
   The dark-theme block below overrides each accent's shades so they read
   well on dark surfaces. */
[data-accent='amber']   { --accent:#a8690c; --accent-strong:#8a560a; --accent-soft:#f3e4c8; --on-accent:#fdfcf9; }
[data-accent='teal']    { --accent:#0d7d74; --accent-strong:#0a655e; --accent-soft:#d3ebe7; --on-accent:#fdfcf9; }
[data-accent='blue']    { --accent:#1d5fb8; --accent-strong:#164a93; --accent-soft:#d7e4f6; --on-accent:#fdfcf9; }
[data-accent='crimson'] { --accent:#b03535; --accent-strong:#8f2828; --accent-soft:#f5d9d9; --on-accent:#fdfcf9; }
[data-accent='violet']  { --accent:#7a46b8; --accent-strong:#613693; --accent-soft:#e6daf5; --on-accent:#fdfcf9; }

[data-theme='dark'][data-accent='amber']   { --accent:#e8b04b; --accent-strong:#f2c26b; --accent-soft:#33291a; --on-accent:#1c1a16; }
[data-theme='dark'][data-accent='teal']    { --accent:#55c4b8; --accent-strong:#7ad4ca; --accent-soft:#173230; --on-accent:#101d1b; }
[data-theme='dark'][data-accent='blue']    { --accent:#6da8ee; --accent-strong:#8fbcf2; --accent-soft:#1a2739; --on-accent:#0f1622; }
[data-theme='dark'][data-accent='crimson'] { --accent:#e88585; --accent-strong:#f0a0a0; --accent-soft:#3a1d1d; --on-accent:#201010; }
[data-theme='dark'][data-accent='violet']  { --accent:#b795ea; --accent-strong:#c9adf0; --accent-soft:#2b1f3a; --on-accent:#171021; }

/* Same dark values when the OS prefers dark and no explicit theme is set yet. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme])[data-accent='amber']   { --accent:#e8b04b; --accent-strong:#f2c26b; --accent-soft:#33291a; --on-accent:#1c1a16; }
  :root:not([data-theme])[data-accent='teal']    { --accent:#55c4b8; --accent-strong:#7ad4ca; --accent-soft:#173230; --on-accent:#101d1b; }
  :root:not([data-theme])[data-accent='blue']    { --accent:#6da8ee; --accent-strong:#8fbcf2; --accent-soft:#1a2739; --on-accent:#0f1622; }
  :root:not([data-theme])[data-accent='crimson'] { --accent:#e88585; --accent-strong:#f0a0a0; --accent-soft:#3a1d1d; --on-accent:#201010; }
  :root:not([data-theme])[data-accent='violet']  { --accent:#b795ea; --accent-strong:#c9adf0; --accent-soft:#2b1f3a; --on-accent:#171021; }
}

/* Global reset. box-sizing: border-box so padding/borders don't blow up
   widths. margin/padding zeroed everywhere and re-applied per-component —
   keeps spacing consistent and predictable. */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  -webkit-text-size-adjust: none; text-size-adjust: none;
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  -webkit-tap-highlight-color: transparent;
}
/* Installed PWA: disable iOS rubber-band / pull-to-refresh bounce behind
   the UI so the app feels native. Only applies when actually installed
   (display-mode: standalone) — the browser still gets normal scrolling. */
@media (display-mode: standalone) {
  html, body { overscroll-behavior-y: none; }
}

body {
  min-height: 100dvh;
  line-height: 1.6;
  font-family: var(--font-body);
  font-size: var(--text-base);
  color: var(--text);
  background: var(--bg);
  transition: background 240ms ease, color 240ms ease;
}

/* Lock body scroll when a modal is open. :has() is now widely supported;
   this avoids JS toggling a class on <body> every time a modal opens. */
body:has(.modal-overlay) { overflow: hidden; }

/* Touch ergonomics: manipulation removes the legacy 300ms double-tap-zoom
   delay on touchscreens, so taps feel instant. The user-select:none on
   nav controls stops long-press from popping the iOS callout / text-selection
   menu on buttons. */
/* Touch: no 300ms/double-tap-zoom delay, no text-selection or link-preview on long-press of controls. */
a, button, select, summary, label, .btn, .icon-btn, .chip-btn, .tab { touch-action: manipulation; }
.bottom-nav a, .tab, .btn, .icon-btn, .chip-btn, .accent-option {
  -webkit-touch-callout: none; -webkit-user-select: none; user-select: none;
}

img, svg { display: block; max-width: 100%; height: auto; }
input, button, textarea, select { font: inherit; color: inherit; }

h1, h2, h3 { line-height: 1.15; text-wrap: balance; font-family: var(--font-display); font-weight: 700; }

::selection { background: var(--accent-soft); }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--radius-sm); }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; left: -999px; top: 0; z-index: 200;
  background: var(--accent); color: var(--on-accent);
  padding: var(--space-2) var(--space-4); border-radius: var(--radius-md);
}
.skip-link:focus { left: var(--space-4); }

/* ------------------------------------------------------------------ shell
   Topbar: brand mark + name, vehicle selector, theme toggle. The tabs
   below are sticky on desktop, hidden on mobile (the bottom-nav takes
   over). The translucent backdrop-filter gives the topbar a frosted-glass
   look on desktop; mobile gets an opaque background to save GPU. */

.topbar {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
  padding-top: env(safe-area-inset-top, 0px);
}
/* iOS notched devices: the translucent status bar area would show
   scrolled content underneath the topbar. This fixed pseudo-element paints
   an opaque strip the same color as --bg, sized to the safe-area inset. */
body::before {
  content: ""; position: fixed; inset: 0 0 auto; height: env(safe-area-inset-top, 0px);
  background: var(--bg); z-index: 60; pointer-events: none;
}
.topbar-inner {
  max-width: 1160px; margin: 0 auto;
  padding: var(--space-3) max(var(--space-5), env(safe-area-inset-right, 0px)) var(--space-3) max(var(--space-5), env(safe-area-inset-left, 0px));
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
}
.brand { display: flex; align-items: center; gap: var(--space-3); }
.brand-mark { width: 30px; height: 30px; }
.brand-bg { fill: var(--accent); rx: 7px; }
.brand-stroke { stroke: var(--on-accent); }
.brand-name {
  font-family: var(--font-display); font-weight: 800;
  font-size: var(--text-lg); letter-spacing: -0.01em;
}
.topbar-actions { display: flex; align-items: center; gap: var(--space-3); min-width: 0; flex: 1; justify-content: flex-end; }

.vehicle-select {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm); font-weight: 500;
  min-width: 0; max-width: 280px; flex: 0 1 auto;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  text-overflow: ellipsis;
}
.vehicle-select:hover { border-color: var(--border-strong); }

.icon-btn {
  width: 40px; height: 40px; display: grid; place-items: center;
  border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--surface); color: var(--text-muted); cursor: pointer;
  box-shadow: var(--shadow-sm);
}
.icon-btn:hover { color: var(--text); border-color: var(--border-strong); }

.tabs {
  max-width: 1160px; margin: 0 auto; padding: 0 var(--space-5);
  display: flex; gap: var(--space-1); overflow-x: auto;
}
.tab {
  padding: var(--space-2) var(--space-3); margin-bottom: calc(-1px);
  font-size: var(--text-sm); font-weight: 500; color: var(--text-muted);
  text-decoration: none; white-space: nowrap;
  border-bottom: 2px solid transparent; border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
.tab:hover { color: var(--text); }
.tab.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }

.main { max-width: 1160px; margin: 0 auto; padding: var(--space-6) var(--space-5) var(--space-16); }
.view.view-enter { animation: view-in 180ms ease-out; }
@keyframes view-in { from { opacity: 0; } }
.view[aria-busy='true'] { cursor: progress; }

.page-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4); flex-wrap: wrap; margin-bottom: var(--space-6);
}
.page-title { font-size: var(--text-xl); letter-spacing: -0.015em; }
.page-sub { color: var(--text-muted); font-size: var(--text-sm); margin-top: var(--space-1); }

/* Mobile breakpoint (≤720px): topbar becomes non-sticky (saves vertical
   space), the desktop tabs hide (the bottom-nav takes over), and the
   vehicle selector shrinks. 16px font-size on the selector is the minimum
   to stop iOS Safari from zooming the page on focus. */
@media (max-width: 720px) {
  .topbar { position: static; backdrop-filter: none; -webkit-backdrop-filter: none; background: var(--bg); }
  .tabs { display: none; }
  .main {
    padding: var(--space-4) max(var(--space-4), env(safe-area-inset-right, 0px))
      calc(var(--space-16) + 56px + env(safe-area-inset-bottom, 0px)) max(var(--space-4), env(safe-area-inset-left, 0px));
  }
  .topbar-inner {
    flex-wrap: nowrap;
    padding-left: max(var(--space-4), env(safe-area-inset-left, 0px));
    padding-right: max(var(--space-4), env(safe-area-inset-right, 0px));
  }
  /* 16px stops iOS Safari zooming the page when the selector is focused */
  .vehicle-select { max-width: 160px; font-size: 16px; }
  .bottom-nav a { font-size: 10px; }
  .bottom-nav a svg { width: 18px; height: 18px; }
}

/* Mobile bottom nav: hidden on desktop, shown ≤720px. Fixed to the
   bottom with safe-area-inset padding so it clears the iOS home indicator.
   translateZ(0) puts it on its own compositor layer so it stays put while
   the page scrolls (otherwise it can lag on iOS). */
/* mobile bottom nav */
.bottom-nav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  display: none;
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding: var(--space-1) max(var(--space-2), env(safe-area-inset-right, 0px)) calc(var(--space-1) + env(safe-area-inset-bottom, 0px)) max(var(--space-2), env(safe-area-inset-left, 0px));
  transform: translateZ(0); /* own compositor layer: stays put while the page scrolls */
}
.bottom-nav a {
  flex: 1; display: grid; place-items: center; gap: 2px;
  padding: var(--space-2) 0; border-radius: var(--radius-md);
  color: var(--text-muted); text-decoration: none;
  font-size: var(--text-xs); font-weight: 500;
}
.bottom-nav a { min-height: 48px; transition: background 120ms ease, color 120ms ease; }
.bottom-nav a:active { background: var(--surface-2); }
.bottom-nav a.active { color: var(--accent); background: var(--accent-soft); }
.bottom-nav svg { width: 20px; height: 20px; }
@media (max-width: 720px) { .bottom-nav { display: flex; } }

/* ------------------------------------------------------------------ cards
   The basic surface container: rounded, bordered, soft shadow. Used for
   KPI tiles, lists, chart boxes, settings sections. .card-pad adds the
   standard interior padding; .card-head is a header row inside a card. */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}
.card-pad { padding: var(--space-5); }
.card-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-3); padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
}
.card-title { font-size: var(--text-base); font-weight: 600; }

.grid { display: grid; gap: var(--space-4); }
.grid-tiles { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }

.stat-tile { padding: var(--space-4) var(--space-5); }
.stat-label {
  font-size: var(--text-xs); font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--text-muted);
}
.stat-value {
  font-family: var(--font-mono); font-size: var(--text-lg); font-weight: 600;
  margin-top: var(--space-1); letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}
.stat-unit { color: var(--text-muted); font-size: var(--text-sm); margin-left: 2px; }
.stat-foot { font-size: var(--text-xs); color: var(--text-faint); margin-top: var(--space-1); }

/* ------------------------------------------------------------------ table
   Data tables for fuel/charge/service history. .table-wrap is a horizontal
   scroll container so wide tables don't blow out the page on phones.
   tabular-nums keeps numeric columns aligned (so 12.5 and 1,234.5 line up).
   .mono uses the JetBrains Mono font for the numeric cells. */

.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; }
table.data { width: 100%; font-size: var(--text-sm); }
table.data th {
  text-align: left; font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--text-muted); font-weight: 600;
  padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
table.data td {
  padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
table.data td.wrap { white-space: normal; }
table.data tr:last-child td { border-bottom: 0; }
table.data td.num, table.data th.num { text-align: right; }
.mono { font-family: var(--font-mono); font-size: 0.9em; }
.muted { color: var(--text-muted); }
.faint { color: var(--text-faint); }
.row-actions { display: flex; gap: var(--space-1); justify-content: flex-end; }

/* ------------------------------------------------------------------ badges
   Pill-shaped status indicators. Color variants pair a soft background
   with a saturated text color, so they're readable on either theme. The
   .badge-full / .badge-partial pair marks fill-ups vs partials;
   .badge-overdue/.badge-soon/.badge-upcoming mark reminder status;
   .badge-cat tags service records with their category. */

.badge {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--text-xs); font-weight: 600; padding: 1px var(--space-2);
  border-radius: var(--radius-full); white-space: nowrap;
}
.badge-full { background: var(--accent-soft); color: var(--accent); }
[data-theme='dark'] .badge-full { color: var(--accent-strong); }
.badge-partial { background: var(--surface-2); color: var(--text-muted); }
.badge-ok { background: var(--success-soft); color: var(--success); }
.badge-overdue { background: var(--danger-soft); color: var(--danger); }
.badge-soon { background: var(--warn-soft); color: var(--warn); }
.badge-upcoming { background: var(--surface-2); color: var(--text-muted); }
.badge-cat { background: var(--surface-2); color: var(--text-muted); text-transform: capitalize; }

/* ------------------------------------------------------------------ buttons
   .btn is the standard button; .btn-primary fills with the accent color;
   .btn-danger-ghost is the red-text variant used for delete confirmations.
   .btn-sm is the table-row inline size; .chip-btn is the filter pill.
   .btn-row is a flex container that wraps on overflow. */

.btn {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-4); border-radius: var(--radius-md);
  font-size: var(--text-sm); font-weight: 600; text-decoration: none;
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
  cursor: pointer; box-shadow: var(--shadow-sm);
}
.btn:hover { border-color: var(--border-strong); }
.btn svg { width: 16px; height: 16px; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn-primary:hover { background: var(--accent-strong); border-color: var(--accent-strong); }
.btn-danger-ghost { color: var(--danger); }
.btn-sm { padding: var(--space-1) var(--space-3); font-size: var(--text-xs); }
.btn-row { display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; }

.chip-btn {
  border: 1px solid var(--border); background: none; color: var(--text-muted);
  border-radius: var(--radius-full); padding: var(--space-1) var(--space-3);
  font-size: var(--text-xs); font-weight: 500; cursor: pointer;
}
.chip-btn:hover { color: var(--text); border-color: var(--border-strong); }
.chip-btn.active { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
[data-theme='dark'] .chip-btn.active { color: var(--accent-strong); }

/* ------------------------------------------------------------------ forms
   Form fields are stacked grids (label over input) so they line up
   consistently. .field-row is a 2-column grid for side-by-side fields
   (date + odometer, quantity + cost). Inputs use the surface-2 background
   so they read as filled wells; focus draws an accent-colored ring. */

.field { display: grid; gap: var(--space-1); margin-bottom: var(--space-4); }
.field label { font-size: var(--text-xs); font-weight: 600; color: var(--text-muted); }
.field input, .field select, .field textarea {
  width: 100%; padding: var(--space-2) var(--space-3);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-md); font-size: var(--text-base);
}
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.field-hint { font-size: var(--text-xs); color: var(--text-faint); }
.field-error { font-size: var(--text-xs); color: var(--danger); }
.check-row { display: flex; align-items: center; gap: var(--space-2); }
.check-row input { width: 18px; height: 18px; accent-color: var(--accent); }
.check-row label { font-size: var(--text-sm); color: var(--text); font-weight: 500; }

/* ------------------------------------------------------------------ modal
   Modals are fixed-position overlays with a translucent backdrop. The
   dialog itself is a card with sticky header and footer (so the close
   button and Save button stay visible while the body scrolls). On mobile
   (≤720px) the modal becomes a bottom sheet (see the responsive block at
   the bottom of the file). The body:has(.modal-overlay) rule at the top
   locks body scroll while a modal is open. */

.modal-overlay {
  position: fixed; inset: 0; z-index: 100;
  background: rgb(10 10 9 / 0.55);
  display: grid; place-items: center; padding: var(--space-4);
  animation: fade-in 160ms ease;
}
.modal {
  width: min(560px, 100%); max-height: 90dvh; overflow-y: auto; overscroll-behavior: contain;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-xl); box-shadow: var(--shadow-lg);
  animation: modal-in 220ms cubic-bezier(0.16, 1, 0.3, 1);
}
.modal-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-4) var(--space-5); border-bottom: 1px solid var(--border);
  position: sticky; top: 0; background: var(--surface);
}
.modal-title { font-size: var(--text-lg); }
.modal-body { padding: var(--space-5); }
.modal-foot {
  display: flex; justify-content: flex-end; gap: var(--space-2);
  padding: var(--space-4) var(--space-5); border-top: 1px solid var(--border);
  position: sticky; bottom: 0; background: var(--surface);
}
@keyframes fade-in { from { opacity: 0; } }
@keyframes modal-in { from { opacity: 0; transform: translateY(12px) scale(0.98); } }

/* ------------------------------------------------------------------ toast
   Transient success/error messages. Auto-stacked (gap), pointer-events:
   none so they don't block clicks underneath. Positioned bottom-center on
   mobile (above the bottom-nav) and bottom-right on desktop. The .err
   variant colors the border + text red. */

.toast-root {
  position: fixed; bottom: calc(var(--space-6) + 56px + env(safe-area-inset-bottom, 0px)); left: 50%; transform: translateX(-50%);
  z-index: 120; display: grid; gap: var(--space-2); width: min(420px, calc(100vw - 32px));
  pointer-events: none;
}
@media (min-width: 721px) { .toast-root { bottom: var(--space-6); } }
.toast {
  background: var(--surface-2); border: 1px solid var(--border-strong);
  border-radius: var(--radius-md); padding: var(--space-2) var(--space-4);
  font-size: var(--text-sm); box-shadow: var(--shadow-md);
  display: flex; align-items: center; gap: var(--space-2);
  animation: toast-in 220ms cubic-bezier(0.16, 1, 0.3, 1);
}
.toast.err { border-color: var(--danger); color: var(--danger); }
@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } }

/* ------------------------------------------------------------------ charts
   The dashboard chart is an inline SVG built by barChart() in app.js.
   .chart-box is the container card; .chart-svg scales to the card width.
   The legend below uses small colored squares (the <i> element) so it
   stays in sync with the bar colors without duplicating color values. */

.chart-box { padding: var(--space-5); }
.chart-svg { width: 100%; height: auto; }
.chart-title {
  font-size: var(--text-xs); font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--text-muted); margin-bottom: var(--space-3);
}
.legend { display: flex; gap: var(--space-4); font-size: var(--text-xs); color: var(--text-muted); margin-top: var(--space-2); }
.legend span { display: inline-flex; align-items: center; gap: var(--space-1); }
.legend i { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }

/* ------------------------------------------------------------------ lists
   The list pattern: rows with an icon on the left, content in the middle,
   and an action area on the right. Used by reminders, recent activity,
   vehicle settings, and the accent picker. minmax(0,1fr) on the grid is
   critical — without it, a long nowrap title would grow the column past
   100% and the whole page would overflow sideways on phones. */

.list { display: grid; grid-template-columns: minmax(0, 1fr); }
.list-item {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-3) var(--space-5);
  border-bottom: 1px solid var(--border);
}
.list-item:last-child { border-bottom: 0; }
.list-main { flex: 1; min-width: 0; }
.list-title { font-weight: 600; font-size: var(--text-sm); }
.list-sub { font-size: var(--text-xs); color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-end { display: grid; gap: var(--space-1); justify-items: end; }
.list-cost { font-family: var(--font-mono); font-size: var(--text-sm); font-weight: 600; }

.activity-icon {
  width: 36px; height: 36px; flex-shrink: 0;
  display: grid; place-items: center; border-radius: var(--radius-md);
  background: var(--surface-2); color: var(--text-muted);
}
.activity-icon svg { width: 18px; height: 18px; }
.activity-icon.fuel { color: var(--accent); }
[data-theme='dark'] .activity-icon.fuel { color: var(--accent-strong); }

/* ------------------------------------------------------------------ empty state
   The friendly placeholder shown when a list is empty ("No fill-ups yet",
   "No reminders", etc.). Centered, muted, with an icon and a CTA button.
   .onboarding is the special variant for the very-first-visit state. */

.empty-state {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  padding: var(--space-16) var(--space-8); color: var(--text-muted);
}
.empty-icon {
  width: 52px; height: 52px; margin-bottom: var(--space-4);
  color: var(--accent); opacity: 0.85;
}
.empty-state h3 { color: var(--text); margin-bottom: var(--space-2); font-size: var(--text-lg); }
.empty-state p { max-width: 38ch; margin-bottom: var(--space-6); font-size: var(--text-sm); }

.onboarding {
  max-width: 520px; margin: var(--space-16) auto; text-align: center;
}
.onboarding .brand-mark { width: 44px; height: 44px; margin: 0 auto var(--space-4); }
.onboarding h1 { font-size: var(--text-xl); margin-bottom: var(--space-2); }
.onboarding p { color: var(--text-muted); margin-bottom: var(--space-6); font-size: var(--text-sm); }
.onboarding form { text-align: left; }

/* ------------------------------------------------------------------ skeleton
   Shimmer placeholder shown while a view's data is loading (after a 250ms
   delay — see render() in app.js). The gradient animates left-to-right to
   suggest progress without committing to a specific ETA. */

.skeleton {
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--border) 50%, var(--surface-2) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
  border-radius: var(--radius-sm);
}
@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

/* ------------------------------------------------------------------ misc
   Odds and ends: .divider (horizontal rule), .section-title (the small
   uppercase label above Settings sections), .kv (the key/value list used
   by the Data exports section), and the prefers-reduced-motion override. */

.divider { border: none; border-top: 1px solid var(--border); margin: var(--space-6) 0; }
.section-title {
  font-size: var(--text-xs); font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--text-muted); margin-bottom: var(--space-3);
}
.kv { display: grid; grid-template-columns: auto 1fr; gap: var(--space-2) var(--space-6); font-size: var(--text-sm); }
.kv dt { color: var(--text-muted); }
.kv dd { text-align: right; }

/* Respect the OS reduced-motion preference: drop animations and
   transitions to near-zero duration. The !important is necessary because
   the original rules use specific selectors that would otherwise win. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ------------------------------------------------------------ accent picker */
.accent-picker { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); }
.accent-option {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3); border: 1px solid var(--border);
  border-radius: var(--radius-md); background: var(--surface-2); color: var(--text);
  font: inherit; font-size: var(--text-sm); font-weight: 600; cursor: pointer;
}
.accent-option:hover { border-color: var(--border-strong); }
.accent-option[aria-pressed='true'] { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
[data-theme='dark'] .accent-option[aria-pressed='true'] { color: var(--accent-strong); }
.accent-swatch { width: 14px; height: 14px; border-radius: 3px; display: inline-block; }

/* Modal head actions row (e.g. the small QR icon in the Add fill-up modal).
 * Right-aligned next to the close button, with a small gap so multiple icons
 * don't crowd. Uses the existing .icon-btn size — keeps the icon unobtrusive
 * and consistent with the close button. */
.modal-head-actions { display: inline-flex; align-items: center; gap: var(--space-1); }

/* Enlarged QR preview overlay (separate from the underlying Add fill-up
 * modal — see showQrPreview in app.js). Stacks ABOVE the existing modal
 * via z-index, so the user can review the QR without losing their form
 * input. On mobile it becomes a bottom sheet like other dialogs. */
.qr-preview-overlay { z-index: 200; }
.qr-preview { width: min(420px, 100%); }
.qr-preview-body { text-align: center; }
.qr-preview-frame {
  display: inline-block;
  padding: var(--space-4);
  background: #fff;            /* white background so the QR scans reliably */
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  margin: 0 auto var(--space-4);
}
.qr-preview-frame img { display: block; width: 280px; height: 280px; image-rendering: pixelated; }
.qr-preview-caption { margin: 0 0 var(--space-2); font-size: var(--text-sm); color: var(--text-muted); }
.qr-preview-url {
  margin: 0 auto; font-size: var(--text-xs); word-break: break-all;
  max-width: 360px; font-family: var(--font-mono); color: var(--text-faint);
}

/* ------------------------------------------------------------ touch & small screens
   Two separate concerns:
   - pointer: coarse: touchscreens get finger-sized tap targets (44px) and
     a press-scale animation (since :hover doesn't exist there)
   - max-width: 720px: small screens get bottom-sheet modals, 16px form
     inputs (to stop iOS Safari zooming on focus), and tighter page heads. */

/* Finger-sized targets and press feedback on touch screens (hover doesn't exist there). */
@media (pointer: coarse) {
  .btn { min-height: 44px; }
  .btn-sm { min-height: 40px; padding: var(--space-2) var(--space-4); }
  .chip-btn { min-height: 40px; padding: var(--space-2) var(--space-4); }
  .icon-btn { width: 44px; height: 44px; }
  .row-actions .icon-btn { width: 40px !important; height: 40px !important; } /* inline 32px sizes need !important; 40px keeps list rows from squeezing titles */
  .accent-option { min-height: 44px; }
  .field input, .field select, .field textarea { min-height: 44px; }
  .btn:active, .icon-btn:active, .chip-btn:active, .accent-option:active { transform: scale(0.97); }
  .btn, .icon-btn, .chip-btn, .accent-option { transition: transform 90ms ease; }
}

@media (max-width: 720px) {
  /* Forms must be >=16px or iOS zooms in on focus. */
  .field input, .field select, .field textarea { font-size: 16px; }

  /* Dialogs become bottom sheets: reachable by thumb, keyboard-friendly, safe-area aware. */
  .modal-overlay { place-items: end center; padding: 0; }
  .modal {
    width: 100%; max-height: 92dvh;
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    border-bottom: 0;
    animation: sheet-in 240ms cubic-bezier(0.16, 1, 0.3, 1);
  }
  .modal-foot { padding-bottom: calc(var(--space-4) + env(safe-area-inset-bottom, 0px)); }
  .modal-foot .btn { flex: 1; justify-content: center; }
  .page-head { margin-bottom: var(--space-4); }
}
@keyframes sheet-in { from { transform: translateY(100%); } }

/* ------------------------------------------------------------ export dialog
   The export modal (CSV vs PDF choice). Each option is a large tappable
   row with an icon, title, and subtitle. The icon tile uses accent-soft
   for the background so it ties into the brand color. */
.export-options { display: grid; gap: var(--space-3); }
.export-opt {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--surface-2); color: var(--text); text-decoration: none;
}
.export-opt:hover { border-color: var(--accent); }
.export-opt:active { background: var(--accent-soft); }
.export-opt-icon {
  width: 40px; height: 40px; flex-shrink: 0; display: grid; place-items: center;
  border-radius: var(--radius-md); background: var(--accent-soft); color: var(--accent);
}
.export-opt-icon svg { width: 20px; height: 20px; }
:root[data-theme="dark"] .export-opt-icon { color: var(--accent-strong); }
.export-opt-text { display: grid; gap: 2px; }
.export-opt-text strong { font-size: var(--text-base); }
.export-opt-text small { font-size: var(--text-xs); color: var(--text-muted); }

/* ------------------------------------------------------------ chart toggle
   The inline button that cycles the dashboard chart mode (spending →
   distance → efficiency). Styled as a subtle dashed-underline link so it
   reads as "click to change" without competing with the chart title. */
.chart-toggle {
  display: inline-flex; align-items: center; gap: var(--space-1);
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; letter-spacing: inherit; text-transform: inherit; color: var(--accent);
  border-bottom: 1px dashed currentColor;
}
.chart-toggle:hover { color: var(--accent-strong); }
.chart-toggle svg { width: 12px; height: 12px; }
:root[data-theme="dark"] .chart-toggle { color: var(--accent-strong); }
