/* HaulBase — locked design tokens (Palette A "Refined Industrial")
 *
 * Two themes shipped: data-theme="dark" (default, primary product
 * surface) and data-theme="light" (printable manifests, sun-glare
 * fallback). All other CSS in the system reads from these vars —
 * never hard-code hex inside components.
 *
 * Type stack: Inter Tight (display/UI), Inter (body fallback),
 * JetBrains Mono (IDs/coords/codes). Loaded via Google Fonts in the
 * page <head>. Always include the system fallbacks listed below — if
 * the network is down (driver in cab, no signal), system-ui still
 * looks correct.
 */

/* ---------- Webfont fallback declarations (kept here for reference) -- */
/*
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;600;700;800
        &family=Inter:wght@400;500;600;700
        &family=JetBrains+Mono:wght@400;500;600
        &display=swap" rel="stylesheet">
*/

:root {
  /* Type stacks — first family is the design intent, rest are fallbacks. */
  --font-display: "Inter Tight", "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-body:    "Inter", "Inter Tight", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono:    "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;

  /* Type scale (px) */
  --fs-display: 30px;
  --fs-h1:      24px;
  --fs-h2:      20px;
  --fs-h3:      16px;
  --fs-body:    14px;
  --fs-sm:      13px;
  --fs-xs:      12px;
  --fs-micro:   10px;

  /* Weights */
  --fw-regular: 400;
  --fw-medium:  500;
  --fw-semi:    600;
  --fw-bold:    700;
  --fw-black:   800;

  /* Tracking */
  --tracking-label: 0.12em;  /* uppercase micro-labels — used everywhere */
  --tracking-tight: -0.015em;

  /* Line heights */
  --lh-tight: 1.1;
  --lh-snug:  1.3;
  --lh-body:  1.5;

  /* Shape */
  --r-sm:   4px;
  --r-md:   6px;
  --r-lg:   8px;
  --r-xl:   12px;
  --r-pill: 9999px;

  /* Spacing — 4px base */
  --sp-1:  4px;
  --sp-2:  8px;
  --sp-3:  12px;
  --sp-4:  16px;
  --sp-5:  20px;
  --sp-6:  24px;
  --sp-8:  32px;
  --sp-10: 40px;
  --sp-12: 48px;

  /* Shadows (theme-agnostic — neutral alpha so they read on both modes) */
  --shadow-card: 0 1px 2px rgba(0,0,0,0.20);
  --shadow-pop:  0 8px 24px rgba(0,0,0,0.30);
  --shadow-pin:  0 2px 6px rgba(0,0,0,0.35);
  --shadow-glow-amber: 0 0 12px rgba(251,191,36,0.18);

  /* Brand accent (amber) — IDENTICAL across themes by design. */
  --accent-50:  #fffbeb;
  --accent-100: #fef3c7;
  --accent-200: #fde68a;
  --accent-300: #fcd34d;
  --accent-400: #fbbf24;
  --accent-500: #f59e0b;
  --accent-600: #d97706;
  --accent-700: #b45309;
  --accent-800: #92400e;
  --accent-900: #78350f;

  /* Brand semantic roles */
  --brand:        var(--accent-400); /* logo, headings, active link text */
  --brand-cta:    var(--accent-500); /* filled primary buttons */
  --brand-cta-fg: #0f172a;           /* always slate on amber */
  --brand-cta-hover: var(--accent-400);
  /* Text/icon color for content sitting ON an amber accent fill. Was
     referenced as var(--accent-on) by day_optimizer.css (primary
     button, active mode pill) but never defined → fell back to light
     inherited text (white-on-amber, ~1.9:1, an AA fail). Alias it to
     the dark "slate on amber" role so all amber fills get >=8:1. */
  --accent-on:    var(--brand-cta-fg);
}

/* ============================================================
 * DARK THEME — primary product surface (refined industrial)
 * ============================================================ */
[data-theme="dark"], :root {
  color-scheme: dark;

  --bg-page:     #0a0f1c;
  --bg-card:     #111827;
  --bg-card-2:   #0d1320;  /* deeper well — map canvas, full-bleed nav */
  --bg-input:    #1c2434;
  --bg-hover:    #283045;

  --border-1:    #27324a;  /* default card border */
  --border-2:    #3a4663;  /* input border / emphasized */
  --border-3:    #4b5876;  /* hairline divider on hover */

  --fg-1: #f1f5f9; /* primary headings + ui labels */
  --fg-2: #cbd5e1; /* body */
  --fg-3: #94a3b8; /* secondary / micro labels */
  --fg-4: #828fa6; /* muted — lightened from #64748b to clear WCAG AA 4.5:1
                      on page/card/input bgs (was ~3.3-4.0:1, an a11y fail) */
  --fg-5: #475569; /* disabled (exempt from contrast minimums) */

  /* Status (en route, on site, completed, issue, invoiced)
     — fg = light-300, bg = solid-900 @ 40% alpha, bd = solid-800 */
  --status-scheduled-fg: #cbd5e1;
  --status-scheduled-bg: #1c2434;
  --status-scheduled-bd: #3a4663;

  --status-enroute-fg:   #93c5fd;
  --status-enroute-bg:   rgba(30, 58, 138, 0.40);
  --status-enroute-bd:   #1e40af;

  --status-onsite-fg:    #fcd34d;
  --status-onsite-bg:    rgba(120, 53, 15, 0.40);
  --status-onsite-bd:    #92400e;

  --status-complete-fg:  #6ee7b7;
  --status-complete-bg:  rgba(6, 78, 59, 0.40);
  --status-complete-bd:  #065f46;

  --status-issue-fg:     #fca5a5;
  --status-issue-bg:     rgba(127, 29, 29, 0.40);
  --status-issue-bd:     #991b1b;

  --status-invoiced-fg:  #c4b5fd;
  --status-invoiced-bg:  rgba(76, 29, 149, 0.40);
  --status-invoiced-bd:  #5b21b6;

  /* GPS heartbeat (driver manifest) */
  --signal-active:    #22c55e;
  --signal-stale:     #64748b;
  --signal-error:     #ef4444;
  --signal-searching: #facc15;
  --signal-bypass:    #3b82f6;

  /* Map pins */
  --pin-driver:      var(--accent-500);
  --pin-driver-bd:   #0f172a;
  --pin-customer:    #10b981;
  --pin-customer-bd: #064e3b;

  /* Active sidebar link surface */
  --link-active-bg:  rgba(245, 158, 11, 0.10);
  --link-active-bd:  rgba(245, 158, 11, 0.30);
  --link-active-fg:  var(--accent-300);
}

/* ============================================================
 * LIGHT THEME — printable manifests + sun-glare fallback
 * Higher contrast text, paper-white surfaces, deeper amber for CTAs.
 * ============================================================ */
[data-theme="light"] {
  color-scheme: light;

  --bg-page:     #f8fafc;
  --bg-card:     #ffffff;
  --bg-card-2:   #f1f5f9;
  --bg-input:    #f1f5f9;
  --bg-hover:    #e2e8f0;

  --border-1:    #e2e8f0;
  --border-2:    #cbd5e1;
  --border-3:    #94a3b8;

  --fg-1: #0f172a;
  --fg-2: #334155;
  --fg-3: #64748b;
  --fg-4: #94a3b8;
  --fg-5: #cbd5e1;

  /* Status — fg shifted to mid-700 for contrast on light fills */
  --status-scheduled-fg: #475569;
  --status-scheduled-bg: #f1f5f9;
  --status-scheduled-bd: #cbd5e1;

  --status-enroute-fg:   #1e40af;
  --status-enroute-bg:   #dbeafe;
  --status-enroute-bd:   #93c5fd;

  --status-onsite-fg:    #b45309;
  --status-onsite-bg:    #fef3c7;
  --status-onsite-bd:    #fcd34d;

  --status-complete-fg:  #065f46;
  --status-complete-bg:  #d1fae5;
  --status-complete-bd:  #6ee7b7;

  --status-issue-fg:     #991b1b;
  --status-issue-bg:     #fee2e2;
  --status-issue-bd:     #fca5a5;

  --status-invoiced-fg:  #5b21b6;
  --status-invoiced-bg:  #ede9fe;
  --status-invoiced-bd:  #c4b5fd;

  /* GPS heartbeat — same hues, slightly deeper for paper contrast */
  --signal-active:    #16a34a;
  --signal-stale:     #94a3b8;
  --signal-error:     #dc2626;
  --signal-searching: #eab308;
  --signal-bypass:    #2563eb;

  --pin-driver:      var(--accent-600);
  --pin-driver-bd:   #ffffff;
  --pin-customer:    #059669;
  --pin-customer-bd: #ffffff;

  --link-active-bg:  #fef3c7;
  --link-active-bd:  #fcd34d;
  --link-active-fg:  #92400e;

  /* Brand reads slightly deeper on white so it doesn't bloom */
  --brand:        var(--accent-600);
}

/* ============================================================
 * LIGHT-MODE COMPATIBILITY LAYER (Phase 9b)
 *
 * Most production code still uses hardcoded Tailwind dark classes
 * (bg-slate-950, text-slate-100, border-slate-800, status colors,
 * etc.) instead of token vars. Without these overrides, those
 * classes stay dark when [data-theme="light"] is active, leaving
 * pages with a half-flipped look.
 *
 * Strategy: under [data-theme="light"] only, remap the most-common
 * hardcoded slate / amber / status Tailwind classes to their token
 * equivalents. Selectors here have specificity (0,2,0) vs Tailwind's
 * (0,1,0), so they win cleanly under the data-theme attribute
 * without needing !important. Dark mode is untouched — the override
 * only fires when the attribute is `light`.
 *
 * This is a retrofit. Long-term these classes should be converted
 * to tokens at the call sites, but this compatibility layer makes
 * light mode usable across the whole app immediately.
 * ============================================================ */

/* Surfaces */
[data-theme="light"] .bg-slate-950 { background-color: var(--bg-page); }
[data-theme="light"] .bg-slate-900 { background-color: var(--bg-card); }
[data-theme="light"] .bg-slate-800 { background-color: var(--bg-input); }
[data-theme="light"] .bg-slate-700 { background-color: var(--bg-hover); }
[data-theme="light"] .bg-slate-900\/70 { background-color: color-mix(in srgb, var(--bg-card) 90%, transparent); }
[data-theme="light"] .bg-slate-900\/60 { background-color: color-mix(in srgb, var(--bg-card) 80%, transparent); }
[data-theme="light"] .bg-slate-900\/50 { background-color: color-mix(in srgb, var(--bg-card) 70%, transparent); }
[data-theme="light"] .bg-slate-900\/40 { background-color: color-mix(in srgb, var(--bg-card-2) 100%, transparent); }
[data-theme="light"] .bg-slate-950\/60 { background-color: color-mix(in srgb, var(--bg-card-2) 80%, transparent); }
[data-theme="light"] .bg-slate-950\/40 { background-color: color-mix(in srgb, var(--bg-card-2) 60%, transparent); }
[data-theme="light"] .bg-slate-800\/70 { background-color: color-mix(in srgb, var(--bg-input) 90%, transparent); }

/* Hovered surfaces */
[data-theme="light"] .hover\:bg-slate-800:hover { background-color: var(--bg-input); }
[data-theme="light"] .hover\:bg-slate-700:hover { background-color: var(--bg-hover); }
[data-theme="light"] .hover\:bg-slate-900:hover { background-color: var(--bg-card); }

/* Foreground text */
[data-theme="light"] .text-slate-100 { color: var(--fg-1); }
[data-theme="light"] .text-slate-200 { color: var(--fg-1); }
[data-theme="light"] .text-slate-300 { color: var(--fg-2); }
[data-theme="light"] .text-slate-400 { color: var(--fg-3); }
[data-theme="light"] .text-slate-500 { color: var(--fg-4); }
[data-theme="light"] .text-slate-600 { color: var(--fg-5); }
[data-theme="light"] .hover\:text-slate-100:hover { color: var(--fg-1); }
[data-theme="light"] .hover\:text-slate-200:hover { color: var(--fg-1); }
[data-theme="light"] .hover\:text-amber-400:hover { color: var(--brand); }
[data-theme="light"] .hover\:text-amber-300:hover { color: var(--accent-700); }
[data-theme="light"] .placeholder-slate-500::placeholder { color: var(--fg-4); }

/* Borders */
[data-theme="light"] .border-slate-800 { border-color: var(--border-1); }
[data-theme="light"] .border-slate-700 { border-color: var(--border-2); }
[data-theme="light"] .border-slate-600 { border-color: var(--border-3); }
[data-theme="light"] .hover\:border-slate-700:hover { border-color: var(--border-2); }
[data-theme="light"] .hover\:border-slate-600:hover { border-color: var(--border-3); }
[data-theme="light"] .focus\:border-amber-400:focus { border-color: var(--accent-400); }

/* Brand amber — light theme uses accent-600+ for contrast on white */
[data-theme="light"] .text-amber-400 { color: var(--accent-600); }
[data-theme="light"] .text-amber-300 { color: var(--accent-700); }
[data-theme="light"] .text-amber-200 { color: var(--accent-700); }
[data-theme="light"] .text-amber-100 { color: var(--accent-800); }
[data-theme="light"] .bg-amber-500 { background-color: var(--accent-500); }
[data-theme="light"] .bg-amber-400 { background-color: var(--accent-400); }
[data-theme="light"] .hover\:bg-amber-500:hover { background-color: var(--accent-500); }
[data-theme="light"] .hover\:bg-amber-400:hover { background-color: var(--accent-400); }
[data-theme="light"] .border-amber-500 { border-color: var(--accent-500); }
[data-theme="light"] .border-amber-400 { border-color: var(--accent-400); }
[data-theme="light"] .border-amber-700 { border-color: var(--accent-300); }
[data-theme="light"] .border-amber-800 { border-color: var(--accent-300); }

/* Status — amber/onsite tints */
[data-theme="light"] .bg-amber-900\/40 { background-color: var(--status-onsite-bg); }
[data-theme="light"] .bg-amber-900\/30 { background-color: var(--status-onsite-bg); }
[data-theme="light"] .bg-amber-950\/40 { background-color: var(--status-onsite-bg); }
[data-theme="light"] .border-amber-900 { border-color: var(--status-onsite-bd); }
[data-theme="light"] .border-amber-900\/60 { border-color: var(--status-onsite-bd); }

/* Status — emerald/complete tints */
[data-theme="light"] .bg-emerald-900\/40 { background-color: var(--status-complete-bg); }
[data-theme="light"] .bg-emerald-900\/30 { background-color: var(--status-complete-bg); }
[data-theme="light"] .bg-emerald-950\/40 { background-color: var(--status-complete-bg); }
[data-theme="light"] .text-emerald-100 { color: var(--status-complete-fg); }
[data-theme="light"] .text-emerald-200 { color: var(--status-complete-fg); }
[data-theme="light"] .text-emerald-300 { color: var(--status-complete-fg); }
[data-theme="light"] .text-emerald-400 { color: var(--status-complete-fg); }
[data-theme="light"] .border-emerald-700 { border-color: var(--status-complete-bd); }
[data-theme="light"] .border-emerald-800 { border-color: var(--status-complete-bd); }
[data-theme="light"] .border-emerald-900 { border-color: var(--status-complete-bd); }
[data-theme="light"] .border-emerald-900\/60 { border-color: var(--status-complete-bd); }
[data-theme="light"] .bg-emerald-700 { background-color: #047857; }
[data-theme="light"] .hover\:bg-emerald-600:hover { background-color: #059669; }

/* Status — red/issue tints */
[data-theme="light"] .bg-red-900\/40 { background-color: var(--status-issue-bg); }
[data-theme="light"] .bg-red-900\/30 { background-color: var(--status-issue-bg); }
[data-theme="light"] .bg-red-950\/40 { background-color: var(--status-issue-bg); }
[data-theme="light"] .text-red-100 { color: var(--status-issue-fg); }
[data-theme="light"] .text-red-200 { color: var(--status-issue-fg); }
[data-theme="light"] .text-red-300 { color: var(--status-issue-fg); }
[data-theme="light"] .text-red-400 { color: var(--status-issue-fg); }
[data-theme="light"] .border-red-700 { border-color: var(--status-issue-bd); }
[data-theme="light"] .border-red-800 { border-color: var(--status-issue-bd); }
[data-theme="light"] .border-red-900 { border-color: var(--status-issue-bd); }
[data-theme="light"] .bg-red-900 { background-color: #b91c1c; }
[data-theme="light"] .hover\:bg-red-800:hover { background-color: #991b1b; }
[data-theme="light"] .text-rose-300 { color: var(--status-issue-fg); }
[data-theme="light"] .text-rose-400 { color: var(--status-issue-fg); }

/* Status — blue/enroute tints */
[data-theme="light"] .bg-blue-900\/40 { background-color: var(--status-enroute-bg); }
[data-theme="light"] .bg-blue-900\/30 { background-color: var(--status-enroute-bg); }
[data-theme="light"] .text-blue-100 { color: var(--status-enroute-fg); }
[data-theme="light"] .text-blue-300 { color: var(--status-enroute-fg); }
[data-theme="light"] .border-blue-800 { border-color: var(--status-enroute-bd); }
[data-theme="light"] .bg-blue-600 { background-color: #2563eb; }
[data-theme="light"] .hover\:bg-blue-500:hover { background-color: #3b82f6; }

/* Status — violet/invoiced tints */
[data-theme="light"] .bg-violet-900\/40 { background-color: var(--status-invoiced-bg); }
[data-theme="light"] .bg-violet-900\/30 { background-color: var(--status-invoiced-bg); }
[data-theme="light"] .text-violet-300 { color: var(--status-invoiced-fg); }
[data-theme="light"] .border-violet-800 { border-color: var(--status-invoiced-bd); }

/* ============================================================
 * Element defaults — apply these by adding class names; nothing
 * here is auto-applied beyond <body>.
 * ============================================================ */

body {
  background: var(--bg-page);
  color: var(--fg-1);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: "ss01", "cv11"; /* Inter stylistic sets — friendlier 'a', open '4' */
}

.font-display { font-family: var(--font-display); letter-spacing: var(--tracking-tight); }
.font-body    { font-family: var(--font-body); }
.font-mono    { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
/* Tabular figures without changing the font — for stat counters, dates,
   and any column of numbers that shouldn't jitter as values change. */
.tnum { font-variant-numeric: tabular-nums; }

.h-display { font: var(--fw-black) var(--fs-display)/var(--lh-tight) var(--font-display); letter-spacing: var(--tracking-tight); color: var(--fg-1); }
.h1        { font: var(--fw-bold)  var(--fs-h1)/var(--lh-tight) var(--font-display); letter-spacing: -0.01em; color: var(--fg-1); }
.h2        { font: var(--fw-semi)  var(--fs-h2)/var(--lh-snug) var(--font-display); color: var(--fg-1); }
.h3        { font: var(--fw-semi)  var(--fs-h3)/var(--lh-snug) var(--font-display); color: var(--fg-1); }

.label {
  font: var(--fw-semi) var(--fs-micro)/1.2 var(--font-body);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--fg-3);
}

.muted   { color: var(--fg-3); }
.muted-2 { color: var(--fg-4); }

/* Skip-to-content link (a11y, WCAG 2.4.1). Off-screen until it
   receives keyboard focus, then drops into the top-left corner. */
.skip-link {
  position: fixed;
  top: 8px;
  left: 8px;
  z-index: 9999;
  padding: 8px 14px;
  border-radius: var(--r-md);
  background: var(--accent-500);
  color: #0a0f1c;
  font: var(--fw-semi) 13px/1 var(--font-body);
  text-decoration: none;
  transform: translateY(-150%);
  opacity: 0;
  transition: transform 120ms ease, opacity 120ms ease;
}
.skip-link:focus,
.skip-link:focus-visible {
  transform: translateY(0);
  opacity: 1;
  outline: 2px solid var(--fg-1);
  outline-offset: 2px;
}
/* Sticky top strip is ~56px — keep the focused <main> clear of it
   when the skip link jumps focus (WCAG 2.4.11 focus-not-obscured). */
#main-content { scroll-margin-top: 72px; }
#main-content:focus { outline: none; }

/* Leaflet layer-switcher controls ship as ~13px radio/checkbox inputs
   stacked tightly — below the WCAG 2.5.8 (AA) 24px target minimum. Enlarge
   the inputs and give each label row a >=24px clear zone so the targets
   pass on both size and spacing. Scoped to .leaflet-* so non-map pages are
   untouched. */
.leaflet-control-layers-list label {
  display: flex;
  align-items: center;
  min-height: 24px;
  margin: 2px 0;
}
.leaflet-control-layers-selector {
  width: 18px;
  height: 18px;
  margin: 0 6px 0 0;
}

/* Pill — base */
.pill {
  display: inline-flex; align-items: center; gap: 6px;
  font: var(--fw-semi) var(--fs-micro)/1 var(--font-body);
  text-transform: uppercase; letter-spacing: var(--tracking-label);
  padding: 4px 8px; border-radius: var(--r-md);
  border: 1px solid transparent;
}
.pill-scheduled { background: var(--status-scheduled-bg); color: var(--status-scheduled-fg); border-color: var(--status-scheduled-bd); }
.pill-enroute   { background: var(--status-enroute-bg);   color: var(--status-enroute-fg);   border-color: var(--status-enroute-bd); }
.pill-onsite    { background: var(--status-onsite-bg);    color: var(--status-onsite-fg);    border-color: var(--status-onsite-bd); }
.pill-complete  { background: var(--status-complete-bg);  color: var(--status-complete-fg);  border-color: var(--status-complete-bd); }
.pill-issue     { background: var(--status-issue-bg);     color: var(--status-issue-fg);     border-color: var(--status-issue-bd); }
.pill-invoiced  { background: var(--status-invoiced-bg);  color: var(--status-invoiced-fg);  border-color: var(--status-invoiced-bd); }

/* Card */
.card {
  background: var(--bg-card);
  border: 1px solid var(--border-1);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
}
.card-tight { padding: var(--sp-3); }

/* Input */
.input {
  background: var(--bg-input);
  border: 1px solid var(--border-2);
  border-radius: var(--r-md);
  padding: 8px 12px;
  color: var(--fg-1);
  font: var(--fw-medium) var(--fs-body)/1.4 var(--font-body);
  outline: none;
  transition: border-color 120ms ease;
  width: 100%;
}
.input:focus { border-color: var(--accent-400); }
.input::placeholder { color: var(--fg-4); }

/* Buttons */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  font: var(--fw-semi) var(--fs-body)/1 var(--font-body);
  padding: 9px 14px;
  border-radius: var(--r-md);
  border: 1px solid transparent;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease;
}
.btn-primary   { background: var(--brand-cta); color: var(--brand-cta-fg); }
.btn-primary:hover { background: var(--brand-cta-hover); }
.btn-secondary { background: var(--bg-input); color: var(--fg-2); border-color: var(--border-2); }
.btn-secondary:hover { background: var(--bg-hover); }
.btn-ghost     { background: transparent; color: var(--fg-3); }
.btn-ghost:hover { color: var(--fg-1); background: var(--bg-input); }

/* ----------------------------------------------------------------
 * Theme toggle (Phase 9). 2-segment pill in the top strip; sun for
 * light mode, moon for dark. State lives in haulbase/theme.py;
 * tokens.css's `:root` / `[data-theme="light"]` rules carry the
 * actual color swap.
 * ---------------------------------------------------------------- */
.tt-toggle {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  background: var(--bg-input);
  border: 1px solid var(--border-2);
  border-radius: var(--r-md);
}
.tt-btn {
  background: transparent;
  border: 0;
  color: var(--fg-3);
  width: 28px;
  height: 28px;
  border-radius: var(--r-sm);
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.tt-btn:hover { color: var(--fg-1); }
.tt-btn.active {
  background: var(--bg-card);
  color: var(--fg-1);
}

/* GPS heartbeat pulse — the only repeating animation in the system */
@keyframes hb-pulse {
  0%, 100% { opacity: 1;    transform: scale(1); }
  50%      { opacity: 0.35; transform: scale(1.35); }
}
.hb-dot {
  width: 10px; height: 10px; border-radius: var(--r-pill);
  animation: hb-pulse 1.4s ease-in-out infinite;
}

/* WCAG 2.3.3 — honor the OS "reduce motion" preference. Collapse
   transitions/animations to near-instant and stop the GPS heartbeat
   loop so motion-sensitive users aren't subjected to movement. */
@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;
  }
}
