/* ==========================================================================
   WattLoft — "Instrument"

   Design notes, so future edits keep the character:
   · Type is Space Grotesk throughout — a technical grotesque. Large sizes get
     negative tracking; numerals are always tabular so columns align.
   · Primary actions are INK, not accent-coloured. Blue buttons everywhere is
     the single biggest tell of a generic dashboard.
   · Accent colour is reserved for DATA (charts, live values, selection). It is
     never decoration.
   · Structure comes from hairlines and spacing rhythm, not from boxes and
     shadows stacked on boxes.
   · Status colours are from a CVD-validated palette and never carry meaning
     alone — always paired with a label.
   ========================================================================== */

@import url('/vendor/fonts/space-grotesk.css');
@import url('/vendor/fonts/anton.css');
@import url('/vendor/fonts/space.css');

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

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

:root {
  color-scheme: light;

  --paper: #f6f6f3;
  --surface: #ffffff;
  --surface-2: #efeee9;
  --surface-3: #e4e3dd;
  --veil: rgba(246, 246, 243, 0.82);

  --ink: #0a0a09;
  --ink-2: #3d3c38;
  --muted: #66645e;
  --faint: #716f68;

  --hairline: rgba(10, 10, 9, 0.10);
  --hairline-2: rgba(10, 10, 9, 0.055);
  --grid: #e2e1da;

  --accent: #2a78d6;
  --accent-2: #1c5cab;
  --accent-wash: rgba(42, 120, 214, 0.09);
  --switch-on: var(--ink);
  --switch-knob-on: #ffffff;

  --good: #0ca30c;   --good-ink: #006300;
  --warning: #fab219; --warning-ink: #855c00;
  --serious: #ec835a; --serious-ink: #9e4123;
  --critical: #d03b3b; --critical-ink: #ac2020;

  /* Ink-on-paper primary action. */
  --btn-fill: #0a0a09;
  --btn-text: #ffffff;

  --lift: 0 1px 2px rgba(10, 10, 9, .045);
  --lift-2: 0 1px 3px rgba(10, 10, 9, .05), 0 10px 30px rgba(10, 10, 9, .07);
  --lift-3: 0 20px 70px rgba(10, 10, 9, .18);

  --r1: 7px; --r2: 11px; --r3: 15px; --r4: 22px;
  --rail: 228px;

  --sans: 'Space Grotesk', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, monospace;
}

@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;
    --paper: #0a0a0a;
    --surface: #151514;
    --surface-2: #1e1e1c;
    --surface-3: #292826;
    --veil: rgba(10, 10, 10, 0.82);
    --ink: #fafaf8;
    --ink-2: #b9b7ae;
    --muted: #aaa79f;
    --faint: #8d8b84;
    --hairline: rgba(250, 250, 248, 0.11);
    --hairline-2: rgba(250, 250, 248, 0.055);
    --grid: #292826;
    --accent: #4a91e8;
    --accent-2: #6ba5ed;
    --accent-wash: rgba(74, 145, 232, 0.13);
    --switch-on: var(--ink);
    --switch-knob-on: #0a0a0a;
    --good-ink: #3cc23c;
    --warning-ink: #f0ae21;
    --serious-ink: #f09468;
    --critical-ink: #f37070;
    --btn-fill: #fafaf8;
    --btn-text: #0a0a09;
    --lift: 0 1px 2px rgba(0, 0, 0, .5);
    --lift-2: 0 1px 3px rgba(0, 0, 0, .5), 0 12px 34px rgba(0, 0, 0, .4);
    --lift-3: 0 24px 80px rgba(0, 0, 0, .7);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --paper: #0a0a0a;
  --surface: #151514;
  --surface-2: #1e1e1c;
  --surface-3: #292826;
  --veil: rgba(10, 10, 10, 0.82);
  --ink: #fafaf8;
  --ink-2: #b9b7ae;
  --muted: #aaa79f;
  --faint: #8d8b84;
  --hairline: rgba(250, 250, 248, 0.11);
  --hairline-2: rgba(250, 250, 248, 0.055);
  --grid: #292826;
  --accent: #4a91e8;
  --accent-2: #6ba5ed;
  --accent-wash: rgba(74, 145, 232, 0.13);
    --switch-on: var(--ink);
    --switch-knob-on: #0a0a0a;
  --good-ink: #3cc23c;
  --warning-ink: #f0ae21;
  --serious-ink: #f09468;
  --critical-ink: #f37070;
  --btn-fill: #fafaf8;
  --btn-text: #0a0a09;
  --lift: 0 1px 2px rgba(0, 0, 0, .5);
  --lift-2: 0 1px 3px rgba(0, 0, 0, .5), 0 12px 34px rgba(0, 0, 0, .4);
  --lift-3: 0 24px 80px rgba(0, 0, 0, .7);
}

/* Art — the poster skin: bone paper, hard ink, the sticker sun's orange.
   Palette shared with the native apps' Art theme (Theme.swift). */
:root[data-theme="art"] {
  color-scheme: light;
  --paper: #f2eee3;
  --surface: #fbf8f0;
  --surface-2: #ece7d8;
  --surface-3: #e0dac8;
  --veil: rgba(242, 238, 227, 0.82);
  --ink: #141414;
  --ink-2: #403d36;
  --muted: #6e6a61;
  --faint: #746f64;
  --hairline: rgba(20, 20, 20, 0.12);
  --hairline-2: rgba(20, 20, 20, 0.06);
  --grid: #ddd7c8;
  --accent: #f2761b;
  --accent-2: #d35f0a;
  --accent-wash: rgba(242, 118, 27, 0.10);
  --switch-on: var(--ink);
  --switch-knob-on: #ffffff;
  --btn-fill: #141414;
  --btn-text: #f6f2e7;

  /* The poster's own display face and sharper print geometry. */
  --display: 'Anton', 'Arial Narrow', var(--sans);
  --r1: 4px; --r2: 7px; --r3: 10px; --r4: 14px;
}

/* ---- The poster voice — not just a palette. -----------------------------
   Anton billboard numerals and headlines, mono petite-caps captions,
   condensed-caps chrome, hairline structure instead of shadows. ---- */

/* Anton is a far narrower face than the house sans, so the ring's number can
   stand a fifth taller and still keep the same three-fifths of the interior. */
:root[data-theme="art"] .ring .pct { font-size: calc(var(--ring) * .22); }

/* Billboard numerals + headlines. Anton is single-weight black, so weight
   drops to 400 and the tight house tracking opens back up. */
:root[data-theme="art"] .hero-info h1,
:root[data-theme="art"] .stat .v,
:root[data-theme="art"] .ring .pct,
:root[data-theme="art"] .gauge .g-num,
:root[data-theme="art"] .ds-speed,
:root[data-theme="art"] .ds-stat .v,
:root[data-theme="art"] .rt-eta b,
:root[data-theme="art"] .replay-readout .v,
:root[data-theme="art"] .empty h2,
:root[data-theme="art"] .login-card h1 {
  font-family: var(--display); font-weight: 400; letter-spacing: .01em;
}
:root[data-theme="art"] .hero-info h1 { text-transform: uppercase; letter-spacing: .015em; }
:root[data-theme="art"] .stat .v.small { letter-spacing: .02em; }
:root[data-theme="art"] .login-card h1 { text-transform: uppercase; }

/* Mono petite-caps labels — the poster's caption voice. */
:root[data-theme="art"] .card h3,
:root[data-theme="art"] .stat .l,
:root[data-theme="art"] label.f,
:root[data-theme="art"] .tbl th,
:root[data-theme="art"] .replay-readout .l {
  font-family: var(--mono); letter-spacing: .12em;
}

/* Poster chrome: condensed-caps controls, nav and brand. */
:root[data-theme="art"] .brand { font-family: var(--display); font-weight: 400; font-size: 16px; text-transform: uppercase; letter-spacing: .035em; }
:root[data-theme="art"] .nav-item { font: 600 11.5px/1 var(--sans); text-transform: uppercase; letter-spacing: .07em; }
:root[data-theme="art"] .btn { font: 700 11px/1 var(--sans); text-transform: uppercase; letter-spacing: .08em; box-shadow: none; }
:root[data-theme="art"] .btn.sm { font-size: 10px; }
:root[data-theme="art"] .seg button,
:root[data-theme="art"] .theme-switch button { font: 700 10px/1 var(--sans); text-transform: uppercase; letter-spacing: .07em; }
:root[data-theme="art"] .chip { font: 600 11px/1 var(--sans); text-transform: uppercase; letter-spacing: .06em; }
:root[data-theme="art"] .badge { font: 600 10px/1 var(--mono); text-transform: uppercase; letter-spacing: .08em; }

/* Print flatness: hairline borders carry the structure, not shadows; the
   hero sits on a hard ink rule like a masthead. */
:root[data-theme="art"] .card { box-shadow: none; border-color: var(--hairline); }
:root[data-theme="art"] .hero { border-bottom: 2px solid var(--ink); }


html, body { height: 100%; }
body {
  background: var(--paper);
  color: var(--ink-2);
  font: 400 14px/1.5 var(--sans);
  font-feature-settings: 'ss01', 'ss02';
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
/* Solid, not a wash: selections must stay readable on dark surfaces (toasts,
   the login glow) in either theme. */
::selection { background: var(--accent); color: #ffffff; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 3px; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
svg { display: block; }

.app { display: flex; min-height: 100vh; opacity: 1; transition: opacity .25s ease; }

/* Until the auth check answers, the app stays invisible — either the login
   overlay or the revealed dashboard is the first thing anyone sees. A small
   spinner covers slow connections; the opaque login overlay (z-index 100)
   paints over it once shown. */
body.booting .app { visibility: hidden; opacity: 0; }
body.booting::after {
  content: ''; position: fixed; z-index: 50;
  top: calc(50% - 11px); left: calc(50% - 11px); width: 22px; height: 22px;
  border: 2px solid var(--hairline); border-top-color: var(--accent);
  border-radius: 50%; animation: spin .7s linear infinite;
}

/* ============================== Rail ============================== */

.sidebar {
  position: fixed; inset: var(--sash-h, 0) auto 0 0; width: var(--rail); z-index: 30;
  display: flex; flex-direction: column;
  padding: 24px 16px 18px;
  background: var(--paper);
  border-right: 1px solid var(--hairline-2);
}
.brand {
  display: flex; align-items: center; gap: 10px;
  padding: 0 8px 26px;
  font-size: 15px; font-weight: 700; letter-spacing: -.02em; color: var(--ink);
}
.brand .mark {
  width: 24px; height: 24px; flex: none;
  display: grid; place-items: center;
  border-radius: 7px; background: var(--accent); color: #fff;
}
/* The monogram fills its own viewBox, so the badge just needs breathing room. */
.brand .mark svg { width: 16px; height: 16px; }

.nav { display: flex; flex-direction: column; gap: 1px; flex: 1; }
.nav-item {
  position: relative;
  display: flex; align-items: center; gap: 11px;
  padding: 9px 10px; border: 0; border-radius: var(--r2);
  background: transparent; color: var(--muted);
  font: 500 13.5px/1 var(--sans); letter-spacing: -.005em;
  text-align: left; cursor: pointer;
  transition: color .13s ease, background .13s ease;
}
.nav-item svg { width: 15px; height: 15px; flex: none; opacity: .8; }
.nav-item:hover { color: var(--ink-2); background: var(--hairline-2); }
.nav-item.active { color: var(--ink); background: var(--surface-2); font-weight: 600; }
.nav-item.active svg { opacity: 1; }
.nav-badge {
  margin-left: auto; min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 9px; background: var(--critical); color: #fff;
  font-size: 10.5px; font-weight: 700; line-height: 18px; text-align: center;
  font-variant-numeric: tabular-nums;
}

.sidebar-foot { display: flex; flex-direction: column; gap: 7px; padding-top: 14px; }

/* Admin-impersonation banner: unmissable, but not in the way. */
#impBanner {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  background: var(--surface-2); color: var(--ink);
  border-bottom: 1px solid var(--hairline); padding: 10px 32px;
  font-size: 12.5px;
}
#impBanner span { min-width: 0; overflow-wrap: anywhere; }
#impBanner .btn { flex: none; }
.tesla-setup { margin: 20px 32px 0; padding: 18px 20px; border: 1px solid var(--hairline);
  border-left: 3px solid var(--accent); border-radius: var(--r3); background: var(--surface);
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap; color: var(--ink); }
.tesla-setup[hidden] { display: none; }
.tesla-setup-copy { flex: 1; min-width: min(240px, 100%); }
.tesla-setup h2 { margin: 0 0 5px; font-size: 16px; }
.tesla-setup p { margin: 0; font-size: 13px; line-height: 1.5; color: var(--ink-2); }
.tesla-setup .setup-step { color: var(--accent); font-size: 10px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; margin-bottom: 6px; }
.tesla-setup-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.tesla-logo { display: inline-block; width: 24px; height: 24px; flex: none; background: currentColor;
  -webkit-mask: url('/img/tesla-logo.svg') center / contain no-repeat;
  mask: url('/img/tesla-logo.svg') center / contain no-repeat; }
.tesla-setup > .tesla-logo { width: 34px; height: 34px; color: var(--accent); }
#teslaCard h3 > .tesla-logo { margin-right: 9px; color: var(--accent); }
.tesla-advanced { width: 100%; }
.tesla-advanced > summary { cursor: pointer; color: var(--muted); font-size: 12px; padding: 8px 0; }
@media (max-width: 700px) {
  #impBanner { padding: 10px 16px; gap: 8px; }
  .tesla-setup { margin: 14px 16px 0; padding: 15px; gap: 12px; }
}

.bug-link {
  padding: 2px 8px; font-size: 11px; color: var(--faint); text-decoration: none;
}
.bug-link:hover { color: var(--accent); text-decoration: underline; }

/* Inline "when new" editor inside the battery-health cards. */
.adjust-form {
  width: 100%; display: flex; flex-direction: column; gap: 4px;
  background: var(--surface-2); border-radius: var(--r2); padding: 12px;
}

/* Signed-in user + sign out, pinned under the theme switch. */
.sidebar-user {
  display: flex; align-items: center; gap: 8px;
  margin-top: 4px; padding: 10px 8px 0;
  border-top: 1px solid var(--hairline-2);
}
.su-email {
  flex: 1; min-width: 0;
  font-size: 11.5px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.su-logout {
  flex: none; display: grid; place-items: center;
  width: 26px; height: 26px; border: none; border-radius: 7px;
  background: none; color: var(--faint); cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.su-logout:hover { background: var(--surface-2); color: var(--critical-ink); }
.su-logout svg { width: 15px; height: 15px; }
/* The hidden attribute must always win — any rule that sets display on an
   element quietly defeats it otherwise, which is how "Tunnel" haunted the
   sidebar of people who were never meant to see it. */
[hidden] { display: none !important; }

.pill {
  display: flex; align-items: center; gap: 8px;
  padding: 0 8px; height: 30px;
  font-size: 12px; color: var(--muted); letter-spacing: -.005em;
}
.pill .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--faint); flex: none; }
.pill.ok .dot { background: var(--good); }
.pill.bad .dot { background: var(--critical); }
.pill .txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pill .sub { margin-left: auto; color: var(--faint); font-variant-numeric: tabular-nums; }

.theme-switch {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px;
  padding: 2px; border-radius: var(--r2); background: var(--surface-2);
}
.theme-switch button {
  border: 0; background: transparent; color: var(--ink-2);
  font: 600 11px/1 var(--sans); letter-spacing: .01em;
  padding: 7px 4px; border-radius: 9px; cursor: pointer;
  transition: color .13s, background .13s;
}
.theme-switch button:hover { color: var(--ink-2); }
.theme-switch button.on { background: var(--surface); color: var(--ink); box-shadow: var(--lift); }

/* ============================== Frame ============================== */

.main { flex: 1; margin-left: var(--rail); min-width: 0; }
.topbar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 15px 32px;
  background: var(--veil);
  backdrop-filter: saturate(180%) blur(16px);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  border-bottom: 1px solid var(--hairline-2);
}
.view { padding: 26px 32px 88px; max-width: 1580px; }
/* Settings is tables and full-width cards — on a big screen, let it breathe. */
.view[data-viewname="settings"] { max-width: none; }

.vehicle-pills { display: flex; gap: 6px; flex-wrap: wrap; }
.vpill {
  display: inline-flex; align-items: center; gap: 8px;
  height: 32px; padding: 0 14px; border-radius: 999px;
  border: 1px solid var(--hairline); background: transparent;
  color: var(--ink-2); font: 550 13px/1 var(--sans); letter-spacing: -.01em;
  cursor: pointer; transition: all .15s ease;
}
.vpill:hover { border-color: var(--faint); color: var(--ink); }
.vpill.active { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.vpill .dot { width: 6px; height: 6px; border-radius: 50%; flex: none; }
.top-actions { display: flex; gap: 7px; align-items: center; margin-left: auto; }

/* ============================== Cards ============================== */

/* Stretch, not start: with `start` each card keeps its natural height and the row
   is as tall as its tallest member, leaving ragged holes. Stretching makes a row
   read as one band, and .card-foot already pins itself to the bottom. */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(304px, 1fr)); gap: 12px; align-items: stretch; }
.card {
  background: var(--surface);
  border: 1px solid var(--hairline-2);
  border-radius: var(--r3);
  padding: 17px 19px 16px;
  display: flex; flex-direction: column; gap: 12px;
  min-width: 0;
  box-shadow: var(--lift);
}
.card.span2 { grid-column: span 2; }
@media (max-width: 800px) { .card.span2 { grid-column: span 1; } }
.card h3 {
  display: flex; align-items: center; gap: 8px;
  font: 600 10.5px/1 var(--sans);
  letter-spacing: .1em; text-transform: uppercase; color: var(--faint);
}
.card h3 .spacer { flex: 1; }
.card-foot {
  margin-top: auto; padding-top: 6px;
  font-size: 11px; color: var(--faint); letter-spacing: -.005em;
}

/* hero — the one place with real typographic scale */
.hero {
  display: flex; flex-direction: row; align-items: center; gap: 30px;
  flex-wrap: wrap; margin-bottom: 22px;
  padding: 4px 2px 24px;
  border-bottom: 1px solid var(--hairline);
}
.hero-info { display: flex; flex-direction: column; gap: 9px; min-width: 0; flex: 1; }
.hero-info h1 {
  color: var(--ink); font-size: 38px; font-weight: 700;
  letter-spacing: -.035em; line-height: 1.02;
}
.hero-sub {
  color: var(--muted); font-size: 12.5px; letter-spacing: -.005em;
  display: flex; gap: 9px; flex-wrap: wrap; align-items: center;
  font-variant-numeric: tabular-nums;
}
.hero-sub .sep { color: var(--faint); }
.hero-badges { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 2px; }
/* Customize lives inside the info column — deterministic at every width, no
   flex-wrap adventures next to the ring. */
.hero-info #btnCustomize { align-self: flex-start; margin-top: 4px; }
/* The Share button carries an icon — flex-centre it so the pair sit level. */
.hero-share { display: inline-flex; align-items: center; gap: 5px; }

/* battery ring — one knob. `--ring` is the box, and everything inside it
   (the number, its %, the caption, the bolt) is sized from it, so the figure
   stays about two thirds of the interior at every breakpoint. The old fixed
   32px was four fifths of a 128 ring's inside, and at 96 "79.9%" all but
   touched the arc. The native apps' WLBatteryRing uses the same ratios. */
.ring { position: relative; --ring: 128px; width: var(--ring); height: var(--ring); flex: none; }
.ring .ring-meter { transform: rotate(-90deg); }
.ring .track { stroke: var(--surface-3); }
.ring .bar {
  stroke: var(--good); stroke-linecap: round;
  transition: stroke-dashoffset .6s cubic-bezier(.32,.72,0,1), stroke .3s;
}
.ring .bar.low { stroke: var(--warning); }
.ring .bar.verylow { stroke: var(--critical); }
/* Energy travels at a constant speed outside the fixed battery measurement.
   Its shared phase survives a card rebuild when telemetry changes. */
.ring .charge-orbit { stroke: var(--accent); }
.ring .charge-orbit circle { animation: chargeOrbit 6s linear infinite; animation-delay: var(--charge-phase, 0s); }
.ring .charge-trail { opacity: .22; }
.ring .charge-tip { opacity: .9; transform: rotate(21deg); transform-origin: 58px 58px; }
@keyframes chargeOrbit { to { stroke-dashoffset: -360; } }
.ring .mid .bolt {
  width: calc(var(--ring) * .115); height: calc(var(--ring) * .115); color: var(--accent);
  display: block; margin: 0 auto 1px;
}
@media (prefers-reduced-motion: reduce) {
  .ring .charge-orbit circle { animation: none; }
}
.ring .bar.charging { stroke: var(--accent); }
.ring .mid { position: absolute; inset: 0; display: grid; place-content: center; text-align: center; }
.ring .pct {
  font-size: calc(var(--ring) * .2); font-weight: 700; color: var(--ink);
  letter-spacing: -.045em; line-height: 1; font-variant-numeric: tabular-nums;
}
.ring .pct sup { font-size: .55em; font-weight: 600; vertical-align: super; margin-left: 1px; letter-spacing: 0; }
.ring .cap {
  font-size: clamp(7.5px, calc(var(--ring) * .078), 10px); color: var(--faint); text-transform: uppercase;
  letter-spacing: .09em; margin-top: max(4px, calc(var(--ring) * .04)); font-weight: 600;
}
/* Phones: a roomier ring — "CHARGING" was touching the arc at 128px. */
@media (max-width: 900px) {
  .ring { --ring: 148px; }
}

/* ============================== Data display ============================== */

.stat-row { display: flex; gap: 22px; flex-wrap: wrap; align-items: flex-start; }
.stat { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.stat .v {
  font-size: 27px; font-weight: 600; color: var(--ink);
  letter-spacing: -.035em; line-height: 1; font-variant-numeric: tabular-nums;
}
.stat .v.small { font-size: 17px; letter-spacing: -.02em; }
.stat .l {
  font-size: 10.5px; color: var(--faint); text-transform: uppercase;
  letter-spacing: .09em; font-weight: 600;
}

/* No column gap — padding does the spacing instead, so the hairline runs
   unbroken across the whole row rather than dashing at the column boundary. */
.kv { display: grid; grid-template-columns: auto 1fr; gap: 0; font-size: 13px; }
.kv dt {
  color: var(--muted); padding: 7px 16px 7px 0;
  border-top: 1px solid var(--hairline-2); letter-spacing: -.005em;
}
.kv dd {
  color: var(--ink); text-align: right; padding: 7px 0 7px 16px;
  border-top: 1px solid var(--hairline-2);
  font-variant-numeric: tabular-nums; overflow-wrap: break-word; font-weight: 500;
}
.kv dt:first-of-type, .kv dd:first-of-type { border-top: 0; }

.bar { position: relative; height: 6px; border-radius: 3px; background: var(--surface-3); }
.bar .fill {
  position: absolute; inset: 0 auto 0 0; border-radius: 3px; background: var(--good);
  transition: width .6s cubic-bezier(.32,.72,0,1);
}
.bar .fill.low { background: var(--warning); }
.bar .fill.verylow { background: var(--critical); }
.bar .fill.charging { background: var(--accent); }
.bar .limit { position: absolute; top: -4px; bottom: -4px; width: 1.5px; background: var(--ink); opacity: .5; }

/* ============================== Badges ============================== */

.badge {
  display: inline-flex; align-items: center; gap: 6px;
  height: 24px; padding: 0 10px; border-radius: 6px;
  font: 550 11.5px/1 var(--sans); letter-spacing: -.005em;
  background: var(--surface-2); color: var(--ink-2);
}
.badge .dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; flex: none; }
.badge.good { color: var(--good-ink); }
.badge.warning { color: var(--warning-ink); }
.badge.serious { color: var(--serious-ink); }
.badge.critical { color: var(--critical-ink); }
.badge.info { color: var(--accent); }
/* Anti Mold: always blue, whatever the theme's accent — the one status that is a fan, not a charge. */
.badge.antimold { color: var(--accent); }
/* The Controls page's Anti Mold row: a wind glyph in an accent well, the name, a short hint, the button. */
.card.antimold-card { display: grid; grid-template-columns: 44px minmax(0, 1fr) auto auto; align-items: center; gap: 12px; padding: 14px 17px; text-align: left; }
.antimold-card .am-icon { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; border-radius: 12px; background: var(--accent-wash); color: var(--accent); flex: none; }
.antimold-card.on .am-icon { background: var(--accent); color: var(--on-accent, #fff); }
.antimold-card .am-icon svg { width: 24px; height: 24px; }
.antimold-card .am-text { flex: 1; min-width: 0; }
.antimold-card .am-text strong { display: block; font-size: 15px; }
.antimold-card .am-text .small { white-space: normal; line-height: 1.45; }
.antimold-card > button { white-space: nowrap; margin: 0; }
@media (max-width: 420px) {
  .card.antimold-card { grid-template-columns: 36px minmax(0, 1fr) auto auto; gap: 8px; padding: 12px; }
  .antimold-card .am-icon { width: 36px; height: 36px; }
}
.badge.neutral { color: var(--muted); }

/* ============================== Controls ============================== */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: 34px; padding: 0 15px; border-radius: var(--r2);
  border: 1px solid var(--hairline); background: var(--surface);
  color: var(--ink); font: 550 13px/1 var(--sans); letter-spacing: -.01em;
  cursor: pointer; white-space: nowrap;
  transition: all .14s ease; box-shadow: var(--lift);
}
.btn:hover:not(:disabled) { border-color: var(--faint); }
.btn:active:not(:disabled) { transform: translateY(.5px); }
/* Primary is ink, never accent — accent belongs to data. */
.btn.primary { background: var(--btn-fill); border-color: var(--btn-fill); color: var(--btn-text); font-weight: 600; }
.btn.primary:hover:not(:disabled) { opacity: .87; border-color: var(--btn-fill); }
.btn.danger { color: var(--critical-ink); }
.btn.ghost { background: transparent; box-shadow: none; border-color: transparent; }
.btn.ghost:hover:not(:disabled) { background: var(--surface-2); border-color: transparent; }
.btn.sm { height: 28px; padding: 0 11px; font-size: 12px; border-radius: var(--r1); }
.btn:disabled { opacity: .38; cursor: not-allowed; box-shadow: none; }

.chip {
  height: 29px; padding: 0 13px; border-radius: 999px;
  border: 1px solid var(--hairline); background: transparent;
  color: var(--muted); font: 500 12.5px/1 var(--sans); letter-spacing: -.005em;
  cursor: pointer; transition: all .14s ease;
  display: inline-flex; align-items: center;
}
.chip:hover { border-color: var(--faint); color: var(--ink-2); }
.chip.on { background: var(--ink); border-color: var(--ink); color: var(--paper); font-weight: 550; }
.chip .x { margin-left: 7px; opacity: .55; }

.seg {
  display: inline-flex; padding: 2px; gap: 1px;
  background: var(--surface-2); border-radius: var(--r2);
}
.seg button {
  border: 0; background: transparent; color: var(--muted);
  font: 550 12.5px/1 var(--sans); letter-spacing: -.005em;
  height: 26px; padding: 0 13px; border-radius: 9px; cursor: pointer;
  transition: all .13s ease;
}
.seg button:hover { color: var(--ink-2); }
.seg button.on { background: var(--surface); color: var(--ink); box-shadow: var(--lift); font-weight: 600; }

input, select, textarea {
  background: var(--surface); border: 1px solid var(--hairline);
  border-radius: var(--r2); color: var(--ink);
  font: 400 13.5px/1.4 var(--sans); padding: 9px 12px;
  transition: border-color .14s, box-shadow .14s;
  max-width: 100%;
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-wash);
}
input::placeholder { color: var(--faint); }
label.f {
  display: flex; flex-direction: column; gap: 6px; min-width: 0;
  font: 600 10.5px/1 var(--sans); letter-spacing: .08em;
  text-transform: uppercase; color: var(--faint);
}
.form-row { display: flex; gap: 9px; align-items: flex-end; flex-wrap: wrap; }
.controls { display: flex; gap: 9px; flex-wrap: wrap; align-items: center; margin-bottom: 18px; }
.controls .grow { flex: 1; min-width: 200px; }
.chipset { display: flex; gap: 5px; flex-wrap: wrap; }

.switch { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; font-size: 13px; color: var(--ink-2); }
.switch input { position: absolute; opacity: 0; pointer-events: none; }
.switch .track {
  width: 34px; height: 20px; border-radius: 999px; background: var(--surface-3);
  position: relative; transition: background .18s ease; flex: none;
}
.switch .track::after {
  content: ''; position: absolute; top: 2px; left: 2px;
  width: 16px; height: 16px; border-radius: 50%; background: #fff;
  box-shadow: 0 1px 2px rgba(0,0,0,.25); transition: transform .18s cubic-bezier(.32,.72,0,1);
}
.switch input:checked + .track { background: var(--switch-on); }
.switch input:checked + .track::after { background: var(--switch-knob-on, #fff); }
.switch input:checked + .track::after { transform: translateX(14px); }
.switch input:focus-visible + .track { outline: 2px solid var(--accent); outline-offset: 3px; }
/* Delivery → Cars: one switch per car under the severity picker. */
.notify-cars { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
/* "How it works": the explanation folded away until asked for. */
details.howto { margin: 0 0 10px; }
details.howto summary { cursor: pointer; font: 500 12.5px/1.4 var(--sans); color: var(--accent); list-style: none; }
details.howto summary::-webkit-details-marker { display: none; }
details.howto summary::before { content: '›'; display: inline-block; margin-right: 6px; transition: transform .15s; }
details.howto[open] summary::before { transform: rotate(90deg); }
details.howto p { margin: 8px 0 0; font-size: 12.5px; color: var(--muted); line-height: 1.5; }
/* Settings: the chip strip above the cards, and the ring a jumped-to card wears. */
.settings-jump { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 0 0 14px; }
.settings-jump .sj-lab { font: 600 10.5px/1 var(--sans); letter-spacing: .11em; text-transform: uppercase; color: var(--faint); margin-right: 4px; }
.settings-overview { margin: 0 0 24px; }
.settings-overview h2 { font-size: clamp(23px, 3vw, 30px); margin: 0 0 8px; color: var(--ink); }
.settings-overview > p { margin: 0 0 18px; color: var(--ink-2); font-size: 14px; line-height: 1.5; }
.settings-categories { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.settings-category { position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: 8px; min-height: 130px; padding: 16px; border: 1px solid var(--hairline-2); border-radius: 18px; background: var(--surface); color: var(--ink); text-align: left; cursor: pointer; font: inherit; }
.settings-category:hover { border-color: var(--accent); background: var(--surface-2); }
.settings-category:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.settings-category[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-wash); }
.settings-category strong { font-size: 15px; line-height: 1.35; }
.settings-category > span:not(.settings-category-icon):not(.settings-category-check) { color: var(--ink-2); font-size: 12px; line-height: 1.4; }
.settings-category-icon { color: var(--accent); font-size: 24px; line-height: 1; margin-bottom: 5px; }
.settings-category-check { position: absolute; right: 14px; top: 14px; color: var(--accent); visibility: hidden; }
.settings-category[aria-pressed="true"] .settings-category-check { visibility: visible; }
.settings-filtered-out { display: none !important; }
#liveActivitiesCard .notify-cars { display: flex; flex-wrap: wrap; gap: 12px 24px; }
#liveActivitiesCard .switch { min-height: 44px; font-size: 14px; }
.settings-grid.notification-rules-grid { grid-template-columns: repeat(auto-fit, minmax(min(100%, 310px), 1fr)); gap: 24px; }
.notification-rules-grid .switch { min-height: 40px; font-size: 14px; line-height: 1.4; color: var(--ink); }
.notification-rules-grid .g-lab { color: var(--ink); font-size: 15px; font-weight: 600; }
@media (pointer: coarse) { .notification-rules-grid .switch { min-height: 44px; } }
.sound-list { display: flex; flex-wrap: wrap; gap: 6px; }
.sound-gallery > summary { margin-top: 4px; }
/* On a phone the five category tiles took most of the first screen: two
   columns of one-line buttons show every choice and the settings below. */
@media (max-width: 600px) {
  .settings-overview { margin-bottom: 16px; }
  .settings-overview h2 { font-size: 22px; }
  .settings-overview > p { display: none; }
  .settings-categories { grid-template-columns: 1fr 1fr; gap: 8px; }
  .settings-category { min-height: 52px; flex-direction: row; align-items: center; gap: 10px; padding: 10px 30px 10px 12px; border-radius: 14px; }
  .settings-category strong { font-size: 14px; }
  .settings-category-icon { margin-bottom: 0; font-size: 20px; }
  .settings-category > span:not(.settings-category-icon):not(.settings-category-check) { display: none; }
  .settings-category-check { top: 50%; right: 10px; transform: translateY(-50%); }
}
[id^="rule-"] { scroll-margin-top: 110px; border-radius: var(--r1); }
[id^="rule-"].jump-flash { outline: 3px solid var(--accent); outline-offset: 5px; }
.settings-grid .card, #teslaCard, .section-title { scroll-margin-top: 78px; }
.card.jump-flash { animation: cardFlash 1.6s ease-out; }
@keyframes cardFlash {
  0%, 45% { box-shadow: 0 0 0 3px var(--warning-ink); }
  100% { box-shadow: none; }
}
/* The gear beside an Anti Mold row: its settings live on the Settings page. */
.am-gear {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 9px; flex: none;
  border: 1px solid var(--hairline); background: transparent; color: var(--muted); cursor: pointer;
  transition: color .13s, border-color .13s;
}
.am-gear:hover { color: var(--ink-2); border-color: var(--faint); }
.am-gear svg { width: 15px; height: 15px; }
/* Delivery → where push goes: one row per browser or app on the account. */
.devices .device {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 0; border-top: 1px solid var(--hairline-2);
}
.devices .device:first-of-type { border-top: 0; }
.devices .device .ic { width: 18px; height: 18px; color: var(--muted); flex: none; }
.devices .device .ic svg { width: 18px; height: 18px; display: block; }
.devices .device .who { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; font-size: 13px; color: var(--ink); }
.devices .device .who .tag { margin-left: 4px; }
.devices .device.here .who b { color: var(--accent); }
.devices .device .btn { flex: none; }
.apns-key { font-family: var(--mono); font-size: 11.5px; line-height: 1.4; min-height: 84px; resize: vertical; }
/* The alert a notification pointed at, lit for a moment. */
.feed-item.focus { animation: feedFocus 6s ease-out; }
@keyframes feedFocus {
  0%, 60% { background: var(--warning-bg, rgba(185, 135, 8, .12)); box-shadow: inset 3px 0 0 var(--warning-ink); }
  100% { background: transparent; box-shadow: none; }
}

/* ============================== Tables ============================== */

.tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
.tbl th {
  position: sticky; top: 64px; z-index: 5;
  text-align: left; font: 600 10px/1 var(--sans);
  text-transform: uppercase; letter-spacing: .1em; color: var(--faint);
  padding: 11px 13px; background: var(--surface);
  border-bottom: 1px solid var(--hairline);
}
.tbl td { padding: 9px 13px; border-bottom: 1px solid var(--hairline-2); vertical-align: top; }
.tbl tr.rowbtn { cursor: pointer; }
.tbl tr.rowbtn:hover td { background: var(--surface-2); }
.tbl .fld { color: var(--ink); font-weight: 500; letter-spacing: -.005em; }
.tbl .val { color: var(--ink); font-variant-numeric: tabular-nums; }
.tbl .desc-row td { background: var(--surface-2); color: var(--muted); font-size: 12.5px; }
.mono { font-family: var(--mono); font-size: 12px; letter-spacing: -.02em; }

/* ============================== Feed ============================== */

.feed { display: flex; flex-direction: column; }
.feed-item {
  position: relative;
  padding: 15px 16px 15px 20px;
  border-bottom: 1px solid var(--hairline-2);
  cursor: pointer; transition: background .13s;
}
.feed-item:hover { background: var(--surface); }
.feed-item::before {
  content: ''; position: absolute; left: 4px; top: 19px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--faint);
}
.feed-item.sev-info::before { background: var(--accent); }
.feed-item.sev-warning::before { background: var(--warning); }
.feed-item.sev-serious::before { background: var(--serious); }
.feed-item.sev-critical::before { background: var(--critical); }
.feed-head { display: flex; gap: 9px; align-items: baseline; flex-wrap: wrap; }
.feed-head .name { color: var(--ink); font-weight: 600; letter-spacing: -.01em; }
.feed-head .name.mono { font-size: 12.5px; font-weight: 500; }
.feed-head .time {
  margin-left: auto; color: var(--faint); font-size: 11.5px;
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.feed-detail { color: var(--muted); font-size: 13px; margin-top: 4px; letter-spacing: -.005em; }
.feed-raw { margin-top: 10px; }
.feed-raw pre {
  background: var(--paper); border: 1px solid var(--hairline-2); border-radius: var(--r1);
  padding: 12px; overflow: auto; font-size: 11.5px; color: var(--ink-2); max-height: 320px;
  font-family: var(--mono);
}
.tag {
  font: 500 10.5px/1 var(--sans); letter-spacing: .02em;
  padding: 4px 7px; border-radius: 5px;
  background: var(--surface-2); color: var(--muted); white-space: nowrap;
}

/* ============================== Charts ============================== */

.chart-card { min-height: 248px; }
.chart-wrap { position: relative; height: 200px; }
.timeline { display: flex; height: 22px; border-radius: var(--r1); overflow: hidden; background: var(--grid); }
.timeline .seg2 { min-width: 1px; transition: opacity .14s; }
.timeline .seg2:hover { opacity: .7; }
.legend { display: flex; gap: 16px; font-size: 11.5px; color: var(--muted); flex-wrap: wrap; }
.legend .sw { display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-right: 6px; }

/* ============================== Tires ============================== */

/* The car from above with a tire at each corner; every chip sits beside its
   own wheel. Grid areas split at the beltline, close enough to the axles. */
.tires-map {
  display: grid;
  grid-template-areas: "fl car fr" "rl car rr";
  grid-template-columns: 1fr auto 1fr;
  gap: 14px 14px; align-items: center; justify-items: center;
  margin-top: 16px; padding-bottom: 4px;
  max-width: 380px; margin-left: auto; margin-right: auto;
}
.tires-car { width: 84px; grid-area: car; }
.tires-car .body { stroke: var(--faint); stroke-width: 2.5; fill: var(--surface-2); stroke-linecap: round; }
.tires-car .glass { fill: var(--surface-3); }
.tires-car .tw { fill: var(--ink); opacity: .7; }
.tires-car .tw.good { fill: var(--good); opacity: .85; }
.tires-car .tw.warnish { fill: var(--warning); opacity: .95; }
.tires-car .tw.warn { fill: var(--critical); opacity: 1; }
.tire-chip.left { grid-area: fl; justify-self: end; text-align: right; }
.tire-chip.right { grid-area: fr; justify-self: start; text-align: left; }
.tire-chip.left.rear { grid-area: rl; }
.tire-chip.right.rear { grid-area: rr; }
.tire-chip .p { font: 700 17px var(--sans); color: var(--ink); letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.tire-chip .l { font-size: 9.5px; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); margin-top: 1px; }
.tire-chip.good .p { color: var(--good-ink, var(--good)); }
.tire-chip.warnish .p { color: var(--warning); }
.tire-chip.crit .p { color: var(--critical); }

.tires { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; max-width: 340px; margin: 0 auto; }
.tire {
  background: var(--surface-2); border-radius: var(--r2);
  padding: 12px; text-align: center; border: 1px solid transparent;
}
.tire .p {
  font-size: 19px; font-weight: 600; color: var(--ink);
  font-variant-numeric: tabular-nums; letter-spacing: -.03em;
}
.tire .l {
  font-size: 10px; color: var(--faint); margin-top: 3px;
  text-transform: uppercase; letter-spacing: .08em; font-weight: 600;
}
.tire.warn { border-color: var(--warning); }
.tire.warn .p { color: var(--warning-ink); }

.map-embed { width: 100%; height: 236px; border: 0; border-radius: var(--r2); background: var(--surface-2); }
:root[data-theme="dark"] .map-embed { filter: grayscale(.3) brightness(.8) contrast(1.06); }
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) .map-embed { filter: grayscale(.3) brightness(.8) contrast(1.06); }
}

/* ============================== Login ============================== */

.overlay {
  position: fixed; inset: 0; z-index: 100;
  background: var(--paper);
  display: flex;
  /* iOS toolbars shrink the visible area under a fixed layer; sizing to the
     dynamic viewport plus margin:auto on the card keeps it truly centered on
     phones, yet scrollable instead of clipped when the keyboard is up. */
  height: 100dvh;
  padding: max(20px, env(safe-area-inset-top)) 20px max(20px, env(safe-area-inset-bottom));
  /* overflow-y:auto silently computes overflow-x to auto as well, and the
     drifting glow layer scales past the viewport — clamp it or phones can pan
     the login sideways. */
  overflow-y: auto; overflow-x: hidden;
}

/* Theme cycle on the login screen — the sidebar switch is locked behind it. */
.login-themebtn {
  position: absolute; z-index: 2;
  top: max(14px, env(safe-area-inset-top)); right: 16px;
  width: 44px; height: 44px; border-radius: 50%;
  border: 1px solid var(--hairline); background: var(--surface); color: var(--ink-2);
  font-size: 16px; line-height: 1; cursor: pointer;
  transition: border-color .15s ease, color .15s ease;
}
.login-themebtn:hover { border-color: var(--accent); color: var(--accent); }
.overlay .login-card { margin: auto; }
/* display:grid above outranks the UA [hidden] rule — restate it, or a hidden
   overlay silently swallows every click in the app. */
.overlay[hidden] { display: none; }

/* Faint instrument-dial motif behind the login, built from gradients only. */
.overlay::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(1px 1px at 50% 50%, var(--hairline) 99%, transparent 100%),
    conic-gradient(from 200deg at 50% 46%, var(--accent-wash) 0deg, transparent 82deg);
  opacity: .8;
  -webkit-mask-image: radial-gradient(closest-side at 50% 46%, #000 30%, transparent 78%);
  mask-image: radial-gradient(closest-side at 50% 46%, #000 30%, transparent 78%);
}
/* Two soft accent glows drift behind the card — same ambience as the landing hero. */
.overlay::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(340px 300px at 24% 18%, var(--accent-wash), transparent 70%),
    radial-gradient(420px 360px at 78% 86%, var(--accent-wash), transparent 70%);
  animation: glowDrift 9s ease-in-out infinite alternate;
}
@keyframes glowDrift { to { transform: translate(0, -14px) scale(1.04); } }

.login-card {
  position: relative; z-index: 1;
  width: 384px; max-width: 100%;
  display: flex; flex-direction: column; gap: 0;
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--hairline-2);
  border-radius: var(--r4);
  padding: 36px 32px 26px;
  box-shadow: var(--lift-2);
}
.login-mark {
  position: relative;
  width: 54px; height: 54px; margin: 6px auto 24px;
  display: grid; place-items: center;
  border-radius: 15px; color: #fff;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  box-shadow: 0 10px 26px rgba(42, 120, 214, .3);
}
.login-mark .glyph { width: 34px; height: 34px; }
/* A soft glow that breathes behind the badge — quieter than a ring, and it lets
   the spark flicker stay the hero. */
.login-mark { animation: markGlow 4s ease-in-out 1.4s infinite alternate; }
@keyframes markGlow {
  from { box-shadow: 0 10px 26px rgba(42, 120, 214, .3); }
  to { box-shadow: 0 10px 34px rgba(42, 120, 214, .5); }
}
.login-card h1 {
  font-size: 25px; font-weight: 700; color: var(--ink);
  letter-spacing: -.035em; margin-bottom: 6px;
}
.login-card .sub { color: var(--muted); font-size: 13.5px; margin-bottom: 22px; letter-spacing: -.005em; }
.login-tabs { margin: 0 auto 18px; align-self: center; }
.login-card input {
  height: 46px; text-align: center; font-size: 15px;
  letter-spacing: .02em; background: var(--surface-2);
  border: 1px solid transparent;
  border-radius: var(--r2); margin-bottom: 9px;
  transition: border-color .18s ease, box-shadow .18s ease, background .18s ease;
}
.login-card input:focus {
  outline: none; background: var(--surface);
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash);
}
.login-card .btn { height: 46px; font-size: 14px; margin-top: 2px; }
.login-error { color: var(--critical-ink); font-size: 12.5px; margin-top: 14px; }
.login-error:not([hidden]) { animation: shake .4s cubic-bezier(.36,.07,.19,.97); }
@keyframes shake {
  20%, 60% { transform: translateX(-5px); }
  40%, 80% { transform: translateX(4px); }
}
.login-foot { margin-top: 26px; font-size: 11.5px; color: var(--faint); letter-spacing: .02em; }
.login-foot a { color: var(--muted); text-decoration: none; }
.login-foot a:hover { color: var(--accent); }

/* ============================== Toasts ============================== */

.toasts {
  position: fixed; bottom: 24px; right: 24px; z-index: 200;
  display: flex; flex-direction: column; gap: 8px; max-width: 400px;
}
.toast {
  display: flex; align-items: flex-start; gap: 10px;
  background: var(--ink); color: var(--paper);
  border-radius: var(--r2); padding: 13px 16px;
  font-size: 13px; line-height: 1.45; letter-spacing: -.005em;
  box-shadow: var(--lift-3);
  animation: rise .24s cubic-bezier(.32,.72,0,1);
}
.toast::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: var(--good); flex: none; margin-top: 6px;
}
.toast.err::before { background: var(--critical); }
.toast:not(.has-action) { cursor: pointer; }
@keyframes rise { from { transform: translateY(12px); opacity: 0; } }

/* ============================== Misc ============================== */

.muted { color: var(--muted); }
.small { font-size: 12px; }
.empty { text-align: center; color: var(--muted); padding: 84px 20px; }
.empty h2 {
  color: var(--ink); font-size: 22px; font-weight: 700;
  margin-bottom: 10px; letter-spacing: -.03em;
}
.empty p { max-width: 440px; margin: 0 auto 6px; font-size: 13.5px; }
a { color: var(--ink); text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--hairline); }
a:hover { text-decoration-color: currentColor; }

.section-title {
  font: 600 10.5px/1 var(--sans); letter-spacing: .11em; text-transform: uppercase;
  color: var(--faint); margin: 34px 0 13px;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.section-title::after { content: ''; flex: 1; height: 1px; background: var(--hairline-2); }
.section-title .note { text-transform: none; letter-spacing: -.005em; font-weight: 400; font-size: 12px; }
/* A plain-language gloss on a card's shorthand title. */
.card h3 .note {
  text-transform: none; letter-spacing: 0; font-weight: 400; font-size: 11px;
  color: var(--ink-3); padding: 2px 7px; border-radius: 20px;
  background: var(--surface-2); white-space: nowrap;
}

.settings-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); gap: 12px; align-items: start; }
.meter { height: 5px; border-radius: 3px; background: var(--surface-3); overflow: hidden; }
.meter .fill { height: 100%; background: var(--accent); }

/* setup wizards */
.steps { display: flex; flex-direction: column; }
.step {
  display: flex; align-items: flex-start; gap: 13px;
  padding: 15px 0; border-top: 1px solid var(--hairline-2);
}
.step:first-child { border-top: 0; padding-top: 4px; }
.step .num {
  width: 20px; height: 20px; border-radius: 50%; flex: none; margin-top: 1px;
  display: grid; place-items: center;
  font: 600 10.5px/1 var(--sans);
  background: var(--surface-2); color: var(--faint);
}
.step.done .num { background: var(--good); color: #fff; }
.step.active .num { background: var(--ink); color: var(--paper); }
.step .body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 9px; }
.step .title { font-weight: 600; color: var(--ink); font-size: 13.5px; letter-spacing: -.012em; }
.step .desc { font-size: 12.5px; color: var(--muted); letter-spacing: -.005em; line-height: 1.5; }
.step .desc code {
  font-family: var(--mono); font-size: 11.5px;
  background: var(--surface-2); padding: 1px 5px; border-radius: 4px;
}

.logbox {
  background: var(--paper); border: 1px solid var(--hairline-2); border-radius: var(--r1);
  padding: 11px 13px; max-height: 200px; overflow: auto;
  font-family: var(--mono); font-size: 11px; color: var(--muted);
  white-space: pre-wrap; word-break: break-all; line-height: 1.6;
}
.copyrow {
  display: flex; align-items: center; gap: 9px;
  background: var(--surface-2); border-radius: var(--r1);
  padding: 8px 9px 8px 12px;
  font-family: var(--mono); font-size: 11.5px; color: var(--ink);
  overflow-wrap: anywhere; letter-spacing: -.02em;
}
.copyrow a { color: var(--ink); }
.copyrow .btn { margin-left: auto; flex: none; }

/* ============================== Mobile ============================== */

@media (max-width: 940px) {
  .sidebar {
    inset: auto 0 0 0; width: auto; height: 58px; flex-direction: row;
    align-items: center; padding: 0 6px;
    border-right: 0; border-top: 1px solid var(--hairline);
    background: var(--veil);
    backdrop-filter: saturate(180%) blur(16px);
    -webkit-backdrop-filter: saturate(180%) blur(16px);
  }
  .brand, .sidebar-foot { display: none; }
  .nav { flex-direction: row; width: 100%; justify-content: space-around; gap: 0; }
  .nav-item {
    flex-direction: column; gap: 4px; font-size: 9.5px; font-weight: 600;
    padding: 4px 6px; background: transparent !important; letter-spacing: 0;
  }
  .nav-item.active { color: var(--ink); }
  .nav-item svg { width: 18px; height: 18px; }
  .nav-badge { position: absolute; top: -1px; right: 2px; }
  .main { margin-left: 0; }
  .topbar { padding: 12px 16px; }
  .view { padding: 18px 16px 92px; }
  .hero { gap: 20px; padding-bottom: 20px; }
  .hero-info h1 { font-size: 27px; }
  .ring { --ring: 96px; }
  .tbl th { top: 0; }
  .settings-grid { grid-template-columns: 1fr; }
  .toasts { left: 16px; right: 16px; bottom: calc(70px + env(safe-area-inset-bottom, 0px)); max-width: none; }
}

/* ==========================================================================
   Instrument components — gauges, sparklines, controls, customisation
   ========================================================================== */

/* Arc gauge: a 270° sweep open at the bottom, like a cluster dial. */
.gauge { position: relative; display: inline-grid; place-items: center; }
.gauge svg { display: block; }
.gauge .g-track { stroke: var(--surface-3); fill: none; stroke-linecap: round; }
.gauge .g-val {
  fill: none; stroke-linecap: round; stroke: var(--accent);
  transition: stroke-dashoffset .6s cubic-bezier(.32,.72,0,1), stroke .3s;
}
.gauge .g-val.good { stroke: var(--good); }
.gauge .g-val.warning { stroke: var(--warning); }
.gauge .g-val.critical { stroke: var(--critical); }
.gauge .g-tick { stroke: var(--hairline); stroke-width: 1.5; }
.gauge .g-mid {
  position: absolute; inset: 0; display: grid; place-content: center;
  text-align: center; pointer-events: none;
}
.gauge .g-num {
  /* font-size is set inline, scaled to the ring — see fitFont() in app.js */
  font-size: 26px; font-weight: 600; color: var(--ink);
  letter-spacing: -.04em; line-height: 1; font-variant-numeric: tabular-nums;
}
.gauge .g-num sup { font-size: 12px; font-weight: 500; margin-left: 1px; letter-spacing: 0; }
.gauge .g-lab {
  font-size: 9px; color: var(--faint); text-transform: uppercase;
  letter-spacing: .13em; font-weight: 600; margin-top: 4px;
}
/* .sm only affects the label; the number is sized inline. */
.gauge.sm .g-lab { font-size: 8.5px; margin-top: 2px; }

.gauge-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: 10px; justify-items: center;
}

/* Sparkline strip shown under a card heading. */
/* Hover readout. The wrapper is the hit target, so the whole 34px band responds
   rather than only the 2px line itself. */
.login-alt {
  background: none; border: none; padding: 4px; margin-top: 16px; cursor: pointer;
  align-self: center;
  font: 500 12.5px var(--sans); color: var(--muted);
  transition: color .15s ease;
}
.login-alt:hover { color: var(--accent); text-decoration: underline; }

.spark-wrap { position: relative; cursor: crosshair; }
.spark-cross {
  position: absolute; top: 0; bottom: 0; width: 1px;
  background: var(--hairline); pointer-events: none;
}
.spark-dot {
  position: absolute; width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px;
  border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 2px var(--surface); pointer-events: none;
}
.spark-tip {
  position: absolute; top: -6px; z-index: 5; pointer-events: none; white-space: nowrap;
  display: flex; align-items: baseline; gap: 6px;
  padding: 4px 8px; border-radius: 7px;
  background: var(--ink); color: var(--paper);
  font: 500 11px/1.2 var(--sans); box-shadow: var(--lift-2);
}
/* display:flex above outranks the UA [hidden] rule, same trap as .overlay. */
.spark-tip[hidden] { display: none; }
.spark-tip b { font-weight: 700; }
.spark-tip span { opacity: .62; font-size: 10px; }

.spark { display: block; width: 100%; height: 34px; overflow: visible; }
.spark path.line { fill: none; stroke: var(--accent); stroke-width: 1.75; stroke-linejoin: round; stroke-linecap: round; }
.spark path.area { fill: var(--accent-wash); stroke: none; }
.spark .dot { fill: var(--accent); }
.spark-empty {
  height: 34px; display: grid; place-items: center;
  font-size: 10.5px; color: var(--faint); letter-spacing: .04em;
}

/* Battery-health capacity trend. */
.trend { margin: 12px 0 4px; }
.trend-title {
  margin: 0 0 8px 36px;
  font-size: 10px; letter-spacing: .05em; color: var(--faint);
}
.trend-plot { position: relative; height: 56px; margin-left: 36px; cursor: crosshair; }
.trend-plot svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; display: block; }
.trend-plot .grid { stroke: var(--hairline-2); }
.trend-plot .ref { stroke: var(--muted); stroke-dasharray: 4 3; opacity: .55; }
.trend-plot .line {
  fill: none; stroke: var(--accent); stroke-width: 1.75;
  stroke-linejoin: round; stroke-linecap: round;
}
.trend-pt {
  position: absolute; width: 6px; height: 6px; margin: -3px 0 0 -3px;
  border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 2px var(--surface); pointer-events: none;
}
.trend-ylab {
  position: absolute; left: -36px; width: 30px; text-align: right;
  font-size: 9.5px; color: var(--faint);
}
.trend-reflab { position: absolute; right: 2px; font-size: 9.5px; color: var(--muted); }
.trend-lastlab {
  position: absolute; transform: translate(-115%, -125%); pointer-events: none;
  font: 600 10px/1 var(--sans); color: var(--ink-2);
}
.trend-x {
  display: flex; justify-content: space-between; margin: 4px 0 0 36px;
  font-size: 9.5px; color: var(--faint);
}

/* Energy-flow strip while charging. */
.flow {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border-radius: var(--r2); background: var(--surface-2);
}
.flow .node { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.flow .node .v { font-size: 15px; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.flow .node .l { font-size: 9.5px; color: var(--faint); text-transform: uppercase; letter-spacing: .1em; font-weight: 600; }
.flow .arrow { flex: 1; height: 3px; border-radius: 2px; background: var(--surface-3); position: relative; overflow: hidden; min-width: 24px; }
.flow .arrow::after {
  content: ''; position: absolute; inset: 0;
  background: repeating-linear-gradient(90deg, transparent 0 18px, var(--accent) 31px, transparent 32px);
  animation: chargeFlow 1.6s linear infinite;
  animation-delay: var(--charge-phase, 0s);
}
@keyframes chargeFlow { to { background-position-x: 32px; } }
:root[data-page-hidden="true"] .ring .charge-orbit circle,
:root[data-page-hidden="true"] .flow .arrow::after { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
  .flow .arrow::after { animation: none; }
}

/* Controls (vehicle commands). */
.cmd-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; }
.cmd {
  display: flex; align-items: center; gap: 9px;
  height: 44px; padding: 0 14px; border-radius: var(--r2);
  border: 1px solid var(--hairline); background: var(--surface);
  color: var(--ink); font: 550 13px/1 var(--sans); letter-spacing: -.01em;
  cursor: pointer; text-align: left; transition: all .14s ease;
}
.cmd:hover:not(:disabled) { border-color: var(--faint); background: var(--surface-2); }
.cmd:disabled { opacity: .4; cursor: not-allowed; }
.cmd .k { margin-left: auto; font-size: 10px; color: var(--faint); text-transform: uppercase; letter-spacing: .08em; }
.cmd.danger { color: var(--critical-ink); }
.cmd.busy { opacity: .6; }

/* Session / trip rows. */
.rows { display: flex; flex-direction: column; }
.row-item {
  display: grid; grid-template-columns: auto 1fr auto; gap: 14px; align-items: center;
  padding: 13px 2px; border-top: 1px solid var(--hairline-2);
}
.row-item:first-child { border-top: 0; }
.row-item .when { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.row-item .what { min-width: 0; }
.row-item .what .t { color: var(--ink); font-weight: 550; font-size: 13.5px; letter-spacing: -.01em; }
.row-item .what .s { color: var(--faint); font-size: 11.5px; margin-top: 2px; }
.row-item .amt { text-align: right; font-variant-numeric: tabular-nums; }
.row-item .amt .t { color: var(--ink); font-weight: 600; font-size: 14px; letter-spacing: -.02em; }
.row-item .amt .s { color: var(--faint); font-size: 11px; margin-top: 2px; }

/* Horizontal proportion bar (charging by location, etc). */
.propbar { display: flex; height: 8px; border-radius: 4px; overflow: hidden; background: var(--surface-3); }
.propbar span { display: block; }

/* Dashboard customisation */
.customize-bar {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  padding: 12px 14px; margin-bottom: 14px;
  border: 1px dashed var(--hairline); border-radius: var(--r2);
  background: var(--surface);
}
.wlist { display: flex; flex-direction: column; gap: 2px; }
.witem {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 11px; border-radius: var(--r2); background: var(--surface-2);
}
.witem .nm { flex: 1; font-size: 13px; color: var(--ink); font-weight: 500; }
.witem.off .nm { color: var(--faint); }
.witem .mv {
  width: 26px; height: 26px; border-radius: 7px; border: 1px solid var(--hairline);
  background: var(--surface); color: var(--ink-2); cursor: pointer;
  font-size: 12px; line-height: 1; display: grid; place-items: center;
}
.witem .mv:disabled { opacity: .3; cursor: not-allowed; }
.card.editing { outline: 1px dashed var(--accent-ring); outline-offset: 3px; }

/* Rendered picture of the actual car, from Tesla's configurator compositor. */
.car-shot {
  width: 300px; max-width: 44vw; height: auto; flex: none;
  margin: -14px -10px -18px -18px;
  filter: drop-shadow(0 10px 12px rgba(10, 10, 9, .22));
}
/* A desktop window has room to enjoy the car — let it breathe there. */
@media (min-width: 1200px) {
  .car-shot { width: 400px; margin: -26px -14px -26px -26px; }
}
@media (min-width: 1600px) {
  .car-shot { width: 470px; margin: -32px -16px -30px -32px; }
}
:root[data-theme="dark"] .car-shot { filter: drop-shadow(0 14px 26px rgba(0, 0, 0, .6)); }
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) .car-shot { filter: drop-shadow(0 14px 26px rgba(0, 0, 0, .6)); }
}
/* The ring's SVG is authored at a fixed 128px — letting it fill its container
   makes the container the single size knob for the breakpoints below. */
.ring svg { width: 100%; height: 100%; }

/* Mid widths: keep the row, just tighter. Below tablet width the car and ring
   pair up centered on the first row and the name/facts take the full width
   beneath — no stretched voids. */
@media (max-width: 1180px) {
  .hero { gap: 22px; }
  .car-shot { width: 290px; }
}
@media (max-width: 940px) {
  .hero { justify-content: center; gap: 6px 24px; padding-bottom: 18px; }
  /* The car is the hero of the hero — as big as the row can hold. The calc is
     100vw minus view padding, gap and ring, so this pair can NEVER exceed the
     viewport: any overflow makes iOS zoom the whole page out. */
  .car-shot { width: min(370px, calc(100vw - 192px)); max-width: none; margin: -16px -8px -20px -12px; }
  .hero .ring { --ring: 118px; }
  .hero-info { flex-basis: 100%; }
  .hero-info h1 { font-size: 30px; }
}
@media (max-width: 520px) {
  .view { padding: 18px 16px 88px; }
  .hero { gap: 4px 16px; }
  .car-shot { width: min(310px, calc(100vw - 128px)); }
  .hero .ring { --ring: 96px; }
  .hero-info h1 { font-size: 26px; }
}
/* Phone topbar: the pills and both action buttons must share 100vw. */
@media (max-width: 640px) {
  .topbar { padding: 12px 16px; gap: 8px 10px; }
  .top-actions { gap: 6px; }
  .top-actions .btn { height: 34px; padding: 0 13px; font-size: 12.5px; }
}

/* ==========================================================================
   Interactive gauges — drag the arc to set a value, then commit explicitly.
   Nothing is sent to the car until you confirm; a stray drag must never
   actuate anything.
   ========================================================================== */

.gauge.interactive { cursor: grab; touch-action: none; user-select: none; }
.gauge.interactive:active { cursor: grabbing; }
.gauge.interactive .g-hit { fill: transparent; stroke: transparent; }
.gauge.interactive:hover .g-track { stroke: var(--surface-3); filter: brightness(1.08); }
.gauge .g-pending {
  fill: none; stroke: var(--accent); stroke-linecap: round;
  opacity: .95; stroke-dasharray: 4 4;
}
.gauge .g-knob { fill: var(--surface); stroke: var(--accent); stroke-width: 2.5; }
.gauge.dirty .g-num { color: var(--accent); }

.gauge-commit {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  margin-top: 8px; animation: rise .18s cubic-bezier(.32,.72,0,1);
}
.gauge-wrap { display: flex; flex-direction: column; align-items: center; }
.gauge-hint {
  font-size: 10px; color: var(--faint); text-transform: uppercase;
  letter-spacing: .1em; font-weight: 600; margin-top: 6px;
}

/* Stepper beside a gauge, for precise adjustment. */
.stepper { display: inline-flex; align-items: center; gap: 6px; }
.stepper button {
  width: 28px; height: 28px; border-radius: 8px;
  border: 1px solid var(--hairline); background: var(--surface);
  color: var(--ink); font: 600 15px/1 var(--sans); cursor: pointer;
  display: grid; place-items: center;
}
.stepper button:hover { border-color: var(--faint); }
.stepper button:active { transform: translateY(.5px); }

/* Climate control panel */
.climate-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; }
.toggle-tile {
  display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
  padding: 12px 13px; border-radius: var(--r2);
  border: 1px solid var(--hairline); background: var(--surface);
  cursor: pointer; transition: all .15s ease; text-align: left;
  font: 550 12.5px/1.2 var(--sans); color: var(--ink-2);
}
.toggle-tile:hover:not(:disabled) { border-color: var(--faint); }
.toggle-tile:disabled { opacity: .4; cursor: not-allowed; }
.toggle-tile .st {
  font-size: 10px; text-transform: uppercase; letter-spacing: .1em;
  font-weight: 700; color: var(--faint);
}
.toggle-tile.on { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.toggle-tile.on .st { color: var(--paper); opacity: .7; }
.toggle-tile.busy { opacity: .55; }

/* Level picker (seat heaters, fan) */
.levels { display: inline-flex; gap: 3px; }
.levels button {
  min-width: 26px; height: 26px; padding: 0 7px; border-radius: 7px;
  border: 1px solid var(--hairline); background: var(--surface);
  color: var(--muted); font: 600 11px/1 var(--sans); cursor: pointer;
}
.levels button.on { background: var(--ink); border-color: var(--ink); color: var(--paper); }

/* The cabin map — one tap per seat, heat as warming bars. */
.cabin {
  margin-top: 12px; padding: 12px 14px 8px;
  background: var(--surface-2); border-radius: var(--r2);
}
.cabin-grid { display: flex; gap: 8px; justify-content: center; }
.cabin-grid.rear { margin-top: 8px; }
.seat-tile {
  flex: 1; max-width: 96px;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 9px 6px 7px; border-radius: 10px; cursor: pointer;
  border: 1px solid var(--hairline-2); background: var(--surface); color: var(--muted);
  transition: transform .12s cubic-bezier(.32,.72,0,1), border-color .15s ease, color .15s ease;
}
.seat-tile:hover:not(:disabled) { transform: translateY(-1px); border-color: var(--accent); }
.seat-tile:active:not(:disabled) { transform: scale(.95); }
.seat-tile:disabled { opacity: .55; cursor: default; }
.seat-tile svg { width: 20px; height: 20px; }
.seat-tile .lbl { font: 600 9.5px var(--sans); letter-spacing: .05em; text-transform: uppercase; }
.seat-tile .waves { display: flex; gap: 3px; }
.seat-tile .waves i { width: 10px; height: 4px; border-radius: 2px; background: var(--surface-3); transition: background .2s ease; }
.seat-tile.lvl-1 { color: var(--warning-ink); }
.seat-tile.lvl-1 .waves i:nth-child(1) { background: var(--warning); }
.seat-tile.lvl-2 { color: var(--serious-ink); }
.seat-tile.lvl-2 .waves i:nth-child(-n+2) { background: var(--serious); }
.seat-tile.lvl-3 { color: var(--critical-ink); }
.seat-tile.lvl-3 .waves i { background: var(--critical); }
/* Ventilated seats: same tiles, cool instead of warm. */
.seat-tile.vent.lvl-1, .seat-tile.vent.lvl-2, .seat-tile.vent.lvl-3 { color: var(--accent); }
.seat-tile.vent.lvl-1 .waves i { background: var(--surface-3); }
.seat-tile.vent.lvl-1 .waves i:nth-child(1) { background: var(--accent); }
.seat-tile.vent.lvl-2 .waves i { background: var(--surface-3); }
.seat-tile.vent.lvl-2 .waves i:nth-child(-n+2) { background: var(--accent); }
.seat-tile.vent.lvl-3 .waves i { background: var(--accent); }
.cabin-mode { align-self: center; margin: 0 auto 10px; display: flex; width: fit-content; }
.cabin-mode button { display: inline-flex; align-items: center; gap: 5px; }
.cabin-mode button svg { width: 13px; height: 13px; }
.cabin-hint { text-align: center; margin-top: 8px; font-size: 10px; color: var(--faint); letter-spacing: .03em; }

/* Cabin air flowing — four ribbons swaying, unmistakably HVAC and nothing like
   the static seat-heat bars. */
.hvac-live { display: inline-flex; gap: 3px; align-items: flex-end; height: 22px; padding: 2px 0 3px; }
.hvac-live i {
  width: 3.5px; height: 16px; border-radius: 2px;
  background: linear-gradient(to top, var(--accent), transparent);
  transform-origin: bottom; animation: airflow 1.4s ease-in-out infinite;
}
.hvac-live i:nth-child(2) { animation-delay: .18s; }
.hvac-live i:nth-child(3) { animation-delay: .36s; }
.hvac-live i:nth-child(4) { animation-delay: .54s; }
@keyframes airflow {
  0%, 100% { transform: scaleY(.55); opacity: .55; }
  50% { transform: scaleY(1); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .hvac-live i { animation: none; } }

/* The driving hero strip — pinned above everything while the wheels turn. */
/* Deliberately NOT `overflow: hidden` any more. The scenery that needed the
   clip moved into `.ds-scene`, which clips itself — so the flow pill's popover
   can stand outside the strip instead of being sliced off by it. */
.drive-strip {
  position: relative;
  display: flex; flex-direction: column; gap: 10px;
  margin-bottom: 14px; padding: 14px 16px;
  /* 1.8.47: flat graphite in every theme (Space keeps its navy) — no accent
     gradient, no texture, nothing moving behind the numbers. */
  background: linear-gradient(180deg, #1c2129, #13171d);
  border: 1px solid rgba(255,255,255,.08);
  border-radius: calc(var(--r3) + 2px); color: #fff; box-shadow: 0 2px 8px rgba(0,0,0,.12);
  animation: cardIn .3s cubic-bezier(.32,.72,0,1);
  container-type: inline-size;
  line-height: 1.25;
}
:root[data-theme="art"] .drive-strip { box-shadow: none; }
/* Power and the numbers: stacked on a phone, side by side once the strip is
   wide — nine numbers as five and four, never a stray one. */
.ds-bottom { display: flex; flex-direction: column; gap: 10px; margin-top: 1px; }
@container (min-width: 670px) {
  .ds-bottom { flex-direction: row; gap: 24px; align-items: flex-start; }
  .ds-bottom > .ds-bars { flex: 0 0 42%; }
  .ds-bottom > .ds-stats { flex: 1; grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .ds-bottom > .ds-stats .ds-stat.wide { display: flex; }
}
/* The tenth number (outside temperature) only where it makes five and five;
   a phone keeps three even rows of three. */
.ds-stat.wide { display: none; }
/* Scenery slipping past: faint diagonal streaks drifting backwards, phase-locked
   to the wall clock (--ds-phase) so re-renders never visibly restart them. */
/* Written as a descendant so it outranks `.drive-strip > *` below, which would
   otherwise make this layer `position: relative` and push the strip's own
   content out of the way with it. */
.drive-strip > .ds-scene {
  position: absolute; inset: 0; overflow: hidden;
  border-radius: inherit; pointer-events: none;
}
.drive-strip > * { position: relative; }
.ds-main { display: flex; align-items: center; gap: 10px; min-width: 0; min-height: 32px; padding-right: 42px; }
/* "D · 22 min": the gear and how long the drive has run, quiet beside the speed. */
.ds-meta { font: 500 11px/1 var(--sans); opacity: .55; white-space: nowrap; font-variant-numeric: tabular-nums; }
.ds-dot {
  flex: none; width: 8px; height: 8px; border-radius: 50%; background: #3ee58a;
  animation: dsPulse var(--ds-pulse, 1.6s) ease-out infinite;
}
@keyframes dsPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(62,229,138,.55); }
  70% { box-shadow: 0 0 0 7px transparent; }
}
.ds-speed { font: 700 30px/1 var(--sans); letter-spacing: -.025em; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ds-speed small { font: 600 13px/1 var(--sans); letter-spacing: 0; opacity: .6; margin-left: 5px; }
/* Accelerator as a thin bar; brake, heat and pack temperature as small chips,
   pushed to the right of the speed. */
.ds-signals { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 6px; margin-left: auto; min-width: 0; }
.ds-signals:empty { display: none; }
/* A phone-width strip gives up the expendable signals first, as the apps do:
   the pedal (it is also the kW), then heater and hvac (the aux popover names
   them). */
@container (max-width: 440px) {
  .drive-strip :is(.ds-pedal, .ds-chip.heat, .ds-chip.hvac) { display: none; }
  /* With a warning showing, the gear and drive time make room, so BRAKE, a
     hot motor and the pack temperature all fit beside the speed. */
  .drive-strip .ds-main:has(.ds-chip.brake, .ds-chip.hot) .ds-meta { display: none; }
}
/* A phone: a little tighter, so BRAKE, MOTOR and the pack temperature all fit
   beside the speed (they wrap under it only in the extreme case). */
@container (max-width: 380px) {
  .drive-strip .ds-main { gap: 8px; padding-right: 34px; }
  .drive-strip .ds-speed { font-size: 26px; }
  .drive-strip .ds-speed small { font-size: 12px; margin-left: 4px; }
  .drive-strip .ds-signals { gap: 5px; }
  .drive-strip .ds-chip { font-size: 8.5px; padding: 3px 5px; }
  .drive-strip .ds-hud-btn { width: 28px; height: 28px; top: 14px; right: 12px; font-size: 13px; }
}
.ds-pedal { display: inline-block; width: 64px; height: 4px; border-radius: 2px; background: rgba(255,255,255,.22); overflow: hidden; }
/* No transition: the glide loop in app.js writes this width every frame, and a
   transition restarted sixty times a second only lags behind it. */
.ds-pedal i { display: block; height: 100%; background: #fff; border-radius: 2px; }
.ds-chip {
  display: inline-flex; align-items: center; gap: 2px;
  font: 700 9px/1.2 var(--sans); letter-spacing: .05em; text-transform: uppercase;
  padding: 3px 6px; border-radius: 5px; background: rgba(255,255,255,.18); white-space: nowrap;
}
.ds-chip svg { width: 10px; height: 10px; flex: none; }
.ds-chip.brake { background: #ff5d5d; color: #fff; }
.ds-chip.hot { background: #ffb347; color: #3a2400; }
/* Where to: destination and what is left of the leg, then arrival time and
   arrival charge — a quiet inset instead of one long wrapped sentence. */
.ds-route {
  display: flex; align-items: center; gap: 10px; min-width: 0; min-height: 30px;
  padding: 6px 10px; border-radius: var(--r2); background: rgba(255,255,255,.06);
}
.ds-route .ic { flex: none; width: 20px; display: grid; place-items: center; opacity: .85; }
.ds-route .ic svg { width: 16px; height: 16px; }
.rt-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.rt-main b { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rt-sub { font-size: 11px; color: rgba(255,255,255,.65); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rt-sub em { font-style: normal; color: #ffc861; }
.rt-eta { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 1px; white-space: nowrap; }
.rt-eta b { font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
.rt-eta b small { font-size: 11.5px; font-weight: 500; opacity: .65; }
.rt-eta span { font-size: 11px; opacity: .65; font-variant-numeric: tabular-nums; }
.rt-none { font-size: 12.5px; font-weight: 500; opacity: .6; }
/* Battery ⟷ motors: dots stream right under power, back (green) under regen. */
.ds-flow {
  display: flex; align-items: center; gap: 7px;
  padding: 6px 11px; border-radius: 999px;
  background: rgba(255,255,255,.13);
}
.ds-flow svg { width: 17px; height: 17px; opacity: .9; }
.ds-flow b { font-size: 12px; font-variant-numeric: tabular-nums; margin-left: 2px; }
.f-dots { display: flex; gap: 3px; width: 30px; overflow: hidden; }
.f-dots i {
  width: 4px; height: 4px; border-radius: 50%; background: #fff; opacity: .35; flex: none;
}
.ds-flow.live .f-dots i { animation: flowDots var(--flow-dur, 1s) linear infinite; }
.ds-flow.live .f-dots i:nth-child(2) { animation-delay: calc(var(--flow-dur, 1s) / -3); }
.ds-flow.live .f-dots i:nth-child(3) { animation-delay: calc(var(--flow-dur, 1s) / -1.5); }
@keyframes flowDots {
  0% { transform: translateX(-8px); opacity: 0; }
  30%, 70% { opacity: 1; }
  100% { transform: translateX(26px); opacity: 0; }
}
.ds-flow.regen .f-dots i { animation-direction: reverse; background: #8ef0a1; }
.ds-flow.regen b, .ds-flow.regen svg:first-of-type { color: #8ef0a1; }
/* Drivetrain layout: one lane of dots per motor the car actually has. */
.ds-flow .lanes { display: flex; flex-direction: column; gap: 2px; }
.ds-flow .lane { display: flex; align-items: center; gap: 3px; }
.ds-flow .lane .f-dots { width: 22px; }
.ds-flow .lane .f-dots i { width: 3.5px; height: 3.5px; }
.ds-flow .lane svg.motor { width: 13px; height: 13px; opacity: .9; flex: none; }
.ds-flow .lane em { font: 600 8px var(--sans); font-style: normal; opacity: .7; }
.ds-flow .lane .lkw {
  font: 700 9.5px var(--sans); margin-left: 2px; font-variant-numeric: tabular-nums;
}
.ds-flow .lane .lkw.regen { color: #8ef0a1; }
.ds-flow .aux { display: inline-flex; align-items: center; gap: 3px; }
.ds-flow .aux .akw { font: 700 9.5px var(--sans); font-variant-numeric: tabular-nums; }
.ds-flow .aux svg { width: 13px; height: 13px; opacity: .8; animation: fanSpin 2.6s linear infinite; }
@keyframes fanSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .ds-flow.live .f-dots i { animation: none; opacity: .8; }
  .ds-flow .aux svg { animation: none; }
}

/* What the aux figure is made of — on hover, on tap (.pop) and on keyboard
   focus. Absolutely positioned, so however tall it gets the strip is not one
   pixel taller; it stands ABOVE the pill because the strip is pinned to the top
   of the page and there is always room up there. */
.ds-flow { cursor: help; }
.ds-auxpop {
  position: absolute; left: 50%; bottom: calc(100% + 9px); transform: translateX(-50%);
  z-index: 30; width: max-content; max-width: 250px;
  display: none; flex-direction: column; gap: 3px;
  padding: 9px 11px; border-radius: var(--r2);
  background: var(--ink); color: var(--paper);
  box-shadow: var(--lift-2); text-align: left;
}
.ds-flow:hover .ds-auxpop,
.ds-flow:focus-visible .ds-auxpop,
.ds-flow.pop .ds-auxpop { display: flex; }
/* The little nib, so it reads as belonging to the pill under it. */
.ds-auxpop::after {
  content: ""; position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  border: 5px solid transparent; border-top-color: var(--ink);
}
.ds-auxpop .h {
  font: 700 8.5px var(--sans); letter-spacing: .07em; text-transform: uppercase;
  opacity: .7; display: flex; gap: 5px; align-items: baseline;
}
.ds-auxpop .h em {
  font-style: normal; font-size: 8px; letter-spacing: .05em;
  padding: 1px 4px; border-radius: 6px; background: rgba(255,255,255,.16);
}
.ds-auxpop .r { display: flex; gap: 14px; align-items: baseline; font-size: 11.5px; }
.ds-auxpop .r i { font-style: normal; flex: 1; }
.ds-auxpop .r b { font-weight: 700; font-variant-numeric: tabular-nums; }
.ds-auxpop .r.off { opacity: .45; }
.ds-auxpop .f { font-size: 9.5px; opacity: .6; line-height: 1.35; max-width: 228px; }

.ds-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px 12px; }
.ds-stat { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ds-stat .v { font: 600 14px/1.15 var(--sans); font-variant-numeric: tabular-nums; white-space: nowrap; }
.ds-stat .l { font-size: 8.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .08em; opacity: .5; white-space: nowrap; }
@media (prefers-reduced-motion: reduce) { .ds-dot, .ds-scene::before { animation: none; } }

/* A background repaint of the same view must not look like a page load:
   entrance animations are suppressed, only changed values flash. Continuous
   "live" animations (charging flow, streaks, airflow) are untouched. */
#view.steady .card, #view.steady .hero, #view.steady .drive-strip { animation-name: none; }
#view.steady .trend-plot .line,
#view.steady .trend-pt,
#view.steady .trend-lastlab { animation: none; }
/* The trend line and its dots only draw themselves in on a fresh view, too. */
#view:not(.swap) .trend-plot .line,
#view:not(.swap) .trend-pt,
#view:not(.swap) .trend-lastlab { animation: none; }
#view.steady .gauge .g-val { transition: none; }
/* (No `.car-wrap .beam` here: the greeting is gated by its own `.hello` class
   now, and this rule used to outrank `.car-wrap.lights .beam` — after the
   page settled, a hover could never flash the headlights again.) */

/* Diagnosis hero: verdict ring, live cell strip, thermal scale, drivetrain. */
.diag-hero {
  display: flex; gap: 22px; align-items: center; flex-wrap: wrap;
  padding: 18px 20px; margin-bottom: 16px;
  background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r3);
}
.dh-score { position: relative; width: 128px; height: 128px; flex: none; }
.dh-score svg { width: 100%; height: 100%; }
.dh-score .dh-track { stroke: var(--surface-3); }
.dh-score .dh-val { stroke: var(--good); transition: stroke-dasharray .8s cubic-bezier(.32,.72,0,1); }
.dh-score.warning .dh-val { stroke: var(--warning); }
.dh-score.critical .dh-val { stroke: var(--critical); }
.dh-mid { position: absolute; inset: 0; display: grid; place-content: center; text-align: center; }
.dh-num { font: 700 30px var(--sans); color: var(--ink); letter-spacing: -.04em; }
.dh-l { font-size: 9px; text-transform: uppercase; letter-spacing: .1em; color: var(--muted); }
.dh-body { flex: 1; min-width: 260px; display: flex; flex-direction: column; gap: 10px; }
.dh-verdict { font: 600 15px var(--sans); color: var(--good-ink, var(--good)); letter-spacing: -.01em; }
.dh-verdict.warning { color: var(--warning); } .dh-verdict.critical { color: var(--critical); }
.dh-cells { display: flex; align-items: flex-end; gap: 3px; height: 46px; }
.dh-cells i {
  flex: 1; border-radius: 2px 2px 1px 1px; background: var(--accent); opacity: .8;
  animation: cellRise .5s cubic-bezier(.32,.72,0,1) backwards;
}
.dh-cells i.off { background: var(--warning); }
@keyframes cellRise { from { height: 6% !important; opacity: 0; } }
.dh-legend { display: flex; gap: 16px; flex-wrap: wrap; font-size: 11.5px; color: var(--muted); }
.dh-legend b { color: var(--ink); font-variant-numeric: tabular-nums; }
.dh-therm-bar {
  position: relative; height: 6px; border-radius: 3px;
  background: linear-gradient(90deg, #4a91e8, #3cc23c 42%, #f0ae21 72%, #d03b3b);
}
.dh-therm-bar i {
  position: absolute; top: 50%; width: 12px; height: 12px; margin: -6px 0 0 -6px;
  border-radius: 50%; background: var(--surface); border: 2.5px solid var(--ink);
  transition: left .6s cubic-bezier(.32,.72,0,1);
}
.dh-therm-lab { display: flex; justify-content: space-between; font-size: 10px; color: var(--faint); margin-top: 4px; }
.dh-therm-lab b { color: var(--ink); }
.dh-motors { display: flex; gap: 10px; flex-wrap: wrap; }
.dh-motor {
  display: flex; align-items: center; gap: 7px; padding: 7px 11px;
  background: var(--surface-2); border-radius: 10px;
}
.dh-motor svg { width: 17px; height: 17px; color: var(--accent); }
.dh-motor b { font: 700 13px var(--sans); color: var(--ink); font-variant-numeric: tabular-nums; }
.dh-motor span { font-size: 10px; color: var(--muted); }

/* Diagnosis rows: label · value · verdict chip. In a narrow card the chip
   drops to its own line, right-aligned, rather than squeezing the value —
   "43 °C – 43.5 °C" beside "normal while charging" used to break after every
   character. */
.diag-row {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; padding: 7px 0;
  border-bottom: 1px solid var(--hairline);
}
.diag-row:last-of-type { border-bottom: 0; }
.diag-row .l { color: var(--muted); font-size: 13px; flex: 1; min-width: 96px; }
.diag-row .v {
  font-weight: 600; color: var(--ink); font-size: 13px;
  text-align: right; margin-left: auto; font-variant-numeric: tabular-nums;
  /* Tesla's alert codes are long unbroken tokens — let them break rather than
     stretch the row past the card and push the severity chip outside it. */
  min-width: 0; overflow-wrap: anywhere; word-break: break-word;
}
.diag-row .badge { flex: none; margin-left: auto; }
.diag-row.diag-open { cursor: pointer; }
/* An alert row is two lines — what happened and how serious, then when and
   Analyze — so the title has the card's full width. Side by side, a narrow
   card squeezed it to a letter or two per line. */
.diag-row.diag-alert { display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "title sev" "when act";
  align-items: center; gap: 3px 10px; }
.diag-alert .da-title { grid-area: title; text-align: left; margin-left: 0; overflow-wrap: break-word; word-break: normal; line-height: 1.35; }
.diag-alert .da-title .small { display: block; font-weight: 400; margin-top: 2px; }
.diag-alert .da-sev { grid-area: sev; justify-self: end; }
.diag-alert .da-when { grid-area: when; min-width: 0; font-size: 12px; }
.diag-alert .da-act { grid-area: act; justify-self: end; }
.diag-row.diag-open:hover { background: var(--surface-2); margin: 0 -8px; padding-left: 8px; padding-right: 8px; border-radius: 7px; }
.diag-row.open { background: var(--surface-2); margin: 0 -8px; padding-left: 8px; padding-right: 8px; border-radius: 7px 7px 0 0; }
.diag-detail {
  padding: 8px 8px 12px; margin: 0 -8px 6px; background: var(--surface-2);
  border-radius: 0 0 7px 7px; font-size: 12px;
}
.diag-detail[hidden] { display: none; }
.feed-raw-open {
  margin-top: 8px; padding: 8px 10px; background: var(--paper); border-radius: 6px;
  font: 500 11px/1.5 var(--mono); color: var(--ink-2); overflow: auto; max-height: 220px;
}

/* Software update progress inside the Software card. */
.sw-progress { margin-top: 8px; }
.sw-progress-top {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: 12px; color: var(--muted); margin-bottom: 5px;
}
.sw-progress-top b { color: var(--accent); font-variant-numeric: tabular-nums; }

/* The live BMS card: a temperature dial, current flowing between the terminals,
   and the cell-balance bar. Only ever rendered on a streaming car. */
.pack-live { display: flex; gap: 18px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; }
.pack-dial { position: relative; width: 118px; height: 118px; flex: none; }
.pack-dial svg { width: 100%; height: 100%; }
.pack-dial .pd-track { stroke: var(--surface-3); }
.pack-dial .pd-val { stroke: var(--good); transition: stroke-dasharray .5s cubic-bezier(.32,.72,0,1), stroke .3s; }
.pack-dial.cool .pd-val { stroke: var(--accent); }
.pack-dial.warning .pd-val { stroke: var(--warning); }
.pack-dial.critical .pd-val { stroke: var(--critical); }
.pd-mid {
  position: absolute; inset: 0; display: grid; place-content: center;
  text-align: center; pointer-events: none;
}
.pd-v { font: 700 20px var(--sans); color: var(--ink); letter-spacing: -.02em; }
.pd-l { font-size: 9px; text-transform: uppercase; letter-spacing: .09em; color: var(--muted); }
.pd-sub { font-size: 9.5px; color: var(--faint); margin-top: 2px; }
.pack-side { flex: 1; min-width: 170px; display: flex; flex-direction: column; gap: 10px; }
.pack-flow { display: flex; align-items: center; gap: 10px; }
.pf-num { font: 700 16px var(--sans); color: var(--ink); font-variant-numeric: tabular-nums; }
.pf-num i { font: 600 10px var(--sans); font-style: normal; color: var(--muted); margin-left: 2px; }
.pf-dots { display: flex; gap: 4px; flex: 1; justify-content: center; }
.pf-dots i { width: 5px; height: 5px; border-radius: 50%; background: var(--surface-3); }
.pack-live.draining .pf-dots i { background: var(--ink-2); animation: packFlow 1.2s linear infinite; }
.pack-live.charging .pf-dots i { background: var(--good); animation: packFlow 1.2s linear infinite reverse; }
.pack-live .pf-dots i:nth-child(2) { animation-delay: -.4s; }
.pack-live .pf-dots i:nth-child(3) { animation-delay: -.8s; }
@keyframes packFlow { 0% { opacity: .2; transform: translateX(-6px); } 50% { opacity: 1; } 100% { opacity: .2; transform: translateX(6px); } }
.pack-kw { font: 700 22px var(--sans); color: var(--ink); letter-spacing: -.03em; display: flex; align-items: baseline; gap: 8px; }
.pack-kw.out { color: var(--ink); }
.pack-kw.in { color: var(--good-ink, var(--good)); }
.pk-l { font: 500 10.5px var(--sans); color: var(--muted); letter-spacing: 0; }
.pack-balance { display: flex; flex-direction: column; gap: 4px; }
.pb-head { display: flex; justify-content: space-between; font-size: 10px;
  text-transform: uppercase; letter-spacing: .08em; color: var(--muted); }
.pb-head b { font-size: 12px; letter-spacing: 0; text-transform: none; color: var(--good-ink, var(--good)); }
.pb-head b.warning { color: var(--warning); } .pb-head b.critical { color: var(--critical); }
.pb-bar { height: 5px; border-radius: 3px; background: var(--surface-3); overflow: hidden; }
.pb-bar i { display: block; height: 100%; border-radius: 3px; background: var(--good);
  transition: width .5s cubic-bezier(.32,.72,0,1); }
.pb-bar i.warning { background: var(--warning); } .pb-bar i.critical { background: var(--critical); }
.pb-foot { font-size: 10.5px; color: var(--faint); font-variant-numeric: tabular-nums; }
.witem.locked { opacity: .55; }
/* Drag-to-reorder in the customize list. */
.witem[draggable="true"] { cursor: grab; }
.witem.dragging { opacity: .45; cursor: grabbing; background: var(--surface-2); }
.witem .grip {
  color: var(--faint); font-size: 13px; line-height: 1; letter-spacing: -1px;
  cursor: grab; user-select: none; flex: none;
}
.witem:hover .grip { color: var(--muted); }
@media (prefers-reduced-motion: reduce) { .pack-live .pf-dots i { animation: none; } }

/* The can't-miss virtual-key bar — a full line above the hero, not just a chip. */
.keybar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-bottom: 14px; padding: 12px 16px;
  border: 1px solid color-mix(in srgb, var(--warning) 45%, transparent);
  background: color-mix(in srgb, var(--warning) 9%, var(--surface));
  border-radius: var(--r3); font-size: 13px; color: var(--ink-2);
}
.keybar svg { width: 20px; height: 20px; color: var(--warning); flex: none; }
.keybar > div { flex: 1; min-width: 220px; }
.keybar b { color: var(--ink); }

/* The car greets you: headlights double-blink once when a screen first shows
   it (`.hello`, set by heroLightsClass), and blink twice more every time you
   ask (`.lights`: a hover with a pointer, a tap on a touch screen). Nothing
   plays without one of the two classes, so a re-render never replays the
   greeting. Positions fit the compositor's front-3/4 view. */
.car-wrap { position: relative; display: inline-block; line-height: 0; }
.car-wrap .beam {
  position: absolute; left: 3%; top: 50%; width: 38%; height: 18%;
  background:
    radial-gradient(ellipse 30% 50% at 16% 55%, rgba(255,255,235,.95), rgba(255,250,215,.3) 50%, transparent 75%),
    radial-gradient(ellipse 26% 44% at 46% 62%, rgba(255,255,235,.8), transparent 65%);
  filter: blur(3px); opacity: 0; pointer-events: none; mix-blend-mode: screen;
}
.car-wrap.hello .beam { animation: headlightHello 1.7s ease-out .6s 1; }
/* Asking for the lights: two flashes over a second. */
.car-wrap.lights .beam { animation: headlightAsk 1s ease-out 1; }
@keyframes headlightAsk {
  0%, 100% { opacity: 0; }
  10% { opacity: 1; } 35% { opacity: .05; }
  60% { opacity: 1; } 90% { opacity: 0; }
}
.car-wrap.lights .car-shot { filter: brightness(1.03) drop-shadow(0 8px 22px color-mix(in srgb, var(--accent) 22%, transparent)); }
.car-wrap.car-hit .car-shot {
  filter: brightness(1.03) drop-shadow(0 8px 22px color-mix(in srgb, var(--accent) 22%, transparent));
}
.car-wrap .car-shot { transition: filter .25s; }
@keyframes headlightHello {
  0%, 100% { opacity: 0; }
  20% { opacity: 1; } 40% { opacity: .15; } 60% { opacity: .9; } 85% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .car-wrap .beam, .car-wrap.hello .beam, .car-wrap.lights .beam { animation: none; } }

/* "You're not being told about…" — the switched-off rules, on the Alerts tab. */
.offrules {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 14px; margin-bottom: 12px;
  background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r3);
}
.offrules.open { display: block; }
.or-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.or-head b { color: var(--ink); font-size: 13px; }
.or-head .btn { margin-left: auto; }
.or-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 6px; }
.or-item {
  display: flex; align-items: center; gap: 8px; text-align: left;
  padding: 8px 10px; border-radius: var(--r2); cursor: pointer;
  background: var(--surface-2); border: 1px solid transparent;
  font: 500 12.5px var(--sans); color: var(--ink-2);
}
.or-item:hover { border-color: var(--accent); color: var(--ink); }
.or-item .or-plus {
  width: 18px; height: 18px; border-radius: 50%; flex: none;
  display: grid; place-items: center; background: var(--accent); color: #fff;
  font-weight: 700; font-size: 13px; line-height: 1;
}
.or-item .muted { margin-left: auto; }
.or-foot { margin-top: 10px; }

/* Graphs: every field owns a colour, in its chip, its card and the combined view. */
.chip.gchip { display: inline-flex; align-items: center; gap: 6px; }
.chip.gchip .sw { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.chart-card h3 .sw {
  display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  margin-right: 7px; vertical-align: baseline;
}
.chart-card { position: relative; overflow: hidden; }
/* A hairline of the field's own colour along the top edge of its card. */
.chart-card[style*="--gc"]::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 2px;
  background: var(--gc); opacity: .8;
}
.chart-wrap.tall { height: 380px; }
.combined-legend {
  display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 14px;
  padding-top: 12px; border-top: 1px solid var(--hairline);
}
.cl-item { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; }
.cl-item .sw { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.cl-item b { color: var(--ink); font-weight: 600; }
.cl-item .muted { font-variant-numeric: tabular-nums; }

/* Ask/confirm dialogs — the app's replacement for prompt() and confirm(). */
.ask-card h1 { letter-spacing: -.02em; }
.ask-card input {
  height: 42px; padding: 0 12px; font-size: 14px;
  border-radius: var(--r2); border: 1px solid var(--hairline);
  background: var(--surface-2); color: var(--ink); width: 100%;
}
.ask-card input:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: transparent; }
/* A destructive confirm should look destructive, not just say so. */
.ask-card .btn.danger {
  background: var(--critical); border-color: var(--critical); color: #fff; font-weight: 600;
}
.ask-card .btn.danger:hover { filter: brightness(1.06); }

/* The variable picker under a notification action. */
.notify-vars { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: -2px 0 10px 2px; }
.notify-vars select { max-width: 260px; font-size: 12px; padding: 5px 8px; }

/* Cabin v2: the level bars ARE the buttons — one tap to any level. */
.seat-tile.v2 {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 10px 8px; border-radius: 12px; background: var(--surface);
  border: 1px solid var(--hairline);
}
.seat-tile.v2 svg { width: 22px; height: 22px; color: var(--ink-2); }
.lvl-row { display: flex; gap: 4px; align-items: flex-end; height: 16px; }
.lvl-btn {
  background: none; border: 0; padding: 2px 3px; cursor: pointer;
  display: flex; align-items: flex-end;
}
.lvl-btn i { display: block; width: 5px; border-radius: 2px; background: var(--surface-3); }
/* Even at rest the rows show their colour, so you always know which is which:
   warm wash = heating, cool wash = ventilation. */
.lvl-row.heat .lvl-btn i { background: color-mix(in srgb, var(--warning) 26%, var(--surface-3)); }
.lvl-row.vent .lvl-btn i { background: color-mix(in srgb, var(--accent) 30%, var(--surface-3)); }
.lvl-row.heat .lvl-btn.on i { background: var(--warning); }
.lvl-row.heat .lvl-btn.on:last-child i { background: var(--critical); }
.lvl-row.vent .lvl-btn.on i { background: var(--accent); }
.lvl-btn:hover i { outline: 2px solid var(--hairline); }
.lvl-btn[disabled] { cursor: default; opacity: .5; }
.seat-tile.v2 .lbl {
  font-size: 9px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}

/* The per-car share panel under the hero. */
.share-rows { display: flex; gap: 6px; flex-wrap: wrap; width: 100%; align-items: center; }
.share-rows .badge { max-width: 100%; }
.share-rows .mono { font-size: 11px; }

/* Admin: the month's Fleet API spend, person by person. */
.usage-rows { display: flex; flex-direction: column; gap: 5px; margin-bottom: 10px; font-size: 12.5px; }
.usage-row { display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap; }
.usage-row > span:first-child { flex: 1; min-width: 130px; color: var(--ink2); }
.usage-row b { font-variant-numeric: tabular-nums; }
.usage-row.total { border-top: 1px solid var(--hairline); padding-top: 7px; margin-top: 3px; }
.usage-row.total > span:first-child { color: var(--ink); font-weight: 600; }

/* Value readout that follows the cursor across every graph. */
.g-readout {
  position: absolute; top: 10px; right: 12px;
  font: 600 15px/1 var(--sans); color: var(--ink);
  letter-spacing: -.02em; font-variant-numeric: tabular-nums;
  pointer-events: none; opacity: 0; transition: opacity .12s;
  /* A frosted chip, so the readout stays legible when the line runs beneath it. */
  background: var(--veil); border-radius: 8px; padding: 6px 9px;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
.g-readout.show { opacity: 1; }
.g-readout .t { display: block; font-size: 9.5px; font-weight: 600; color: var(--faint);
  text-transform: uppercase; letter-spacing: .1em; margin-top: 3px; text-align: right; }

/* ==========================================================================
   Motion
   Everything here is decoration: the whole block is disabled under
   prefers-reduced-motion, and nothing depends on it to be usable.
   Durations stay under ~300ms — anything slower starts to feel like lag.
   ========================================================================== */

@keyframes cardIn { from { opacity: 0; transform: translateY(10px) scale(.995); } }
@keyframes fadeIn { from { opacity: 0; } }
@keyframes shimmer { to { background-position: 200% 0; } }
@keyframes flashVal {
  0% { color: var(--accent); transform: scale(1.04); }
  100% { color: var(--ink); transform: scale(1); }
}
@keyframes pulseDot {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--good) 55%, transparent); }
  70% { box-shadow: 0 0 0 6px transparent; }
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Cards arrive in sequence rather than all at once. --i is set per card in JS. */
.grid > .card, .settings-grid > .card {
  animation: cardIn .3s cubic-bezier(.32,.72,0,1) backwards;
  animation-delay: calc(var(--i, 0) * 28ms);
}
.hero { animation: cardIn .32s cubic-bezier(.32,.72,0,1) backwards; }
.view > .feed .feed-item {
  animation: fadeIn .24s ease backwards;
  animation-delay: calc(var(--i, 0) * 12ms);
}

/* A value that just changed gets a brief accent pulse. */
.flash-val { animation: flashVal .7s cubic-bezier(.32,.72,0,1); }
/* Accent and HUD surfaces cannot pulse to the accent/ink pair used on paper:
   orange-on-orange disappears and ink is the wrong resting colour. Keep the
   figure white and use the same 700ms scale with a brief white bloom instead. */
@keyframes flashValOnLive {
  0% { color: #fff; transform: scale(1.06); text-shadow: 0 0 9px rgba(255,255,255,.9); }
  100% { color: #fff; transform: scale(1); text-shadow: 0 0 0 transparent; }
}
.drive-strip .flash-val, .drive-hud .flash-val {
  animation: flashValOnLive .7s cubic-bezier(.32,.72,0,1);
}

/* The live indicator breathes only while data is actually flowing. */
.pill.ok.live .dot { animation: pulseDot 2.4s ease-out infinite; }

/* Skeletons while a view loads — far better than the word "Loading". */
.skel {
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
  background-size: 200% 100%;
  animation: shimmer 1.4s linear infinite;
  border-radius: var(--r1);
}
.skel-line { height: 11px; margin: 9px 0; }
.skel-card { height: 168px; border-radius: var(--r3); }
.skel-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(304px, 1fr)); gap: 12px; }

/* Buttons and tiles get a little physical feedback. */
.btn, .chip, .cmd, .toggle-tile, .vpill, .nav-item, .seg button, .levels button {
  transition: background .15s ease, border-color .15s ease, color .15s ease,
              transform .12s cubic-bezier(.32,.72,0,1), box-shadow .15s ease, opacity .15s;
}
.cmd:active:not(:disabled), .toggle-tile:active:not(:disabled),
.levels button:active, .chip:active { transform: scale(.975); }
.card { transition: box-shadow .2s ease, transform .2s cubic-bezier(.32,.72,0,1); }
.grid > .card:hover { transform: translateY(-1px); }

/* A command in flight shows a spinner instead of going dead. */
.cmd.busy::after, .toggle-tile.busy::after {
  content: ''; width: 11px; height: 11px; margin-left: auto; flex: none;
  border: 1.5px solid currentColor; border-right-color: transparent;
  border-radius: 50%; animation: spin .7s linear infinite; opacity: .7;
}

/* Theme changes cross-fade rather than snapping. */
body, .card, .sidebar, .topbar, input, select, .btn, .badge, .tire, .toggle-tile {
  transition: background-color .22s ease, color .22s ease, border-color .22s ease;
}

/* The view itself fades in when switching tabs. */
.view.swap { animation: fadeIn .2s ease; }

/* Gauges sweep their needle-arc in as their card lands — the instrument-cluster
   move. Interactive gauges are exempt: they re-render on every drag tick, and a
   replaying sweep would strobe under the finger. pathLength="1" on the arc makes
   the dash math trivial. */
/* Only the first paint of a view sweeps. Streaming rebuilds these cards every
   few seconds; replaying the sweep each time reads as a page that never settles. */
#view.swap .gauge:not(.interactive) .g-val {
  stroke-dasharray: 1; stroke-dashoffset: 0;
  animation: sweepIn .6s cubic-bezier(.32,.72,0,1) backwards;
  animation-delay: calc(var(--i, 0) * 28ms + .1s);
}
/* The dash pattern lives only inside the keyframes: once the draw-in ends the
   resting style has no dasharray at all — Safari renders pathLength+dash
   combinations as broken segments, so the line must never keep one. */
@keyframes sweepIn {
  from { stroke-dasharray: 1; stroke-dashoffset: 1; }
  to { stroke-dasharray: 1; stroke-dashoffset: 0; }
}

/* The battery-health trend draws itself, then its points land on the line. */
.trend-plot .line {
  stroke-dasharray: none;
  animation: sweepIn .7s ease-out backwards;
  animation-delay: .15s;
}
.trend-pt, .trend-lastlab {
  animation: dotPop .25s cubic-bezier(.34,1.4,.4,1) backwards;
  animation-delay: .55s;
}
@keyframes dotPop { from { opacity: 0; transform: scale(.4); } }

/* Login card lands with a little spring, its contents follow in sequence, the
   mark pops and then flickers once like a struck arc. Toggling to sign-up mode
   un-hides the name field, which replays its own entrance — free choreography. */
.overlay:not([hidden]) .login-card { animation: loginIn .4s cubic-bezier(.3,1.25,.4,1) backwards; }
.overlay:not([hidden]) .login-card > * { animation: loginRise .4s cubic-bezier(.32,.72,0,1) backwards; }
.overlay:not([hidden]) .login-card > :nth-child(2) { animation-delay: .08s; }
.overlay:not([hidden]) .login-card > :nth-child(3) { animation-delay: .12s; }
.overlay:not([hidden]) .login-card > :nth-child(4) { animation-delay: .16s; }
.overlay:not([hidden]) .login-card > :nth-child(5) { animation-delay: .20s; }
.overlay:not([hidden]) .login-card > :nth-child(6) { animation-delay: .24s; }
.overlay:not([hidden]) .login-card > :nth-child(n+7) { animation-delay: .28s; }
.overlay:not([hidden]) .login-mark { animation: dotPop .35s cubic-bezier(.34,1.4,.4,1) .12s backwards; }
.overlay:not([hidden]) .login-mark svg { animation: spark .6s ease .55s both; }
@keyframes loginIn { from { opacity: 0; transform: translateY(14px) scale(.97); } }
@keyframes loginRise { from { opacity: 0; transform: translateY(9px); } }
@keyframes spark {
  0%, 100% { opacity: 1; }
  25% { opacity: .35; }
  38% { opacity: 1; }
  52% { opacity: .55; }
  65% { opacity: 1; }
}

/* The active nav item acknowledges the tap. */
.nav-item.active { animation: navPop .22s cubic-bezier(.32,.72,0,1); }
@keyframes navPop { 50% { transform: scale(.965); } }

/* The bolt jolts and arcs when you tease it — same move as the landing page.
   The overlay-qualified selector outranks the entrance spark, so hovering
   replays the flicker any time. */
.brand:hover .mark svg, .login-mark:hover .glyph,
.overlay:not([hidden]) .login-mark:hover .glyph {
  animation: jolt .4s ease, spark .6s ease;
}
@keyframes jolt { 25% { transform: rotate(-7deg); } 60% { transform: rotate(4deg); } }

@media (prefers-reduced-motion: reduce) {
  .grid > .card, .settings-grid > .card, .hero, .view > .feed .feed-item,
  .view.swap, .flash-val, .pill.ok.live .dot, .skel,
  .gauge:not(.interactive) .g-val, .trend-plot .line, .trend-pt, .trend-lastlab,
  .login-card, .login-card > *, .login-mark, .login-mark svg,
  .login-error:not([hidden]), .overlay::after,
  .brand:hover .mark svg, .login-mark:hover .glyph,
  .nav-item.active { animation: none !important; }
  .grid > .card:hover { transform: none; }
}

/* Draggable charge-limit marker, set directly on the battery bar. */
/* Media player. Transport buttons are round so the group reads as a player rather
   than another row of settings tiles. */
.bar.slim { height: 4px; border-radius: 2px; }
/* The default fill is the battery green; volume and track position are neither. */
.media-time .bar .fill, .media-vol .bar .fill { background: var(--accent); }
.media-time { display: flex; align-items: center; gap: 10px; }
.media-time .bar { flex: 1; }
.media-time span { color: var(--faint); font: 500 10.5px/1 var(--sans); font-variant-numeric: tabular-nums; }
.media-transport { display: flex; align-items: center; justify-content: center; gap: 8px; }
.media-btn {
  width: 34px; height: 34px; flex: none; display: grid; place-items: center;
  border: 1px solid var(--hairline-2); border-radius: 50%;
  background: var(--surface-2); color: var(--ink); cursor: pointer;
  transition: background .13s ease, border-color .13s ease, transform .13s ease;
}
.media-btn svg { width: 15px; height: 15px; }
.media-btn.big { width: 44px; height: 44px; background: var(--ink); color: var(--paper); border-color: var(--ink); }
.media-btn.big svg { width: 18px; height: 18px; }
.media-btn:hover:not(:disabled) { transform: translateY(-1px); border-color: var(--hairline); }
.media-btn:disabled { opacity: .4; cursor: not-allowed; }
.media-vol { display: flex; align-items: center; gap: 9px; }
.media-vol .bar { flex: 1; }
.media-vol .media-btn { width: 28px; height: 28px; }
.media-vol .media-btn svg { width: 13px; height: 13px; }
.media-vol-num {
  min-width: 34px; text-align: right; color: var(--faint);
  font: 600 10.5px/1 var(--sans); font-variant-numeric: tabular-nums;
}
@media (pointer: coarse) {
  .media-btn { width: 44px; height: 44px; }
  .media-btn.big { width: 50px; height: 50px; }
  .media-vol .media-btn { width: 44px; height: 44px; }
}

.bar.settable { height: 12px; border-radius: 6px; cursor: ew-resize; touch-action: none; }
.bar.settable .fill { border-radius: 6px; }
.bar .handle {
  position: absolute; top: 50%; width: 15px; height: 22px;
  transform: translate(-50%, -50%);
  border-radius: 5px; background: var(--surface);
  border: 2px solid var(--ink); cursor: grab;
  box-shadow: var(--lift); transition: border-color .15s, transform .12s;
}
.bar .handle:active { cursor: grabbing; transform: translate(-50%, -50%) scale(1.08); }
.bar.dirty .handle { border-color: var(--accent); }
.bar .handle::after {
  content: attr(data-v); position: absolute; bottom: 26px; left: 50%;
  transform: translateX(-50%); white-space: nowrap;
  font: 600 10.5px/1 var(--sans); color: var(--ink);
  background: var(--surface); border: 1px solid var(--hairline);
  padding: 4px 6px; border-radius: 5px; box-shadow: var(--lift);
}
.bar-row { display: flex; align-items: center; gap: 10px; }
.bar-row .bar { flex: 1; }
.limit-commit { display: flex; gap: 6px; align-items: center; }

/* Compact toggles — the climate card was far too tall with full-size tiles. */
.tiles-compact { display: flex; flex-wrap: wrap; gap: 5px; }
.tiles-compact .toggle-tile {
  flex-direction: row; align-items: center; gap: 7px;
  padding: 7px 11px; border-radius: 999px; font-size: 12px; font-weight: 550;
}
.tiles-compact .toggle-tile .st { display: none; }
.tiles-compact .toggle-tile::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: var(--faint); flex: none;
}
.tiles-compact .toggle-tile.on::before { background: var(--paper); }

.seat-row { display: flex; align-items: center; gap: 9px 16px; flex-wrap: wrap; font-size: 11.5px; }
.seat { display: flex; align-items: center; gap: 7px; }
.seat-row .lbl {
  color: var(--faint); font-weight: 600; text-transform: uppercase;
  letter-spacing: .09em; font-size: 9.5px; white-space: nowrap;
}
/* Only shown when the car actually reports rear seats, so it never labels an empty row. */
.seat-sub {
  margin: 12px 0 6px; padding-top: 10px; border-top: 1px solid var(--hairline-2);
  color: var(--faint); font: 600 9.5px/1 var(--sans);
  text-transform: uppercase; letter-spacing: .09em;
}
.temp-head { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.temp-head .pair { display: flex; gap: 16px; }
/* Inside on the left, outside on the right at the same size (1.7.25); the airflow ribbons sit between. */
.temp-head .stat.right { margin-left: auto; align-items: flex-end; text-align: right; }
/* The dial and, beside it, the readouts that fill the corner it used to leave empty.
   On a phone the grid drops beneath the dial. */
.climate-body { display: flex; align-items: center; gap: 16px; margin-top: 4px; }
/* The dial's own "set to" caption says it; the stepper's hint would only widen the column. */
.climate-body .stepper .gauge-hint { display: none; }
/* Nothing beside the dial (climate off, nothing to report): the dial takes the middle. */
.climate-body:has(> .gauge-wrap:only-child) { justify-content: center; }
.climate-readout { display: grid; grid-template-columns: repeat(auto-fit, minmax(84px, 1fr)); gap: 12px 14px; flex: 1; min-width: 0; }
.climate-readout .stat .v.small { font-size: 16px; }
.climate-readout .stat .l { white-space: nowrap; }
/* Anti Mold on the climate card: glyph well, name + status, Run/Stop, and the per-car switch. */
.am-row { display: flex; align-items: center; gap: 12px; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--hairline-2); }
.am-row .am-icon { display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 10px; background: var(--accent-wash); color: var(--accent); flex: none; }
.am-row .am-icon.on { background: var(--accent); color: #fff; }
.am-row .am-icon svg { width: 20px; height: 20px; }
.am-row .am-text { flex: 1; min-width: 0; }
.am-row .am-text strong { display: block; font-size: 14px; }
.am-row .am-text .small { overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.am-row .switch { gap: 0; }

/* Shared date-range control used by Charges and Drives. */
.range-bar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 16px; }
.range-bar .seg button { white-space: nowrap; }

/* Summary figures above a list. */
.summary-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 2px; margin-bottom: 14px;
  border: 1px solid var(--hairline-2); border-radius: var(--r3);
  background: var(--hairline-2); overflow: hidden;
}
.summary-row .cell { background: var(--surface); padding: 15px 17px; }
.summary-row .cell .v {
  font: 600 24px/1 var(--sans); color: var(--ink);
  letter-spacing: -.035em; font-variant-numeric: tabular-nums;
}
.summary-row .cell .l {
  font: 600 10px/1 var(--sans); color: var(--faint); margin-top: 6px;
  text-transform: uppercase; letter-spacing: .1em;
}

/* Expandable session / drive rows. */
.log-row {
  display: grid; grid-template-columns: 92px 1fr auto; gap: 16px; align-items: center;
  padding: 14px 4px; border-top: 1px solid var(--hairline-2); cursor: pointer;
}
.log-row:first-of-type { border-top: 0; }
.log-row:hover { background: var(--surface-2); }
.log-row .date { font-variant-numeric: tabular-nums; }
.log-row .date .d { color: var(--ink); font-weight: 600; font-size: 13px; }
.log-row .date .t { color: var(--faint); font-size: 11px; margin-top: 2px; }
.log-row .mid .title { color: var(--ink); font-weight: 550; font-size: 13.5px; letter-spacing: -.01em; }
/* Recessive so the two place names read as the content and the arrow as punctuation. */
.log-row .mid .title .arrow { color: var(--faint); font-weight: 400; margin: 0 1px; }
.log-row .mid .sub { color: var(--muted); font-size: 11.5px; margin-top: 3px; }
.log-row .mid .sub .sc-tag { color: #e31937; font-weight: 600; }
.log-row .right { text-align: right; font-variant-numeric: tabular-nums; }
.log-row .right .big { color: var(--ink); font-weight: 600; font-size: 15px; letter-spacing: -.02em; }
.log-row .right .small2 { color: var(--faint); font-size: 11px; margin-top: 3px; }
.log-detail { padding: 4px 4px 18px; border-top: 1px dashed var(--hairline); }
.log-detail .kv { max-width: 520px; }

.site-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 7px; vertical-align: 1px; }

/* Command palette — one search across the whole app. */
.palette-overlay {
  position: fixed; inset: 0; z-index: 150;
  background: color-mix(in srgb, var(--paper) 72%, transparent);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  display: flex; justify-content: center; padding: 12vh 20px 20px;
}
.palette-overlay[hidden] { display: none; }
.palette {
  width: 620px; max-width: 100%; max-height: 62vh;
  background: var(--surface); border: 1px solid var(--hairline);
  border-radius: var(--r3); box-shadow: var(--lift-3);
  display: flex; flex-direction: column; overflow: hidden;
  animation: cardIn .2s cubic-bezier(.32,.72,0,1);
}
.palette input {
  border: 0; border-bottom: 1px solid var(--hairline-2); border-radius: 0;
  padding: 17px 20px; font-size: 15.5px; background: transparent;
}
.palette input:focus { box-shadow: none; border-color: var(--hairline-2); }
.palette .results { overflow-y: auto; padding: 6px; }
.palette .grp {
  font: 600 9.5px/1 var(--sans); letter-spacing: .12em; text-transform: uppercase;
  color: var(--faint); padding: 12px 14px 7px;
}
.palette .hit {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 14px; border-radius: var(--r2); cursor: pointer;
}
.palette .hit:hover, .palette .hit.sel { background: var(--surface-2); }
.palette .hit .t { color: var(--ink); font-size: 13.5px; font-weight: 500; flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.palette .hit .s { color: var(--faint); font-size: 11.5px; white-space: nowrap; }
.palette .foot {
  border-top: 1px solid var(--hairline-2); padding: 9px 16px;
  font-size: 11px; color: var(--faint); display: flex; gap: 14px;
}
.palette kbd {
  font: 600 10px/1 var(--mono); background: var(--surface-2);
  border: 1px solid var(--hairline); border-radius: 4px; padding: 3px 5px;
}
.search-btn { display: flex; align-items: center; gap: 8px; color: var(--muted); }
.search-btn:hover { color: var(--ink-2); }

/* ==========================================================================
   Automations — a sentence-shaped builder: WHEN … IF … THEN …
   ========================================================================== */
.auto-card {
  background: var(--surface); border: 1px solid var(--hairline-2);
  border-radius: var(--r3); padding: 16px 18px; margin-bottom: 10px;
  box-shadow: var(--lift);
}
.auto-head { display: flex; align-items: center; gap: 11px; flex-wrap: wrap; }
.auto-head .nm { font-weight: 620; color: var(--ink); font-size: 14.5px; letter-spacing: -.015em; flex: 1; }
.auto-sentence { color: var(--ink-2); font-size: 13px; line-height: 1.7; margin-top: 8px; }
.auto-sentence .kwd {
  font: 600 9.5px/1 var(--sans); letter-spacing: .12em; text-transform: uppercase;
  color: var(--faint); margin-right: 7px;
}
.auto-sentence .val {
  background: var(--surface-2); border-radius: 5px; padding: 2px 7px;
  color: var(--ink); font-weight: 550; font-variant-numeric: tabular-nums;
}
.auto-meta { display: flex; gap: 14px; font-size: 11px; color: var(--faint); margin-top: 10px; flex-wrap: wrap; }
.auto-off { opacity: .5; }

.builder { display: flex; flex-direction: column; gap: 14px; }
.builder .step-box {
  border: 1px solid var(--hairline-2); border-radius: var(--r2);
  padding: 13px 15px; background: var(--surface-2);
}
.builder .step-box > .lbl {
  font: 600 9.5px/1 var(--sans); letter-spacing: .12em; text-transform: uppercase;
  color: var(--faint); margin-bottom: 10px; display: block;
}
.builder .row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.builder select, .builder input { font-size: 13px; padding: 7px 10px; }
.action-row {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 7px;
  padding: 9px 11px; background: var(--surface); border: 1px solid var(--hairline-2);
  border-radius: var(--r1);
}
/* A select won't shrink below its longest option unless told to, which used to
   push the value box and its unit past the edge of the row. */
.action-row select { min-width: 0; max-width: 100%; }
.action-row select[data-k="field"] { flex: 1 1 200px; }
.action-row input[data-k="value"] { width: 90px; flex: 0 0 auto; }
.action-row .muted.small, .action-row [data-cunit] { white-space: nowrap; }
.action-row .nm { flex: 1; font-size: 13px; color: var(--ink); }
.day-pick { display: inline-flex; gap: 3px; }
.day-pick button {
  width: 30px; height: 28px; border-radius: 7px; cursor: pointer;
  border: 1px solid var(--hairline); background: var(--surface);
  color: var(--muted); font: 600 10.5px/1 var(--sans);
}
.day-pick button.on { background: var(--ink); border-color: var(--ink); color: var(--paper); }

/* Admin panel */
.user-row {
  display: grid; grid-template-columns: 1fr auto auto; gap: 12px; align-items: center;
  padding: 11px 2px; border-top: 1px solid var(--hairline-2);
}
.user-row:first-of-type { border-top: 0; }
.user-row .who .e { color: var(--ink); font-weight: 550; font-size: 13.5px; }
.user-row .who .n { color: var(--faint); font-size: 11.5px; margin-top: 2px; }
.role-badge { font-size: 10.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.access-grid { display: grid; gap: 8px; }
.access-row {
  display: flex; gap: 9px; align-items: center; flex-wrap: wrap;
  padding: 10px 12px; background: var(--surface-2); border-radius: var(--r2);
}

/* ==========================================================================
   Phone navigation
   Nine tabs will not fit a bottom bar at 375px — the last simply runs off the
   screen. Four stay put and the rest move into a sheet behind "More".
   ========================================================================== */
.more-btn { display: none; }

@media (max-width: 940px) {
  .nav-item.secondary { display: none; }
  .more-btn { display: flex; }
}

.sheet-overlay {
  position: fixed; inset: 0; z-index: 140;
  /* A dark scrim, not a paper wash: the page behind must read as dimmed and
     present — a 55% paper veil over a paper page erased it into ghosts. */
  background: rgba(10, 10, 9, .34);
  backdrop-filter: blur(2.5px); -webkit-backdrop-filter: blur(2.5px);
  display: flex; align-items: flex-end;
}
.sheet-overlay[hidden] { display: none; }
.sheet {
  width: 100%; background: var(--surface);
  border-top-left-radius: var(--r4); border-top-right-radius: var(--r4);
  border-top: 1px solid var(--hairline);
  padding: 10px 14px calc(18px + env(safe-area-inset-bottom, 0px));
  box-shadow: var(--lift-3);
  animation: sheetUp .26s cubic-bezier(.32,.72,0,1);
}
@keyframes sheetUp { from { transform: translateY(100%); } }
.sheet-grip {
  width: 38px; height: 4px; border-radius: 2px; background: var(--surface-3);
  margin: 2px auto 14px;
}
.sheet-items { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 14px; }
.sheet-item {
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 15px 8px; border-radius: var(--r2);
  border: 1px solid var(--hairline-2); background: var(--surface-2);
  color: var(--ink-2); font: 550 12px/1 var(--sans); cursor: pointer; position: relative;
}
.sheet-item svg { width: 20px; height: 20px; }
.sheet-item.active { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.sheet-item .nav-badge { position: absolute; top: 7px; right: 10px; }

@media (max-width: 940px) {
  /* Give the fixed bar breathing room on notched phones. */
  .sidebar { height: calc(58px + env(safe-area-inset-bottom, 0px)); padding-bottom: env(safe-area-inset-bottom, 0px); }
  /* Room for the bottom bar and the Watty button above it, so the last row of
     every page can scroll clear of both. */
  .view { padding-bottom: calc(150px + env(safe-area-inset-bottom, 0px)); }
}

/* Touch sizing. Segmented controls and level pickers are 26px on a desktop, which is
   fine for a mouse and too small for a finger — Apple and Google both put the floor
   around 44px, so grow them wherever the pointer is coarse. */
@media (pointer: coarse) {
  .seg button { min-height: 44px; padding: 0 15px; }
  .levels button { width: 44px; height: 44px; }
  .witem .mv { width: 44px; height: 44px; }
  .chip { min-height: 44px; padding: 0 15px; }
  .btn.sm { min-height: 44px; padding: 0 13px; }
  .stepper button { width: 44px; height: 44px; }
  .day-pick button { width: 44px; height: 44px; }
  .toggle-tile { min-height: 44px; }
  .copyrow .btn { min-height: 44px; }
  .tbl td, .tbl th { padding-top: 12px; padding-bottom: 12px; }
  .feed-item { padding-top: 17px; padding-bottom: 17px; }
}

/* Nothing should ever push the page sideways on a phone. */
@media (max-width: 940px) {
  html, body { overflow-x: hidden; max-width: 100%; }
  .view, .topbar, .card, .hero { max-width: 100%; }
  .summary-row { grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); }
  .log-row { grid-template-columns: 72px 1fr auto; gap: 10px; }
  .cmd-grid { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); }
  .range-bar .seg { overflow-x: auto; max-width: 100%; }
  .copyrow { font-size: 10.5px; }
  .kv { font-size: 12.5px; }
}

@media (max-width: 940px) {
  /* Inline links inside cards need a finger-sized hit area of their own. */
  .card a, .feed-detail a, .small a { display: inline-block; padding: 6px 0; }
  /* The graph range control is wider than a small phone — let it scroll. */
  .controls .seg { max-width: 100%; overflow-x: auto; scrollbar-width: none; }
  .controls .seg::-webkit-scrollbar { display: none; }
  .controls .seg button { flex: 0 0 auto; }
}

/* ==========================================================================
   Drive replay — the route drawn as SVG and scrubbed with a slider.
   Drawn rather than tiled: no external map requests, works offline, and the
   path is the point rather than the streets.
   ========================================================================== */
.replay { display: flex; flex-direction: column; gap: 12px; }
.replay-map {
  position: relative; width: 100%; aspect-ratio: 16 / 9;
  background: var(--surface-2); border-radius: var(--r2); overflow: hidden;
}
.replay-map svg { width: 100%; height: 100%; display: block; }
.replay-map .route { fill: none; stroke: var(--hairline); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.replay-map .travelled { fill: none; stroke: var(--accent); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.replay-map .endpt { fill: var(--surface); stroke: var(--muted); stroke-width: 2; }
.replay-map .endpt.start { stroke: var(--good); }
.replay-map .endpt.end { stroke: var(--critical); }
.replay-car {
  fill: var(--accent); stroke: var(--surface); stroke-width: 2.5;
  transition: transform .12s linear;
}
.replay-readout {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(78px, 1fr));
  gap: 2px; background: var(--hairline-2);
  border: 1px solid var(--hairline-2); border-radius: var(--r2); overflow: hidden;
}
.replay-readout .cell { background: var(--surface); padding: 10px 12px; }
.replay-readout .v {
  font: 600 17px/1 var(--sans); color: var(--ink);
  letter-spacing: -.03em; font-variant-numeric: tabular-nums;
}
.replay-readout .l {
  font: 600 9px/1 var(--sans); color: var(--faint); margin-top: 5px;
  text-transform: uppercase; letter-spacing: .1em;
}
.replay-controls { display: flex; align-items: center; gap: 11px; }
.replay-controls input[type=range] {
  flex: 1; -webkit-appearance: none; appearance: none;
  height: 5px; padding: 0; border: 0; border-radius: 3px;
  background: var(--surface-3); cursor: pointer;
}
.replay-controls input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 17px; height: 17px; border-radius: 50%;
  background: var(--ink); border: 2px solid var(--surface); box-shadow: var(--lift);
}
.replay-controls .tstamp {
  font-variant-numeric: tabular-nums; font-size: 12px; color: var(--muted); min-width: 64px;
}
.replay-empty { padding: 34px 16px; text-align: center; color: var(--faint); font-size: 12.5px; }

/* Command buttons now carry an icon. */
.cmd svg { width: 17px; height: 17px; flex: none; opacity: .75; }
.cmd .lbl { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.cmd:hover:not(:disabled) svg { opacity: 1; }
.cmd.danger svg { opacity: .85; }

/* ==========================================================================
   Driving mode — only rendered while the car is out of Park.
   ========================================================================== */
@keyframes pulseRing {
  0%   { r: 6; opacity: .9; }
  70%  { r: 17; opacity: 0; }
  100% { r: 17; opacity: 0; }
}
@keyframes dashFlow { to { stroke-dashoffset: -24; } }
@keyframes liveBlink { 0%,100% { opacity: 1; } 50% { opacity: .35; } }

.driving-card { border-color: var(--accent-ring); }
.driving-badge {
  display: inline-flex; align-items: center; gap: 7px;
  color: var(--accent); font: 600 10.5px/1 var(--sans);
  letter-spacing: .12em; text-transform: uppercase;
}
.driving-badge::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent); animation: liveBlink 1.6s ease-in-out infinite;
}

.drive-hero { display: flex; align-items: center; gap: 22px; flex-wrap: wrap; }
.speed-big {
  font: 700 54px/1 var(--sans); color: var(--ink);
  letter-spacing: -.05em; font-variant-numeric: tabular-nums;
}
.speed-big small { font-size: 15px; font-weight: 600; color: var(--faint); margin-left: 5px; letter-spacing: 0; }

/* Live route map: the trail so far, the car, and a pulse that says "moving". */
.drive-map { position: relative; width: 100%; aspect-ratio: 2 / 1; border-radius: var(--r2);
  background: var(--surface-2); overflow: hidden; }
.drive-map svg { width: 100%; height: 100%; }
.drive-map .trail { fill: none; stroke: var(--accent); stroke-width: 2.5; stroke-linecap: round; opacity: .55; }
.drive-map .to-dest {
  fill: none; stroke: var(--muted); stroke-width: 2; stroke-linecap: round;
  stroke-dasharray: 6 6; animation: dashFlow 1.1s linear infinite;
}
.drive-map .pulse { fill: var(--accent); opacity: .35; animation: pulseRing 2s ease-out infinite; }
.drive-map .carmark { fill: var(--accent); stroke: var(--surface); stroke-width: 2; }
.drive-map .dest { fill: var(--surface); stroke: var(--good); stroke-width: 2.5; }

/* Progress toward the destination. */
.eta-bar { position: relative; height: 8px; border-radius: 4px; background: var(--surface-3); overflow: hidden; }
.eta-bar .done {
  position: absolute; inset: 0 auto 0 0; border-radius: 4px;
  background: linear-gradient(90deg, var(--accent-2), var(--accent));
  transition: width .8s cubic-bezier(.32,.72,0,1);
}
/* On the live card the glide loop writes this width every frame; a transition
   under it would only lag. Other bars keep theirs. */
.eta-bar .done[data-glide] { transition: none; }
/* Breathing room below, so the arrival chip that follows is not flush against it. */
.eta-row { display: flex; justify-content: space-between; font-size: 11.5px; color: var(--muted); margin: 6px 0 10px; }

.drive-stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
  gap: 2px; background: var(--hairline-2);
  border: 1px solid var(--hairline-2); border-radius: var(--r2); overflow: hidden;
}
.drive-stats .cell { background: var(--surface); padding: 11px 12px; }
.drive-stats .v { font: 600 18px/1 var(--sans); color: var(--ink); letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.drive-stats .v.regen { color: var(--good-ink); }
.drive-stats .l { font: 600 9px/1 var(--sans); color: var(--faint); margin-top: 5px; text-transform: uppercase; letter-spacing: .1em; }

.arrival-chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 13px; border-radius: var(--r2);
  background: var(--surface-2); font-size: 13px; color: var(--ink-2);
}
.arrival-chip .pct { font-weight: 650; color: var(--ink); font-variant-numeric: tabular-nums; }
.arrival-chip.low { background: color-mix(in srgb, var(--warning) 12%, var(--surface-2)); }
.arrival-chip.crit { background: color-mix(in srgb, var(--critical) 12%, var(--surface-2)); }

/* Rows that behave like buttons should look and feel like it. */
.row-item.tappable { cursor: pointer; border-radius: var(--r1); transition: background .13s; }
.row-item.tappable:hover { background: var(--surface-2); }
.row-item.tappable:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.row-item.tappable .what .t { text-decoration: underline; text-underline-offset: 2px;
  text-decoration-color: transparent; transition: text-decoration-color .13s; }
.row-item.tappable:hover .what .t { text-decoration-color: var(--hairline); }

/* ==========================================================================
   Controls
   Two tiers: the handful of things you actually reach for become large
   state-aware tiles that show what the car is doing and toggle it; everything
   else is a scannable list. Both collapse to one column on a phone.
   ========================================================================== */
.quick-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px; margin-bottom: 18px;
}
.quick {
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
  padding: 16px 17px; min-height: 106px;
  border: 1px solid var(--hairline); border-radius: var(--r3);
  background: var(--surface); color: var(--ink); cursor: pointer;
  text-align: left; box-shadow: var(--lift);
  transition: all .16s cubic-bezier(.32,.72,0,1);
}
.quick:hover:not(:disabled) { box-shadow: var(--lift-2); transform: translateY(-1px); }
.quick:active:not(:disabled) { transform: scale(.985); }
.quick:disabled { opacity: .42; cursor: not-allowed; box-shadow: none; }
.quick svg { width: 22px; height: 22px; opacity: .8; }
.quick .state { font: 600 15px/1.15 var(--sans); letter-spacing: -.02em; }
.quick .hint { font: 500 11.5px/1.2 var(--sans); color: var(--faint); margin-top: -4px; }
/* "On" states invert, so the car's actual condition is readable at a glance. */
.quick.on { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.quick.on svg { opacity: 1; }
.quick.on .hint { color: color-mix(in srgb, var(--paper) 65%, transparent); }
.quick.warn { border-color: var(--hairline); }
.quick.on[data-control="sentry"] svg { color: #f04448; }
.quick.busy { opacity: .6; pointer-events: none; }

.ctl-section { margin-bottom: 12px; }
.ctl-section > h4 {
  font: 600 10px/1 var(--sans); letter-spacing: .12em; text-transform: uppercase;
  color: var(--faint); margin: 0 0 8px 2px;
}
.ctl-list {
  border: 1px solid var(--hairline-2); border-radius: var(--r3);
  background: var(--surface); overflow: hidden; box-shadow: var(--lift);
}
.ctl-row {
  display: flex; align-items: center; gap: 13px;
  width: 100%; padding: 13px 16px; min-height: 52px;
  background: transparent; border: 0; border-top: 1px solid var(--hairline-2);
  color: var(--ink); font: 550 13.5px/1.2 var(--sans); letter-spacing: -.01em;
  text-align: left; cursor: pointer; transition: background .13s;
}
.ctl-row:first-child { border-top: 0; }
.ctl-row:hover:not(:disabled) { background: var(--surface-2); }
.ctl-row:disabled { opacity: .4; cursor: not-allowed; }
.ctl-row svg { width: 18px; height: 18px; flex: none; opacity: .65; }
.ctl-row .name { flex: 1; min-width: 0; }
.ctl-row .now {
  color: var(--muted); font-weight: 500; font-size: 12.5px;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.ctl-row .chev { color: var(--faint); font-size: 15px; line-height: 1; }
.ctl-row.danger { color: var(--critical-ink); }
.ctl-row.danger svg { opacity: .8; }
.ctl-row.busy { opacity: .55; }

@media (max-width: 940px) {
  .quick-grid { grid-template-columns: repeat(2, 1fr); gap: 8px; }
  .quick { min-height: 96px; padding: 14px; }
  .ctl-row { padding: 15px 15px; min-height: 56px; }
}
@media (max-width: 380px) {
  .quick-grid { grid-template-columns: 1fr; }
}

/* ==========================================================================
   Tile map — a grid of proxied tiles with the route drawn on top.
   No mapping library: the projection is 40 lines of arithmetic, and avoiding
   one keeps the page dependency-free and the tiles server-cached.
   ========================================================================== */
.tmap { position: relative; overflow: hidden; border-radius: var(--r2); background: var(--surface-2); }
.tmap .layer { position: absolute; inset: 0; }
.tmap img.tile {
  position: absolute; width: 256px; height: 256px;
  user-select: none; -webkit-user-drag: none;
}
/* Not stretched: the overlay shares the tiles' pixel space exactly. */
/* Not called .overlay: that class is the fullscreen login sheet, whose opaque
   background would paint straight over the tiles. */
.tmap svg.tmap-route { position: absolute; left: 0; top: 0; pointer-events: none; }
.tmap .route-line { fill: none; stroke: #2a78d6; stroke-width: 4; stroke-linecap: round; stroke-linejoin: round;
  paint-order: stroke; }
.tmap .route-casing { fill: none; stroke: #fff; stroke-width: 7; stroke-linecap: round; stroke-linejoin: round; opacity: .9; }
.tmap .route-done { fill: none; stroke: #0ca30c; stroke-width: 4; stroke-linecap: round; stroke-linejoin: round; }
.tmap .pin { stroke: #fff; stroke-width: 2.5; }
.tmap .pin.start { fill: #0ca30c; }
.tmap .pin.end { fill: #d03b3b; }
.tmap .pin.car { fill: #2a78d6; }
.tmap .pin-pulse { fill: #2a78d6; opacity: .3; animation: pulseRing 2s ease-out infinite; }
.tmap .attrib {
  position: absolute; right: 0; bottom: 0; z-index: 2;
  background: rgba(255,255,255,.72); color: #333;
  font: 500 9.5px/1 var(--sans); padding: 3px 6px; border-top-left-radius: 5px;
}
.tmap-style {
  position: absolute; top: 8px; right: 8px; z-index: 3;
  display: inline-flex; padding: 2px; gap: 1px; border-radius: 9px;
  background: rgba(255,255,255,.9); box-shadow: 0 1px 4px rgba(0,0,0,.2);
}
.tmap-style button {
  border: 0; background: transparent; color: #444;
  font: 600 10.5px/1 var(--sans); padding: 6px 10px; border-radius: 7px; cursor: pointer;
}
.tmap-style button.on { background: #111; color: #fff; }
.tmap-zoom {
  position: absolute; top: 8px; left: 8px; z-index: 3;
  display: flex; flex-direction: column; gap: 1px; border-radius: 9px; overflow: hidden;
  background: rgba(255,255,255,.9); box-shadow: 0 1px 4px rgba(0,0,0,.2);
}
.tmap-zoom button {
  width: 32px; height: 30px; border: 0; background: transparent; color: #222; cursor: pointer;
  font: 600 17px/1 var(--sans);
}
.tmap-zoom button:hover { background: rgba(0,0,0,.07); }
@media (pointer: coarse) { .tmap-zoom button { width: 40px; height: 40px; } }
.tmap-empty { display: grid; place-items: center; height: 100%; color: var(--faint); font-size: 12px; }

/* The small preview inside an expanded drive row: the whole route in red, no
   progress segment — it is a finished trip, not a replay. Height comes from the
   inline style, because the tile grid is computed against that exact pixel size. */
.tmap.mini { margin-top: 10px; }
.tmap.mini .route-line { stroke: #d0342c; stroke-width: 3.5; }
.tmap.mini .route-casing { stroke-width: 6.5; }

/* Delivery is opt-in, so say plainly that nothing is going out yet. */
.mutebar {
  display: flex; align-items: center; gap: 12px;
  margin: 2px 0 4px; padding: 11px 13px;
  border: 1px solid var(--warning-line, var(--line));
  border-left: 3px solid var(--warning-ink);
  border-radius: 10px;
  background: var(--warning-bg, rgba(185, 135, 8, .07));
  font-size: 12.5px; line-height: 1.45; color: var(--ink);
}
.mutebar .btn { margin-left: auto; flex: none; }
@media (max-width: 560px) {
  .mutebar { flex-direction: column; align-items: flex-start; }
  .mutebar .btn { margin-left: 0; }
}
select.flash, input.flash {
  animation: fieldFlash 1.2s cubic-bezier(.32,.72,0,1);
}
@keyframes fieldFlash {
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
  15%      { box-shadow: 0 0 0 4px rgba(185,135,8,.28); }
  60%      { box-shadow: 0 0 0 4px rgba(185,135,8,.14); }
}
/* The severity currently in force, when the delivery bar is expanded. */
.or-item.on {
  border-color: var(--accent);
  background: var(--surface);
  color: var(--ink);
}
.or-item.on .or-plus { background: var(--accent); color: var(--surface); }

/* Storage breakdown: dense, scrollable on narrow screens. */
#storageCard .sto { width: 100%; border-collapse: collapse; margin: 6px 0 4px; font-size: 12.5px; }
#storageCard { overflow-x: auto; }
.sto th {
  text-align: right; font: 600 10.5px var(--sans); letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-3); padding: 6px 10px; white-space: nowrap;
}
.sto th.l, .sto td.l { text-align: left; }
.sto th[data-stosort] { cursor: pointer; user-select: none; }
.sto th[data-stosort]:hover { color: var(--ink-2); }
.sto th.sorted { color: var(--ink); }
.sto td { text-align: right; padding: 7px 10px; border-top: 1px solid var(--hairline); white-space: nowrap; }
.sto tfoot td { border-top: 1px solid var(--line); font-weight: 600; }
.sto tbody tr:hover { background: var(--surface-2); }

/* Settings cards vary wildly in height. Rather than let a grid row stretch to
   its tallest card and leave a hole beside the short ones, the page stacks its
   cards in explicit columns and pairs a tall card with a short one in each, so
   both columns finish at roughly the same place. */
/* `align-items: stretch`, not the grid's `start`: a card with a short line
   (a folded "How it works") must not shrink to its words. */
.settings-grid.masonry { display: flex; flex-direction: column; gap: 12px; align-items: stretch; }
.settings-grid.masonry > .card { width: auto; margin: 0; }
.card-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; align-items: start; }
.card-cols > .col { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
@media (max-width: 860px) { .card-cols { grid-template-columns: 1fr; } }

/* A unit belongs beside its input, not stacked under it — inside a label.f the
   bare text would become another row of the column and shove the field upward. */
.f .with-unit { display: flex; align-items: center; gap: 7px; }
.f .with-unit input { width: 92px; flex: none; }
.f .with-unit .small { text-transform: none; letter-spacing: 0; font-weight: 400; }

/* The settings band sits left, its explanation beside it. A grid, not flex:
   two columns that cannot fold into a stack when something nudges them. */
.sto-setup { display: grid; grid-template-columns: 1fr; gap: 8px 28px; align-items: start; }
.sto-setup > .form-row { grid-column: 1; flex-wrap: wrap; align-items: flex-end; gap: 10px 14px; }
.sto-setup > .form-row label.f { flex: 0 1 auto; min-width: 0; }
.sto-setup > .form-row label.f select { min-width: 205px; }
.sto-setup > .form-row .with-unit { display: inline-flex; align-items: center; gap: 8px; }
.sto-setup > .form-row .btn { align-self: flex-end; margin-left: 4px; margin-bottom: 1px; }
.sto-setup > p { grid-column: 1; margin: 2px 0 0; max-width: 78ch; }
/* Native number spinners and selects don't agree on height; pin them so the
   row lines up whatever the platform draws. */
.sto-setup select, .sto-setup input[type="number"] { height: 36px; box-sizing: border-box; }
@media (max-width: 980px) {
  .sto-setup { grid-template-columns: 1fr; }
  .sto-setup > p { grid-column: 1; }
}

/* Map links — pills rather than bare anchors, so they read as actions.
   The "go" variant navigates from wherever you are; the plain one drops a pin. */
.maplinks { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.maplinks .coords { margin-left: auto; font-variant-numeric: tabular-nums; }
.maplink {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 11px 6px 9px; border-radius: 20px;
  border: 1px solid var(--hairline); background: var(--surface-2);
  font: 500 12px var(--sans); color: var(--ink-2); text-decoration: none;
  transition: border-color .14s, background .14s, color .14s, transform .14s;
}
.maplink svg { width: 13px; height: 13px; fill: currentColor; flex: none; opacity: .75; }
.maplink:hover { border-color: var(--accent); color: var(--ink); background: var(--surface); }
.maplink:active { transform: translateY(1px); }
.maplink.go {
  background: var(--ink); border-color: var(--ink); color: var(--surface);
}
.maplink.go svg { opacity: 1; }
.maplink.go:hover { background: var(--ink-2); border-color: var(--ink-2); color: var(--surface); }
@media (max-width: 560px) {
  .maplink { flex: 1 1 auto; justify-content: center; }
  .maplinks .coords { flex-basis: 100%; margin-left: 0; text-align: center; }
}

/* A place name that opens the map. Accent + underline at rest so it reads as
   a link before the pointer ever arrives; hovering turns it into a chip. The
   negative margins mean the box grows around the text without shifting it. */
.addr {
  color: var(--accent); text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  padding: 2px 6px; margin: -2px -6px; border-radius: 7px;
  transition: background .12s, border-color .12s;
}
.addr:hover {
  background: var(--accent-wash);
  border-bottom-color: transparent;
}

/* The demo sash: always visible, never in the way. */
#demobar {
  position: sticky; top: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center; gap: 14px;
  padding: 8px 16px; font: 500 12.5px var(--sans);
  background: var(--ink); color: var(--paper);
}
#demobar strong { font-weight: 700; }
#demobar a { color: inherit; text-decoration: underline; text-underline-offset: 2px; white-space: nowrap; }
@media (max-width: 640px) { #demobar { flex-wrap: wrap; text-align: center; } }

/* The way into the demo, on the sign-in card. */
.login-demo { font-weight: 600; color: var(--accent); text-decoration: none; }
.login-demo:hover { text-decoration: underline; }


/* Phones: the storage band and the people rows stack instead of clipping. */
@media (max-width: 980px) {
  .sto-setup > .form-row { flex-wrap: wrap; }
}
@media (max-width: 640px) {
  .user-row { grid-template-columns: 1fr auto; }
  .user-row .who { grid-column: 1 / -1; }
  .user-row > div:last-child { grid-column: 1 / -1; flex-wrap: wrap; }
}


/* Inputs must never dictate row width: their intrinsic ~230px size was forcing
   flex rows wider than the card on phones, so whole settings panels panned
   sideways and buttons appeared clipped mid-scroll. */
.f input, .f select { width: 100%; min-width: 0; box-sizing: border-box; }
.form-row { min-width: 0; }

/* ---- link-session chrome ---- */
#linkbar {
  position: sticky; top: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center; gap: 14px;
  padding: 8px 16px; font: 500 12.5px var(--sans);
  background: var(--accent); color: #fff;
}
#linkbar .lb-keep {
  border: 1px solid rgba(255,255,255,.5); background: transparent; color: #fff;
  border-radius: 20px; padding: 4px 12px; font: 600 12px var(--sans); cursor: pointer;
}
#linkbar .lb-keep:hover { background: rgba(255,255,255,.14); }
/* A link holder is a user for this one car: automations, alerts and their
   own settings stay. Hidden: sharing onward, wake/refresh (billable), and —
   inside Settings, via the classes below — anything account- or email-shaped. */
body.linkmode #btnShare,
body.linkmode #btnRefresh, body.linkmode #btnWake { display: none; }
body.linkmode .acct-only { display: none !important; }

/* ---- share-link rows: whole row copies, targets you can actually hit ---- */
.linkrow { position: relative; overflow: hidden; border-radius: var(--r2); margin-top: 6px; }
.linkrow .lr-del {
  position: absolute; inset: 0 0 0 auto; width: 100%;
  display: flex; align-items: center; justify-content: flex-end;
  padding-right: 22px; background: #d03b3b; color: #fff;
  font: 700 16px var(--sans); cursor: pointer;
}
.linkrow .lr-main {
  position: relative; display: flex; align-items: center; gap: 10px;
  padding: 10px 8px 10px 12px; background: var(--surface-2);
  border: 1px solid var(--hairline-2); border-radius: var(--r2);
  cursor: copy; min-width: 0;
}
.linkrow .lr-url { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
.linkrow .lr-main:hover { border-color: var(--accent); }
.linkrow .lr-btn {
  flex: none; width: 34px; height: 34px; border-radius: 9px;
  border: 1px solid var(--hairline); background: var(--surface);
  font-size: 14px; color: var(--ink-2); cursor: pointer;
}
.linkrow .lr-btn:hover { color: var(--ink); border-color: var(--ink); }
.linkrow .lr-btn.lr-x:hover { color: #fff; background: #d03b3b; border-color: #d03b3b; }


/* A sash across the top (demo or shared-link) takes real vertical room: the
   fixed sidebar starts below it instead of hiding its first line under it,
   and the sticky topbar parks under the sash rather than on top of it. */
body:has(#demobar), body:has(#linkbar) { --sash-h: 37px; }
body:has(#demobar) .topbar, body:has(#linkbar) .topbar { top: var(--sash-h); }
@media (max-width: 640px) {
  body:has(#demobar), body:has(#linkbar) { --sash-h: 56px; }
}

/* A car that others can see says so. */
.hero-share.is-shared {
  color: var(--good-ink); border-color: color-mix(in srgb, var(--good) 55%, transparent);
  background: color-mix(in srgb, var(--good) 8%, var(--surface));
}
.hero-share.is-shared:hover { border-color: var(--good-ink); }

/* Old pack figures dress as history, not as the present. */
.pack-kw.stale { color: var(--muted); }
.pack-kw.stale .pk-l { color: var(--faint); }


/* iOS Safari auto-zooms the page when a focused input's font is under 16px —
   and the zoom sticks until pinched out, which read as "the app loads zoomed
   in". On touch screens every field meets the 16px bar, so Safari never has a
   reason to zoom; pinch-zoom itself stays available. */
@media (pointer: coarse) {
  input, select, textarea { font-size: 16px; }
}

/* ---- the AI alert analyst ---- */
.ai-btn {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 8px;
  padding: 5px 11px 5px 9px; border-radius: 20px; cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  background: var(--accent-wash); color: var(--accent);
  font: 600 12px var(--sans); transition: background .14s, border-color .14s;
}
.ai-btn svg { width: 13px; height: 13px; fill: currentColor; }
.ai-btn:hover { background: color-mix(in srgb, var(--accent) 18%, transparent); border-color: var(--accent); }

.ai-fab {
  position: fixed; right: 22px; bottom: calc(92px + env(safe-area-inset-bottom, 0px)); z-index: 120;
  display: inline-flex; align-items: center; gap: 8px; padding: 12px 18px 12px 14px;
  border-radius: 999px; border: 0; cursor: pointer;
  background: var(--ink); color: var(--paper); font: 600 13.5px var(--sans);
  box-shadow: 0 8px 28px rgba(10,10,9,.22);
}
.ai-fab svg { width: 16px; height: 16px; fill: currentColor; }
.ai-fab[hidden] { display: none; }
.ai-fab { touch-action: none; user-select: none; -webkit-user-select: none; }
.ai-fab.dragging { transform: scale(1.04); box-shadow: 0 12px 34px rgba(10,10,9,.3); cursor: grabbing; transition: none; }
/* Under an answer: the settings the assistant changed on request. */
.ai-changes { margin-top: 8px; padding: 6px 9px; border-radius: 8px; background: var(--good-bg, rgba(46,125,50,.08)); color: var(--good-ink); font-size: 12px; }
/* Admin → AI: who asked how much. */
.ai-usage { margin-top: 8px; padding-top: 10px; border-top: 1px solid var(--hairline-2); }
.ai-usage-head { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; margin-bottom: 8px; font-size: 13px; }
.ai-usage-tbl { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.ai-usage-tbl th { text-align: left; font: 600 10px/1 var(--sans); text-transform: uppercase; letter-spacing: .1em; color: var(--faint); padding: 4px 8px 6px 0; }
.ai-usage-tbl td { padding: 6px 8px 6px 0; border-top: 1px solid var(--hairline-2); }
.ai-usage-tbl tr.total td { font-weight: 600; }
.ai-fab:hover { transform: translateY(-1px); }
@media (min-width: 900px) { .ai-fab { bottom: 28px; } }
/* On a phone the pill covered the right-hand figures of whatever row it sat
   on; a round icon button takes a third of the width. The name stays for
   screen readers. */
@media (max-width: 899.98px) {
  .ai-fab { width: 48px; height: 48px; padding: 0; justify-content: center; right: 16px; }
  .ai-fab svg { width: 20px; height: 20px; }
  .ai-fab .ai-fab-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}

.ai-panel {
  position: fixed; right: 22px; bottom: calc(92px + env(safe-area-inset-bottom, 0px)); z-index: 130;
  width: min(420px, calc(100vw - 24px)); height: min(560px, calc(100vh - 140px));
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r3);
  box-shadow: 0 18px 60px rgba(10,10,9,.28); overflow: hidden;
}
@media (min-width: 900px) { .ai-panel { bottom: 28px; } }
.ai-head {
  display: flex; align-items: center; gap: 10px; padding: 11px 12px 11px 14px;
  background: var(--ink); color: var(--paper); cursor: grab; user-select: none;
}
.ai-head:active { cursor: grabbing; }
.ai-title { display: inline-flex; align-items: center; gap: 7px; font: 700 13px var(--sans); }
.ai-title svg { width: 15px; height: 15px; fill: currentColor; }
.ai-sub { flex: 1; font: 500 12px var(--sans); opacity: .7; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ai-x { border: 0; background: rgba(255,255,255,.12); color: inherit; border-radius: 8px; padding: 4px 8px; font: 600 11px var(--sans); cursor: pointer; }
.ai-x:hover { background: rgba(255,255,255,.22); }
.ai-log { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 10px; background: var(--paper); }
.ai-msg { max-width: 92%; padding: 10px 13px; border-radius: 14px; font-size: 13.5px; line-height: 1.5; }
.ai-msg.user { align-self: flex-end; background: var(--accent); color: var(--on-accent, #fff); border-bottom-right-radius: 4px; }
.ai-msg.assistant { align-self: flex-start; background: var(--surface); border: 1px solid var(--hairline-2); border-bottom-left-radius: 4px; }
.ai-sources { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; padding-top: 8px; border-top: 1px solid var(--hairline-2); }
/* Watty's cards: the drives and charges an answer is about, under it. */
.ai-msg.assistant:has(.ai-cards) { max-width: 100%; width: 100%; }
.ai-cards { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.ai-card {
  display: flex; flex-direction: column; overflow: hidden; cursor: pointer;
  border: 1px solid var(--hairline); border-radius: var(--r2); background: var(--paper);
  transition: border-color .15s, transform .15s;
}
.ai-card:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--hairline)); }
.ai-card:active { transform: scale(.995); }
.ai-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ai-card-map { height: 118px; overflow: hidden; border-bottom: 1px solid var(--hairline-2); }
.ai-card-map .tmap { border-radius: 0; margin: 0; pointer-events: none; }
.ai-card-body { display: flex; flex-direction: column; gap: 5px; padding: 9px 11px 10px; min-width: 0; }
.ai-card-head { display: flex; align-items: center; gap: 7px; font-size: 11.5px; color: var(--muted); min-width: 0; }
.ai-card-kind {
  font: 700 9.5px/1 var(--sans); letter-spacing: .08em; text-transform: uppercase;
  padding: 3px 6px; border-radius: 5px; background: var(--accent-wash); color: var(--accent);
}
.ai-card-kind.charge { background: color-mix(in srgb, var(--good) 14%, transparent); color: var(--good-ink); }
.ai-card-car { font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ai-card-when { margin-left: auto; white-space: nowrap; font-variant-numeric: tabular-nums; }
.ai-card-where { font-size: 12.5px; font-weight: 500; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ai-card-where span { color: var(--faint); }
.ai-card-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; margin-top: 2px; }
.ai-card { container-type: inline-size; }
/* A phone-width card: two by two, so "201 Wh/km" is never cut. */
@container (max-width: 330px) { .ai-card-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 7px; } }
.ai-card-stats span { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ai-card-stats b { font-size: 13px; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ai-card-stats i { font-style: normal; font-size: 9.5px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--faint); }
.ai-sources a { font-size: 11.5px; color: var(--accent); text-decoration: none; background: var(--accent-wash); padding: 2px 8px; border-radius: 12px; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ai-typing { display: flex; gap: 4px; padding: 12px 14px; }
.ai-typing i { width: 6px; height: 6px; border-radius: 50%; background: var(--muted); animation: aiDot 1.2s infinite ease-in-out; }
.ai-typing i:nth-child(2) { animation-delay: .2s; } .ai-typing i:nth-child(3) { animation-delay: .4s; }
@keyframes aiDot { 0%, 80%, 100% { opacity: .25; transform: translateY(0); } 40% { opacity: 1; transform: translateY(-3px); } }
.ai-form { display: flex; gap: 8px; padding: 10px; border-top: 1px solid var(--hairline-2); background: var(--surface); }
.ai-form input { flex: 1; min-width: 0; }
@media (prefers-reduced-motion: reduce) { .ai-typing i { animation: none; opacity: .6; } }
.ai-quota { padding: 0 12px 8px; font-size: 11px; color: var(--faint); text-align: right; background: var(--surface); }

/* Driving strip: braking flashes the whole strip red twice; the strip is a
   button into Drives; pack temp / heater / hvac chips ride the signals row. */
.drive-strip[data-godrive] { cursor: pointer; }
.drive-strip.braking { animation: brakeFlash .9s ease-out 1; }
/* Two hard brake-light pulses: the whole strip goes red with a wide glow,
   off, then on again — unmistakable at a glance, gone within a second. */
@keyframes brakeFlash {
  0%, 100% { box-shadow: var(--lift-2); }
  6%, 26% { box-shadow: 0 0 0 5px #ff2a1f, 0 0 70px 22px rgba(255, 42, 31, .85); background: linear-gradient(135deg, #ff3b2e, #c8160c); }
  34%, 46% { box-shadow: var(--lift-2); }
  52%, 74% { box-shadow: 0 0 0 5px #ff2a1f, 0 0 70px 22px rgba(255, 42, 31, .85); background: linear-gradient(135deg, #ff3b2e, #c8160c); }
  82% { box-shadow: var(--lift-2); }
}
.drive-strip.braking > .ds-scene::after {
  content: ''; position: absolute; inset: 0; background: #ff2a1f; border-radius: inherit;
  animation: brakeVeil .9s steps(1, end) 1;
}
@keyframes brakeVeil { 0%, 34%, 82%, 100% { opacity: 0; } 6% { opacity: .55; } 26% { opacity: .55; } 52% { opacity: .55; } 74% { opacity: .55; } }
.ds-chip.temp { background: rgba(255,255,255,.18); }
.ds-chip.temp.hot { background: #ffb347; color: #3a2400; }
.ds-chip.heat { background: #ff8a3d; color: #fff; }
.ds-chip.hvac { background: rgba(120, 200, 255, .35); }
@media (prefers-reduced-motion: reduce) { .drive-strip.braking { animation: none; } }

/* Notification message helpers: one-tap value chips, a search for everything
   else, and a live preview of the sentence as it will be sent. */
.notify-vars { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 6px; width: 100%; }
.var-chips, .var-results { display: flex; flex-wrap: wrap; gap: 6px; }
.chip.var { font-size: 12px; padding: 4px 9px; cursor: pointer; font: inherit; }
.chip.var .muted { margin-left: 5px; font-size: 11.5px; }
.chip.var.more { border-style: dashed; }
.var-search input { width: 100%; }
.var-results { margin-top: 6px; }
.var-preview:empty { display: none; }

/* Diagnosis: the analyst button sits on every current-alert row, compact. */
.diag-row .ai-btn-sm { padding: 3px 8px; font-size: 11.5px; margin-left: 8px; flex: 0 0 auto; }
.diag-row .ai-btn-sm svg { width: 12px; height: 12px; }

/* ---- Diagnosis: thermal map, trends, tyre map, timeline ---- */
.tmap-wrap { display: flex; flex-direction: column; gap: 8px; }
.tmap, .tyremap { width: 100%; max-width: 440px; height: auto; display: block; margin: 0 auto; }
.tm-body { fill: var(--surface); stroke: var(--hairline-2); stroke-width: 1.5; }
.tm-wheel { fill: var(--ink); opacity: .16; }
.tm-cap { font-size: 9px; fill: var(--muted); font-family: inherit; }
.tm rect { stroke: var(--hairline-2); stroke-width: 1; }
.tm-none rect { fill: var(--hairline); }
.tm-cold rect, i.tm-cold { fill: color-mix(in srgb, #5aa9ff 55%, transparent); background: color-mix(in srgb, #5aa9ff 55%, transparent); }
.tm-ok rect, i.tm-ok { fill: color-mix(in srgb, var(--good, #3fae5a) 38%, transparent); background: color-mix(in srgb, var(--good, #3fae5a) 38%, transparent); }
.tm-warm rect, i.tm-warm { fill: color-mix(in srgb, var(--warning, #e0a300) 55%, transparent); background: color-mix(in srgb, var(--warning, #e0a300) 55%, transparent); }
.tm-hot rect, i.tm-hot { fill: color-mix(in srgb, var(--critical, #d33) 62%, transparent); background: color-mix(in srgb, var(--critical, #d33) 62%, transparent); }
.tm text { font-size: 10px; fill: var(--ink); text-anchor: middle; font-family: inherit; }
.tm text.tt { font-weight: 600; font-size: 10.5px; letter-spacing: .01em; }
.tm text.ts { fill: var(--muted); font-size: 8.5px; }
.tmap-legend { display: flex; gap: 12px; flex-wrap: wrap; font-size: 11.5px; color: var(--muted); justify-content: center; }
.tmap-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 5px; vertical-align: -1px; border: 1px solid var(--hairline-2); }
.trend-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 14px 20px; }
.trend-h { display: flex; justify-content: space-between; gap: 8px; font-size: 12.5px; margin-bottom: 4px; align-items: baseline; }
.trend-tyres { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 12px; }
.trend-tyres > div > .muted { display: block; margin-bottom: 1px; }
.spark rect.band { fill: var(--good, #3fae5a); opacity: .12; }
.tyre rect { stroke: var(--hairline-2); stroke-width: 1; }
.tyre-good rect { fill: color-mix(in srgb, var(--good, #3fae5a) 30%, transparent); }
.tyre-warn rect { fill: color-mix(in srgb, var(--warning, #e0a300) 48%, transparent); }
.tyre-crit rect { fill: color-mix(in srgb, var(--critical, #d33) 58%, transparent); }
.tyre-none rect { fill: var(--hairline); }
.tyre text { text-anchor: middle; font-family: inherit; fill: var(--ink); }
.tyre .tv { font-size: 12px; font-weight: 650; }
.tyre .td { font-size: 9px; fill: var(--muted); }
.tyre .tk { font-size: 8px; fill: var(--muted); text-transform: uppercase; letter-spacing: .06em; }
.tyremap .tc { text-anchor: middle; font-size: 10px; fill: var(--muted); font-family: inherit; }
.tl-strip { display: grid; grid-template-columns: repeat(30, 1fr); gap: 3px; align-items: end; }
.tl-day { display: flex; flex-direction: column; align-items: center; gap: 3px; justify-content: flex-end; cursor: pointer; }
.tl-dots { display: flex; flex-direction: column-reverse; align-items: center; gap: 2px; min-height: 30px; justify-content: flex-start; transition: transform .12s; }
.tl-day.has:hover .tl-dots { transform: scale(1.18); }
.tl-day:not(.has) .tl-dots::before { content: ''; width: 4px; height: 4px; border-radius: 50%; background: var(--hairline-2); }
.tl-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--warning, #e0a300); display: block; }
.tl-dot.serious { background: var(--critical, #d33); opacity: .75; }
.tl-dot.critical { background: var(--critical, #d33); }
.tl-more { font-size: 9px; color: var(--muted); font-style: normal; }
.tl-lab { font-size: 9.5px; color: var(--muted); height: 12px; line-height: 12px; }

/* Thermal map: glasshouse and lights on the top-view car; state bar for sleep card */
.tm-glass { fill: var(--ink); opacity: .10; }
.tm-roof { fill: var(--ink); opacity: .05; }
.tm-light { fill: #ffe9a8; stroke: var(--hairline-2); stroke-width: .5; }
.tm-tail { fill: #ff6b5e; opacity: .75; }
.tm text.tv { font-size: 11px; font-weight: 500; }
.state-bar { display: flex; height: 8px; border-radius: 4px; overflow: hidden; background: var(--hairline); margin: 2px 0 10px; }
.state-bar i { display: block; height: 100%; }
.state-bar i.on { background: var(--good, #3fae5a); }
.state-bar i.sleep { background: var(--ink); opacity: .35; }
.state-bar i.off { background: var(--critical, #d33); opacity: .7; }

/* Desktop gets a larger, centred thermal map; phones keep the compact one. */
@media (min-width: 900px) { .tmap { max-width: 660px; } .tmap-card .tmap-wrap { align-items: center; } }

/* Weather card: a line icon for the sky, in the theme's ink */
.wx-head { display: flex; align-items: center; gap: 10px; }
.wx-head > div { display: flex; flex-direction: column; }
.wx-icon { width: 38px; height: 41px; flex: 0 0 auto; overflow: visible; fill: none; stroke: var(--ink); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.wx-icon .wx-sun { fill: color-mix(in srgb, var(--warning, #e0a300) 45%, transparent); }
.wx-icon .wx-moon { fill: color-mix(in srgb, var(--ink) 12%, transparent); }
.wx-icon .wx-bolt { fill: color-mix(in srgb, var(--warning, #e0a300) 60%, transparent); }

/* Art theme on the new Diagnosis pieces: billboard numerals in the blocks,
   mono petite-caps for the captions, the same voice as the stat cards. */
:root[data-theme="art"] .tm text.tv,
:root[data-theme="art"] .tyre .tv { font-family: var(--display); font-weight: 400; letter-spacing: .02em; }
:root[data-theme="art"] .tm text.tt,
:root[data-theme="art"] .tm text.ts,
:root[data-theme="art"] .tm-cap,
:root[data-theme="art"] .tyre .tk,
:root[data-theme="art"] .tyre .td,
:root[data-theme="art"] .tyremap .tc,
:root[data-theme="art"] .tl-lab,
:root[data-theme="art"] .tmap-legend,
:root[data-theme="art"] .trend-h > span:first-child { font-family: var(--mono); letter-spacing: .08em; text-transform: uppercase; }
:root[data-theme="art"] .tm text.tt { font-size: 9px; }
:root[data-theme="art"] .trend-h > span:first-child { font-size: 11px; }
:root[data-theme="art"] .wx-head .v { font-family: var(--display); font-weight: 400; }

/* Art everywhere: every view gets the same billboard numerals and mono
   petite-caps captions the stat cards have — lists, drive card, Diagnosis
   rows, key/value tables, section labels, driving-strip chips. */
:root[data-theme="art"] .speed-big,
:root[data-theme="art"] .log-row .big,
:root[data-theme="art"] .cell .v,
:root[data-theme="art"] .diag-row .v,
:root[data-theme="art"] .arrival-chip .pct,
:root[data-theme="art"] .dh-score .num { font-family: var(--display); font-weight: 400; letter-spacing: .02em; }
:root[data-theme="art"] .speed-big small { font-family: var(--sans); font-weight: 600; }
:root[data-theme="art"] .g-lab,
:root[data-theme="art"] .section-title,
:root[data-theme="art"] .step-box .lbl,
:root[data-theme="art"] .cell .l,
:root[data-theme="art"] .kv dt,
:root[data-theme="art"] .diag-row .l,
:root[data-theme="art"] .card-foot,
:root[data-theme="art"] .eta-row { font-family: var(--mono); letter-spacing: .08em; text-transform: uppercase; font-size: 11px; }
:root[data-theme="art"] .ds-chip,
:root[data-theme="art"] .tag { font: 600 10px/1 var(--sans); text-transform: uppercase; letter-spacing: .07em; }
:root[data-theme="art"] input,
:root[data-theme="art"] select,
:root[data-theme="art"] textarea { border-radius: 4px; border-color: var(--hairline); box-shadow: none; }

/* Top-view car shell shared by the thermal and tyre maps */
.car-body { fill: var(--surface); stroke: var(--hairline-2); stroke-width: 1.2; }
.car-body-sheen { fill: url(#carBody); pointer-events: none; }
.car-tyre { fill: var(--ink); opacity: .78; }
.car-tyre-good { fill: var(--good); opacity: 1; }
.car-tyre-warn { fill: var(--warning); opacity: 1; }
.car-tyre-crit { fill: var(--critical); opacity: 1; }
.car-arch { fill: none; stroke: var(--ink); stroke-opacity: .18; stroke-width: 1.5; }
.car-glass { fill: url(#carGlass); }
.car-roof { fill: var(--ink); opacity: .06; }
.car-crease { stroke: var(--ink); stroke-opacity: .10; stroke-width: 1; }
.car-mirror { fill: var(--ink); opacity: .35; }
.car-lamp { fill: #ffe9a8; stroke: var(--hairline-2); stroke-width: .5; }
.car-tail { fill: #ff5a4e; opacity: .85; }
.tm-grid { stroke: var(--ink); stroke-opacity: .12; stroke-width: 1; }
.tm rect { stroke: var(--ink); stroke-opacity: .18; }
.tmap, .tyremap { max-width: 520px; }
@media (min-width: 900px) { .tmap { max-width: 720px; } .tyremap { max-width: 460px; } }

/* Compact pressure, cold-reference difference and status at each wheel. */
.diag-tyres-card h3 { flex-wrap: wrap; gap: 6px 12px; }
.diag-tyre-layout { position: relative; max-width: 460px; margin: 0 auto; padding: 40px 0; }
.diag-tyre-layout .tyremap { width: 100%; max-width: none; margin: 0; }
.diag-tyre-reading { position: absolute; transform: translateX(-50%); display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 5px 8px; border: 1px solid var(--hairline-2); border-radius: 8px; background: var(--surface); color: var(--ink); box-shadow: 0 1px 3px #0002; line-height: 1.2; font-variant-numeric: tabular-nums; white-space: nowrap; }
.diag-tyre-reading.tyre-good { border-color: var(--good); }
.diag-tyre-reading.tyre-warn { border-color: var(--warning); }
.diag-tyre-reading.tyre-crit { border-color: var(--critical); }
.diag-tyre-pressure { display: flex; align-items: baseline; gap: 4px; }
.diag-tyre-pressure b { font-size: 13px; font-weight: 650; }
.diag-tyre-pressure small, .diag-tyre-delta { font-size: 10px; }
.diag-tyre-verdict { font-size: 9px; text-transform: uppercase; letter-spacing: .04em; }
:root[data-theme="art"] .diag-tyre-pressure b { font-family: var(--display); font-weight: 400; }

/* Car strip: pills normally; one button + dropdown on phones or with many cars */
.vstrip { position: relative; display: flex; align-items: center; gap: 6px; min-width: 0; }
.vstrip .vmenu-btn { display: none; gap: 6px; align-items: center; max-width: 60vw; }
.vstrip .vmenu-btn .chev { width: 16px; height: 16px; flex: 0 0 auto; margin-left: 2px; opacity: .95; }
.vstrip.open .vmenu-btn .chev { transform: rotate(180deg); }
.vstrip .vpills { display: flex; gap: 6px; flex-wrap: wrap; }
.vstrip.compact .vmenu-btn { display: inline-flex; }
.vstrip.compact .vpills { display: none; }
.vstrip.compact.open .vpills { display: flex; flex-direction: column; align-items: stretch; gap: 4px; position: absolute;
  top: calc(100% + 6px); left: 0; z-index: 40; min-width: 230px; max-height: 60vh; overflow: auto;
  background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r1); padding: 6px; box-shadow: var(--lift-2); }
.vstrip.compact.open .vpills .vpill { justify-content: flex-start; }

/* Phone top bar: a search glyph at the far right (the sidebar's Search is hidden there) */
.top-actions .top-search { display: none; width: 36px; padding: 0; justify-content: center; margin-left: auto; }
@media (max-width: 640px) {
  .top-actions { margin-left: auto; }
  .top-actions .top-search { display: inline-flex; }
  .top-actions .btn:not(.top-search) { padding: 0 11px; }
}

/* Battery % beside each car in the phone dropdown (and on the current-car pill) */
.vpill .pct { display: none; margin-left: 6px; font-weight: 500; color: var(--muted); font-variant-numeric: tabular-nums; }
.vstrip.compact .vpill .pct { display: inline; }
.vstrip.compact .vpills .vpill .pct { margin-left: auto; padding-left: 12px; }
.vstrip.compact .vmenu-btn.active .pct { color: inherit; opacity: .8; }

/* Toast with an action (Undo after a drag) */
.toast.has-action { display: flex; align-items: center; gap: 12px; }
.toast-act { margin-left: auto; border: 1px solid currentColor; background: transparent; color: inherit;
  font: 600 12px/1 var(--sans); padding: 5px 10px; border-radius: 999px; cursor: pointer; }

/* Driving strip: power bar centred on zero, and the drivetrain split */
.ds-bars { flex: 1 1 100%; display: flex; flex-wrap: wrap; gap: 8px 22px; align-items: center; }
.ds-power { display: flex; align-items: center; gap: 8px; flex: 1 1 260px; min-width: 220px; }
.ds-power .lab { font-size: 10px; text-transform: uppercase; letter-spacing: .08em; opacity: .75; }
.ds-power .track { position: relative; flex: 1; height: 10px; border-radius: 5px; background: rgba(255,255,255,.18); overflow: hidden; }
.ds-power .zero { position: absolute; left: 50%; top: -2px; bottom: -2px; width: 2px; background: rgba(255,255,255,.7); transform: translateX(-1px); }
/* The fill's width and the split's shares are written per frame by the glide
   loop in app.js (they chase the stream), so they carry no CSS transition. */
.ds-power .fill { position: absolute; top: 0; bottom: 0; width: 0; border-radius: 5px; }
.ds-power .fill.drain { left: 50%; background: linear-gradient(90deg, #fff, #ffd166); }
.ds-power .fill.regen { right: 50%; background: linear-gradient(270deg, #fff, #4be38a); }
.ds-power .val { font-size: 13px; font-variant-numeric: tabular-nums; min-width: 62px; text-align: right; }
.ds-power .val.regen { color: #7dffb0; }
.ds-split { display: flex; gap: 3px; flex: 1 1 220px; min-width: 200px; height: 30px; }
.ds-split .seg { display: flex; align-items: center; justify-content: space-between; gap: 6px; padding: 0 9px;
  border-radius: 7px; background: rgba(255,255,255,.22); font-size: 12px; font-variant-numeric: tabular-nums; min-width: 0; overflow: hidden; }
.ds-split .seg em { font-style: normal; font-weight: 700; opacity: .85; }
.ds-split .seg b { font-weight: 600; white-space: nowrap; }
.ds-split .seg.idle { background: rgba(255,255,255,.10); opacity: .8; }
.ds-split .seg.regen { background: rgba(75, 227, 138, .35); }

/* ---- full-screen driving view ---- */
.ds-hud-btn { position: absolute; top: 14px; right: 14px; width: 32px; height: 32px; border-radius: 50%; border: 1px solid rgba(255,255,255,.14);
  background: rgba(255,255,255,.1); color: #fff; font-size: 15px; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
.ds-hud-btn:hover { background: rgba(255,255,255,.2); }
.hud-open-btn { align-self: flex-start; }
body.hud-open { overflow: hidden; }
/* The one column is pinned to the viewport: an auto column would grow to the
   bar's min-content — a route line with a destination, ETA and arrival charge
   is wider than a phone — and push the speed, cells and map off the right edge. */
.drive-hud { position: fixed; inset: 0; z-index: 300; display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto auto auto auto auto 1fr; gap: 12px;
  padding: calc(env(safe-area-inset-top, 0px) + 12px) 20px calc(env(safe-area-inset-bottom, 0px) + 14px);
  background: radial-gradient(120% 90% at 50% 0%, #142a5c 0%, #070b14 60%, #04060c 100%); color: #fff; overflow: hidden; }
.drive-hud.braking { animation: hudBrake .9s steps(1, end) 1; }
@keyframes hudBrake { 0%, 34%, 82%, 100% { box-shadow: none; background-color: transparent; } 6%, 26%, 52%, 74% { box-shadow: inset 0 0 0 100vmax rgba(255, 42, 31, .62); } }
.hud-bar { display: flex; align-items: center; gap: 14px; min-height: 36px; }
.hud-live { display: inline-flex; align-items: center; gap: 8px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; font-size: 13px; }
.hud-route { flex: 1; font-size: 15px; opacity: .9; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hud-close { width: 40px; height: 40px; border-radius: 50%; border: 1px solid rgba(255,255,255,.3); background: rgba(255,255,255,.12); color: #fff; font-size: 18px; cursor: pointer; }
.hud-speed { display: flex; align-items: baseline; justify-content: center; gap: 12px; }
.hud-num { font: 700 clamp(84px, 22vw, 168px)/1 var(--sans); letter-spacing: -.04em; font-variant-numeric: tabular-nums; }
.hud-unit { font-size: clamp(18px, 4vw, 28px); opacity: .8; }
.hud-bars .ds-bars { gap: 14px 28px; }
.hud-bars .ds-power .track { height: 16px; border-radius: 8px; }
.hud-bars .ds-split { height: 44px; }
.hud-bars .ds-split .seg { font-size: 15px; }
.hud-cells { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 10px; }
.hud-cell { background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.12); border-radius: 12px; padding: 10px 12px; display: flex; flex-direction: column; gap: 2px; }
.hud-cell b { font-size: 22px; font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
.hud-cell span { font-size: 10.5px; text-transform: uppercase; letter-spacing: .08em; opacity: .7; }
.hud-signals { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; min-height: 24px; }
.hud-map { min-height: 0; border-radius: 14px; overflow: hidden; }
.hud-map .map-embed, .hud-map iframe { width: 100%; height: 100%; }
.hud-map .tmap { max-height: 100%; }
/* Landscape has no two-column speed/map layout any more: the cluster below
   owns every landscape viewport from 640 px up. The old rule that placed these
   children by grid-area lived on here after the cluster arrived, and with the
   cluster's `grid-template-areas: none` its names resolved to implicit lines —
   the bar fell into an implicit row at the bottom and an implicit column beside
   the cluster stole most of the width (a 100 px gauge on a 2000 px window). */
:root[data-theme="art"] .hud-num { font-family: var(--display); font-weight: 400; letter-spacing: .01em; }

/* Charges: per-site rows under the "Where you charge" bar */
.site-rows { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; }
.site-row { display: grid; grid-template-columns: 12px minmax(90px, 1.1fr) repeat(5, minmax(0, 1fr)); gap: 10px; align-items: center; font-size: 12.5px; }
.site-row .sw { width: 10px; height: 10px; border-radius: 3px; }
.site-row b { font-weight: 600; }
.site-row span:not(.sw) { color: var(--ink-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.site-row i.muted { font-style: normal; }
@media (max-width: 720px) { .site-row { grid-template-columns: 12px 1fr 1fr; } .site-row span:nth-child(n+5) { grid-column: span 1; } }

/* Where-you-charge rows: name + wrapping facts (only the ones that exist) */
.site-row { display: flex; flex-wrap: wrap; gap: 4px 14px; align-items: center; font-size: 12.5px; }
.site-row > span:not(.sw) { white-space: nowrap; }
/* Charge curve */
.charge-chart-slot { margin: 10px 0 4px; }
.charge-chart { position: relative; touch-action: pan-y; }
.charge-chart svg { display: block; width: 100%; height: 150px; }
.cc-head { display: flex; justify-content: space-between; gap: 10px; font-size: 11.5px; color: var(--muted); margin-bottom: 4px; flex-wrap: wrap; }
.cc-sw { display: inline-block; width: 10px; height: 3px; border-radius: 2px; margin: 0 6px 3px 0; vertical-align: middle; }
.cc-sw.kw { background: var(--accent); } .cc-sw.soc { background: var(--good, #3fae5a); }
.cc-grid { stroke: var(--hairline); stroke-width: 1; }
.cc-area { fill: var(--accent); opacity: .10; }
.cc-kw { fill: none; stroke: var(--accent); stroke-width: 2; vector-effect: non-scaling-stroke; stroke-linejoin: round; }
.cc-soc { fill: none; stroke: var(--good, #3fae5a); stroke-width: 1.8; vector-effect: non-scaling-stroke; stroke-linejoin: round; stroke-dasharray: 4 3; }
.cc-t { font: 10px var(--sans); fill: var(--muted); text-anchor: middle; }
.cc-cursor { stroke: var(--ink); stroke-opacity: .5; stroke-width: 1; vector-effect: non-scaling-stroke; }
.cc-tip { position: absolute; top: 22px; transform: translateX(-50%); background: var(--ink); color: var(--paper); font-size: 11.5px; padding: 4px 8px; border-radius: 6px; white-space: nowrap; pointer-events: none; }

/* ---- Fleet map ---- */
.fleet-wrap { display: flex; flex-direction: column; gap: 10px; height: calc(100vh - 150px); min-height: 460px; }
.fleet-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.fleet-note { margin-left: auto; }
/* Leaflet stacks its panes and controls at z-index 400–1000 inside; isolating
   the container keeps all of that below the app's bars and sheets. */
.fleet-map { flex: 1; min-height: 360px; border-radius: var(--r3); overflow: hidden; border: 1px solid var(--hairline-2); background: var(--surface-2); position: relative; isolation: isolate; z-index: 0; }
.fleet-map .leaflet-control-attribution { font-size: 10px; opacity: .8; }
.fm-icon { background: none; border: none; }
.fm-car { position: relative; transform: translate(-50%, -50%); display: flex; flex-direction: column; align-items: center; gap: 2px; pointer-events: auto; }
.fm-ring { width: 30px; height: 30px; border-radius: 50%; background: var(--surface); border: 2px solid var(--ink); display: grid; place-items: center; box-shadow: 0 2px 8px rgba(0,0,0,.25); position: relative; }
.fm-car.st-online .fm-ring { border-color: #2fae5a; } .fm-car.st-offline .fm-ring { border-color: #d33; } .fm-car.sel .fm-ring { border-width: 3px; }
.fm-arrow { width: 0; height: 0; border-left: 7px solid transparent; border-right: 7px solid transparent; border-bottom: 14px solid var(--ink); transform-origin: 50% 60%; transition: transform .4s ease; }
.fm-car.moving .fm-ring { animation: fmPulse 1.4s ease-out infinite; }
@keyframes fmPulse { 0% { box-shadow: 0 0 0 0 rgba(47,124,246,.45); } 100% { box-shadow: 0 0 0 14px rgba(47,124,246,0); } }
.fm-label { display: inline-flex; align-items: center; gap: 4px; background: var(--surface); color: var(--ink); border: 1px solid var(--hairline); border-radius: 999px; padding: 2px 7px; font-size: 11.5px; white-space: nowrap; box-shadow: 0 1px 4px rgba(0,0,0,.18); }
.fm-chip { font-style: normal; font-weight: 600; font-variant-numeric: tabular-nums; }
.fm-chip.speed { color: #2f7cf6; }
.fm-pop b { font-size: 14px; }
.fm-me { position: relative; transform: translate(-50%, -50%); width: 0; height: 0; }
.fm-me-dot { position: absolute; left: -7px; top: -7px; width: 14px; height: 14px; border-radius: 50%; background: #2f7cf6; border: 2.5px solid #fff; box-shadow: 0 0 0 4px rgba(47,124,246,.25); }
.fm-me-cone { position: absolute; left: -22px; top: -46px; width: 44px; height: 46px; background: conic-gradient(from -22deg at 50% 100%, rgba(47,124,246,.35) 0 44deg, transparent 44deg); transform-origin: 50% 100%; border-radius: 50% 50% 0 0; }
@media (max-width: 720px) { .fleet-wrap { height: calc(100dvh - 230px); min-height: 380px; } .fleet-note { flex-basis: 100%; margin-left: 0; } }
/* The top bar (and its car dropdown) always paints above view content such as the map. */
.topbar { z-index: 60; }

/* Map: choose which cars to show */
.fm-cars { position: relative; display: inline-block; }
.fm-cars .btn .chev { width: 14px; height: 14px; margin-left: 4px; vertical-align: -2px; }
.fm-cars-menu { position: absolute; top: calc(100% + 6px); left: 0; z-index: 80; min-width: 220px; background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r1); box-shadow: var(--lift-2); padding: 6px; display: flex; flex-direction: column; gap: 2px; }
.fm-opt { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 8px; cursor: pointer; font-size: 13px; }
.fm-opt:hover { background: var(--surface-2); }
.fm-opt .dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.fm-opt i.muted { margin-left: auto; font-style: normal; font-variant-numeric: tabular-nums; }

/* Strip: aux popover now hangs off the power bar's value */
.ds-power { position: relative; }
.ds-power .val[data-auxval] { cursor: pointer; text-decoration: underline dotted rgba(255,255,255,.5); text-underline-offset: 3px; }
.ds-power .ds-auxpop { display: none; position: absolute; right: 0; top: calc(100% + 8px); z-index: 5; }
.ds-power .ds-auxpop.open { display: block; }

/* HUD: portrait — smaller map, bigger type; landscape — the cluster */
.hud-tool { padding: 4px 10px; font-size: 11.5px; color: #fff; border-color: rgba(255,255,255,.35); }
.hud-tool.on { background: rgba(255,255,255,.18); }
.drive-hud .hud-map { height: clamp(130px, 22dvh, 190px); max-height: 190px; align-self: end; }
.drive-hud .hud-cell b { font-size: 26px; }
.drive-hud .hud-num { font-size: clamp(96px, 26vw, 190px); }
.hud-cluster { display: none; }
.cl-gauge { display: grid; place-items: center; min-height: 0; }
/* Sized to the row under the bar — bar, gap, padding and the phone's safe areas
   come off the viewport height — so the bottom ticks are not clipped by the
   HUD's overflow on a phone held sideways. */
.cl-svg { width: min(calc(100dvh - 86px - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)), 100%); max-width: 560px; aspect-ratio: 1; }
.cl-bg { fill: url(#clBg); stroke: rgba(255,255,255,.08); stroke-width: 2; }
.cl-track { fill: none; stroke: rgba(255,255,255,.14); stroke-width: 12; stroke-linecap: round; }
.cl-track.thin { stroke-width: 7; }
.cl-speed { fill: none; stroke: #ffffff; stroke-width: 12; stroke-linecap: round; filter: url(#clGlow); opacity: .95; }
.cl-power { fill: none; stroke-width: 7; stroke-linecap: round; filter: url(#clGlow); }
.cl-power.regen { stroke: #3ee98a; } .cl-power.drain { stroke: #ffb347; }
.cl-zero { stroke: rgba(255,255,255,.7); stroke-width: 2; }
.cl-batt { fill: none; stroke: #4fa3ff; stroke-width: 5; stroke-linecap: round; opacity: .9; }
.cl-tick { stroke: rgba(255,255,255,.55); stroke-width: 2; }
.cl-tn { font: 600 12px var(--sans); fill: rgba(255,255,255,.7); text-anchor: middle; }
.cl-needle { stroke: #fff; stroke-width: 3.5; stroke-linecap: round; filter: url(#clGlow); }
.cl-hub { fill: #fff; filter: url(#clGlow); }
#clNeedle { transition: none; }
.cl-num { font: 700 84px/1 var(--sans); fill: #fff; text-anchor: middle; letter-spacing: -.04em; font-variant-numeric: tabular-nums; }
.cl-unit { font: 500 16px var(--sans); fill: rgba(255,255,255,.75); text-anchor: middle; }
.cl-sub { font: 600 15px var(--sans); fill: rgba(255,255,255,.85); text-anchor: middle; }
.cl-pow { font: 700 20px var(--sans); fill: #ffb347; text-anchor: middle; font-variant-numeric: tabular-nums; }
.cl-pow.regen { fill: #3ee98a; }
.cl-gear { font: 700 22px var(--sans); fill: #fff; text-anchor: middle; }
.cl-panel { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 18px; align-content: start; padding: 8px 4px; overflow: auto; }
.cl-row { display: flex; flex-direction: column; gap: 2px; border-bottom: 1px solid rgba(255,255,255,.1); padding: 6px 0; }
.cl-row span { font: 600 10.5px var(--mono, ui-monospace, monospace); letter-spacing: .08em; text-transform: uppercase; color: rgba(255,255,255,.6); }
.cl-row b { font-size: 17px; font-variant-numeric: tabular-nums; }
:root[data-theme="art"] .cl-num, :root[data-theme="art"] .cl-row b { font-family: var(--display); font-weight: 400; letter-spacing: .01em; }
/* Landscape (real rotation) or the Landscape button: cluster replaces the portrait stack */
.drive-hud.rot, .drive-hud.landscape-auto { grid-template-rows: auto 1fr; }
@media (orientation: landscape) and (min-width: 640px) {
  .drive-hud { grid-template-rows: auto 1fr; grid-template-columns: minmax(0, 1fr); grid-template-areas: none; }
  .drive-hud .hud-speed, .drive-hud .hud-bars, .drive-hud .hud-cells, .drive-hud .hud-signals, .drive-hud .hud-map { display: none; }
  .drive-hud .hud-cluster { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); gap: 16px; min-height: 0; }
}
.drive-hud.rot .hud-speed, .drive-hud.rot .hud-bars, .drive-hud.rot .hud-cells, .drive-hud.rot .hud-signals, .drive-hud.rot .hud-map { display: none; }
.drive-hud.rot .hud-cluster { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); gap: 16px; min-height: 0; }
/* On a portrait phone the Landscape button turns the cluster sideways so the phone can be held rotated */
/* Centred by the self-alignment alone: the un-rotated box is wider than its
   column, and auto margins on an overflowing grid item collapse to zero and
   leave it start-aligned — the rotated gauge then hangs off the right edge. */
@media (orientation: portrait) {
  .drive-hud.rot .hud-cluster { transform: rotate(90deg); transform-origin: center; width: calc(100dvh - 120px); height: calc(100vw - 40px); align-self: center; justify-self: center; }
}
/* A wide window is not a phone held sideways. From tablet-landscape up the
   cluster is capped and centred under a full-width bar, the gauge grows with
   the shorter side of the window, and the panel beside it gets two columns
   (three from 1600 px) of larger figures whose labels have room not to wrap. */
@media (orientation: landscape) and (min-width: 1000px) {
  .drive-hud { padding-left: 40px; padding-right: 40px; }
  .drive-hud .hud-cluster, .drive-hud.rot .hud-cluster {
    width: 100%; max-width: 1500px; justify-self: center; align-items: center;
    grid-template-columns: auto minmax(0, 1fr); gap: 24px 56px; padding: 0 24px;
  }
  .cl-svg { width: min(78dvh, 40vw, 680px); max-width: none; }
  .cl-panel { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 32px; max-height: 100%; padding: 8px 0; }
  .cl-row { padding: 8px 0; }
  .cl-row span { font-size: 11.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .cl-row b { font-size: 22px; }
}
@media (orientation: landscape) and (min-width: 1600px) {
  .cl-panel { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Stationary drain card — ring + headline + tiles + rows. */
.drain-hero { display: grid; grid-template-columns: 140px 1fr; gap: 18px 22px; align-items: center; }
.drain-ring { flex: none; }
.drain-ring .dr-pct { font: 700 26px/1 var(--sans); fill: var(--ink); letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.drain-ring .dr-lab { font: 600 9px/1 var(--sans); fill: var(--faint); letter-spacing: .14em; }
.drain-total { font: 700 40px/1 var(--sans); color: var(--ink); letter-spacing: -.04em; font-variant-numeric: tabular-nums; }
.drain-total .u, .drain-tiles .tile .u { font-size: .5em; font-weight: 600; color: var(--muted); margin-left: 5px; letter-spacing: 0; }
.drain-sub { font-size: 12.5px; color: var(--muted); margin: 6px 0 12px; }
.drain-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); gap: 2px;
  border: 1px solid var(--hairline-2); border-radius: var(--r3); background: var(--hairline-2); overflow: hidden; }
.drain-tiles .tile { background: var(--surface); padding: 11px 13px; }
.drain-tiles .tile .v { font: 600 19px/1 var(--sans); color: var(--ink); letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.drain-tiles .tile .l { font: 600 10px/1.3 var(--sans); color: var(--faint); margin-top: 5px; text-transform: uppercase; letter-spacing: .08em; }
.drain-rows { display: flex; flex-direction: column; gap: 7px; margin-top: 16px; }
.drain-row { display: grid; grid-template-columns: 12px minmax(96px, .9fr) 2fr auto 44px; gap: 10px; align-items: center; font-size: 12.5px; }
.drain-row .sw { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.drain-row b { font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.drain-row .bar { display: block; height: 8px; border-radius: 4px; background: var(--surface-3); overflow: hidden; }
.drain-row .bar i { display: block; height: 100%; border-radius: 4px; }
.drain-row .n { font-variant-numeric: tabular-nums; color: var(--ink-2); white-space: nowrap; }
.drain-row .p { font-variant-numeric: tabular-nums; color: var(--muted); text-align: right; }
@media (max-width: 640px) {
  .drain-hero { grid-template-columns: 1fr; justify-items: center; text-align: center; }
  .drain-main { width: 100%; }
  .drain-row { grid-template-columns: 12px minmax(80px, 1fr) 1.4fr auto 40px; }
}
:root[data-theme="art"] .drain-total, :root[data-theme="art"] .drain-tiles .tile .v, :root[data-theme="art"] .drain-ring .dr-pct {
  font-family: var(--display); font-weight: 400; letter-spacing: .01em;
}
:root[data-theme="art"] .drain-row b { text-transform: uppercase; letter-spacing: .04em; font-size: 11.5px; }

/* Values keep their number and unit together whatever the column width. */
.kv dd b { white-space: nowrap; }

/* Drive / charge detail rows: the phone keeps the list; a desktop gets a grid
   of stat tiles across the full width, long values spanning two. */
.kv-tiles .kt { display: contents; }
.kv-tiles .kt:not(:first-child) dt, .kv-tiles .kt:not(:first-child) dd { border-top: 1px solid var(--hairline-2); }
@media (min-width: 900px) {
  .log-detail .kv-tiles { max-width: none; }
  .kv-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 2px;
    border: 1px solid var(--hairline-2); border-radius: var(--r3); background: var(--hairline-2); overflow: hidden; }
  .kv-tiles .kt { display: block; background: var(--surface); padding: 11px 13px; }
  .kv-tiles .kt.wide { grid-column: span 2; }
  .kv-tiles .kt dt, .kv-tiles .kt dd { border-top: 0 !important; padding: 0; }
  .kv-tiles .kt dt { font: 600 10px/1.3 var(--sans); color: var(--faint); text-transform: uppercase; letter-spacing: .1em; margin-bottom: 5px; white-space: normal; }
  .kv-tiles .kt dd { text-align: left; font-size: 17px; font-weight: 600; letter-spacing: -.02em; line-height: 1.25; overflow-wrap: break-word; }
  .kv-tiles .kt dd .muted.small, .kv-tiles .kt dd .small { font-size: 12px; font-weight: 500; letter-spacing: 0; }
  .kv-tiles .kt dd .placelink, .kv-tiles .kt dd a { font-size: 14px; font-weight: 600; }
}

/* Car dropdown (phones): each row says what the car is doing. */
.vpill .vstat { display: none; margin-left: auto; padding-left: 10px; font-size: 11px; font-weight: 600; color: var(--muted); letter-spacing: .02em; white-space: nowrap; }
.vstrip.compact .vpills .vpill .vstat { display: inline; }
.vstrip.compact .vpills .vpill { width: 100%; }
.vpill .vstat.driving { color: var(--accent); }
.vpill .vstat.charging { color: var(--good-ink, var(--good)); }
.vpill .vstat.antimold { color: var(--accent); }
.vpill.active .vstat { color: inherit; opacity: .8; }

/* More sheet: signed-in user and sign out, since the sidebar is out of reach on a phone. */
.sheet-user { display: flex; align-items: center; gap: 12px; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--hairline-2); font-size: 12.5px; color: var(--muted); }
.sheet-user .su-email { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sheet-user .bug-link { font-size: 12px; color: var(--muted); }

/* Analyst panel on phones: a sheet that tracks the visual viewport (see aiFit). */
@media (max-width: 720px) {
  .ai-panel { left: 8px; right: 8px; top: 8px; bottom: auto; width: auto; height: calc(100dvh - 16px); border-radius: 18px; }
  .ai-form input { font-size: 16px; }   /* ≥16px stops iOS zooming the page on focus */
}

/* AdminStats */
.as-spark { margin-top: 6px; }
.as-spark svg, .as-bars { display: block; max-width: 100%; height: 34px; }
.as-grid { margin-top: 14px; gap: 18px 24px; }
.as-rank { display: flex; flex-direction: column; gap: 5px; }
.as-row { display: grid; grid-template-columns: minmax(90px, 1.2fr) 2fr 52px; gap: 10px; align-items: center; font-size: 12.5px; }
.as-row .k { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--ink-2); }
.as-row .bar { height: 7px; border-radius: 4px; background: var(--surface-3); overflow: hidden; }
.as-row .bar i { display: block; height: 100%; background: var(--accent); border-radius: 4px; }
.as-row .n { text-align: right; font-variant-numeric: tabular-nums; color: var(--ink); font-weight: 600; }
.as-table-wrap { overflow-x: auto; margin-top: 4px; }
/* AdminStats → Access log */
.as-tabs { margin-right: 6px; }
.al-filters { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin: 0 0 12px; }
.al-filters input[type="search"] { flex: 1 1 240px; min-width: 180px; }
.al-filters select { max-width: 200px; }
.al-filters .chip.on { cursor: pointer; }
.al-table td { padding: 7px 8px; vertical-align: top; border-bottom: 1px solid var(--hairline-2); }
.al-table td.mono { font-family: var(--mono); font-size: 12px; }
.al-table td.nowrap, .al-table th.nowrap { white-space: nowrap; }
.al-table td.al-path { max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.al-table a[data-alip] { color: inherit; text-decoration: none; border-bottom: 1px dotted var(--faint); }
/* On a phone the eight columns become a card: when · status / request / IP · where / who. */
@media (max-width: 700px) {
  .al-table thead { display: none; }
  .al-table tr { display: grid; grid-template-columns: 1fr auto; gap: 3px 10px; padding: 9px 0; border-bottom: 1px solid var(--hairline-2); }
  .al-table td { display: block; padding: 0; border: 0; }
  .al-table td:nth-child(1) { grid-column: 1; grid-row: 1; }
  .al-table td:nth-child(7) { grid-column: 2; grid-row: 1; justify-self: end; }
  .al-table td:nth-child(6) { grid-column: 1 / -1; grid-row: 2; max-width: none; white-space: normal; word-break: break-all; }
  .al-table td:nth-child(2) { grid-column: 1; grid-row: 3; }
  .al-table td:nth-child(3) { grid-column: 2; grid-row: 3; justify-self: end; text-align: right; }
  .al-table td:nth-child(4) { grid-column: 1 / -1; grid-row: 4; }
  .al-table td:nth-child(5), .al-table td:nth-child(8) { display: none; }
  .al-filters select { max-width: none; flex: 1 1 45%; }
}
.as-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.as-table th { text-align: left; font: 600 10px var(--sans); text-transform: uppercase; letter-spacing: .08em; color: var(--faint); padding: 6px 8px; border-bottom: 1px solid var(--hairline-2); white-space: nowrap; }
.as-table td { padding: 8px; border-bottom: 1px solid var(--hairline-2); vertical-align: top; }
.as-table .r { text-align: right; font-variant-numeric: tabular-nums; }
.as-table tr.off td { opacity: .55; }
.as-table .chip { display: inline-block; font-size: 10.5px; padding: 2px 7px; border-radius: 999px; background: var(--surface-3); color: var(--ink-2); margin: 1px 0; }
.summary-row .cell .as-sub { font-size: 11px; color: var(--muted); margin-top: 4px; }
@media (max-width: 640px) { .as-row { grid-template-columns: minmax(70px, 1fr) 1.4fr 48px; } }

.as-axis { display: flex; justify-content: space-between; font-size: 9.5px; color: var(--faint); margin-top: 2px; letter-spacing: .04em; }
.as-table td .dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 4px; vertical-align: middle; }
.as-table .badge { font-size: 9.5px; padding: 1px 6px; }

/* AdminStats: motion and hover */
.as-bars rect { transform-box: fill-box; transform-origin: bottom; animation: asGrow .5s cubic-bezier(.2,.8,.2,1) both; transition: opacity .15s, filter .15s; opacity: .55; }
.as-bars rect.last { opacity: 1; }
.as-bars:hover rect { opacity: .3; }
.as-bars rect:hover { opacity: 1; filter: brightness(1.15); }
@keyframes asGrow { from { transform: scaleY(0); } }
.as-row .bar i { transform-origin: left; animation: asGrowX .6s cubic-bezier(.2,.8,.2,1) both; transition: filter .15s; }
@keyframes asGrowX { from { transform: scaleX(0); } }
.as-row { border-radius: 6px; padding: 1px 4px; margin: 0 -4px; transition: background .15s; }
.as-row:hover { background: var(--surface-2); }
.as-row:hover .bar i { filter: brightness(1.12); }
.summary-row .cell { transition: transform .18s ease, box-shadow .18s ease; }
#asBody .summary-row .cell:hover { transform: translateY(-2px); box-shadow: var(--lift-2); position: relative; z-index: 1; }
.as-table th[data-astable] { cursor: pointer; user-select: none; transition: color .15s; }
.as-table th[data-astable]:hover, .as-table th.on { color: var(--ink); }
.as-table th .arrow { display: inline-block; width: 12px; margin-left: 3px; color: var(--accent); }
.as-table tbody tr { transition: background .12s; }
.as-table tbody tr:hover { background: var(--surface-2); }
.as-tip { position: fixed; left: 0; top: 0; z-index: 200; pointer-events: none; background: var(--ink); color: var(--paper); font: 600 11.5px var(--sans); padding: 5px 9px; border-radius: 8px; box-shadow: var(--lift-2); white-space: nowrap; transition: transform .06s linear; }
.as-tip[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) { .as-bars rect, .as-row .bar i { animation: none; } }

/* AdminStats layout: the ranked lists sit in a real grid, tiles keep a clean ground, chips never break. */
#asBody .as-grid, #asBody .grid2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px 32px; margin-top: 18px; align-items: start; }
#asBody .g-lab { font: 600 10px/1 var(--sans); text-transform: uppercase; letter-spacing: .1em; color: var(--faint); margin: 0 0 9px; }
#asBody .summary-row { background: var(--surface); gap: 2px; }
#asBody .summary-row .cell { box-shadow: 0 0 0 1px var(--hairline-2); border-radius: 2px; }
#asBody .as-bars { width: 100%; }
.as-table .chip { white-space: nowrap; }
.as-table td { white-space: nowrap; }
.as-table td:first-child { white-space: normal; min-width: 150px; }
@media (max-width: 720px) { #asBody .as-grid, #asBody .grid2 { grid-template-columns: 1fr; } }

.as-row { grid-template-columns: minmax(150px, 1.6fr) 1.6fr 56px; }
.as-row .k { white-space: normal; overflow-wrap: anywhere; line-height: 1.25; }
#asBody .summary-row + .summary-row { margin-top: 12px; }

/* Space — orbital telemetry. Shared type, colors and geometry with Theme.swift.
   Font derivatives retain their OFL licenses in vendor/fonts/space. */
:root[data-theme="space"] {
  color-scheme: dark;
  --sans: 'WL Oxanium', 'Space Grotesk', sans-serif;
  --display: 'WL Orbit', 'WL Oxanium', sans-serif;
  --paper: #080e1c; --surface: #101b2f; --surface-2: #17253d; --surface-3: #22344f;
  --veil: rgba(8, 14, 28, .90);
  --ink: #edf4ff; --ink-2: #c0cde2; --muted: #9aadc7; --faint: #899db9;
  --hairline: rgba(191, 218, 255, .16); --hairline-2: rgba(191, 218, 255, .09);
  --grid: #243653; --accent: #72ddf7; --accent-2: #a1b3ff;
  --on-accent: #080e1c;
  --accent-wash: rgba(114, 221, 247, .10);
  --good-ink: #6cdda8; --warning-ink: #f3c674; --serious-ink: #ffac83; --critical-ink: #ff8b9c;
  --btn-fill: #edf4ff; --btn-text: #080e1c;
  --switch-on: #72ddf7; --switch-knob-on: #080e1c;
  --r1: 6px; --r2: 9px; --r3: 16px; --r4: 24px;
  --lift: 0 1px 2px rgba(0, 0, 0, .3);
  --lift-2: 0 14px 36px rgba(0, 0, 0, .24);
  --lift-3: 0 24px 80px rgba(0, 0, 0, .6);
}
:root[data-theme="space"] body {
  background: radial-gradient(ellipse at 85% 0%, #17233c 0, transparent 46%), var(--paper);
  background-attachment: fixed;
}
:root[data-theme="space"] .brand { font-family: var(--display); font-size: 13.5px; letter-spacing: .015em; }
:root[data-theme="space"] .brand .mark,
:root[data-theme="space"] .login-mark { color: #080e1c; }
:root[data-theme="space"] .sidebar { background: var(--veil); }
:root[data-theme="space"] .nav-item { letter-spacing: .025em; }
:root[data-theme="space"] .nav-item.active,
:root[data-theme="space"] .theme-switch button.on {
  color: var(--accent); background: var(--accent-wash);
}
:root[data-theme="space"] .nav-item.active::before {
  content: ''; position: absolute; left: 1px; top: calc(50% - 8px);
  width: 2px; height: 16px; border-radius: 2px; background: var(--accent);
}
:root[data-theme="space"] .card {
  position: relative;
  background: linear-gradient(145deg, #263e6061, transparent), var(--surface);
  border-color: var(--hairline);
}
:root[data-theme="space"] .card::before {
  content: ''; position: absolute; top: -1px; left: 20px;
  width: 30px; height: 2px; border-radius: 2px; background: #72ddf7a6;
  pointer-events: none;
}
:root[data-theme="space"] .hero-info h1,
:root[data-theme="space"] .stat .v,
:root[data-theme="space"] .ring .pct,
:root[data-theme="space"] .gauge .g-num,
:root[data-theme="space"] .ds-speed,
:root[data-theme="space"] .ds-stat .v,
:root[data-theme="space"] .rt-eta b,
:root[data-theme="space"] .replay-readout .v,
:root[data-theme="space"] .empty h2,
:root[data-theme="space"] .login-card h1 {
  font-family: var(--display); letter-spacing: 0;
}
:root[data-theme="space"] .hero-info h1 { font-size: 32px; line-height: 1.15; overflow-wrap: anywhere; }
:root[data-theme="space"] .stat .v.small { letter-spacing: 0; }
.space-eyebrow { display: none; }
:root[data-theme="space"] .space-eyebrow {
  display: flex; align-items: center; gap: 7px; margin-bottom: 3px;
  font: 600 9px/1.25 var(--sans); letter-spacing: .2em; color: var(--accent);
}
:root[data-theme="space"] .space-eyebrow::before { content: '✦'; font-size: 9px; }
:root[data-theme="space"] .hero {
  position: relative; isolation: isolate;
  border: 1px solid var(--hairline); border-radius: var(--r4);
  padding: 18px 20px 24px;
  background: url('/img/space-orbit.svg') center / 100% 100% no-repeat, var(--paper);
}
:root[data-theme="space"] .hero::before,
:root[data-theme="space"] .hero::after {
  content: ''; position: absolute; pointer-events: none; width: 8px; height: 8px;
  background: linear-gradient(#72ddf766, #72ddf766) center / 1px 8px no-repeat,
              linear-gradient(#72ddf766, #72ddf766) center / 8px 1px no-repeat;
}
:root[data-theme="space"] .hero::before { top: 14px; left: 16px; }
:root[data-theme="space"] .hero::after { bottom: 14px; right: 16px; }
:root[data-theme="space"] .hero .car-wrap::before {
  content: ''; pointer-events: none; position: absolute;
  width: 84%; height: 9%; left: 6%; bottom: 20%;
  border: 1px solid #72ddf74d; border-radius: 50%;
  background: radial-gradient(ellipse, #72ddf712, transparent 70%);
  box-shadow: 0 0 18px #72ddf70b;
}
.hero .car-shot { transform: scale(1.16); }
:root[data-theme="space"] .hero .ring { filter: drop-shadow(0 0 20px #72ddf714); }
:root[data-theme="space"] .ring::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%; pointer-events: none;
  border: 1.5px dotted #72ddf75c;
}
:root[data-theme="space"] .ring .pct { font-size: calc(var(--ring) * .175); }
:root[data-theme="space"] .hero .car-shot {
  filter: drop-shadow(0 12px 11px #000000a6) drop-shadow(0 -2px 12px #aacfff18);
}
:root[data-theme="space"] .btn,
:root[data-theme="space"] .chip { letter-spacing: .025em; }
:root[data-theme="space"] :is(.btn, .chip, .nav-item, .cmd):focus-visible {
  outline: 2px solid var(--accent); outline-offset: 3px;
}
:root[data-theme="space"] .map-embed { filter: brightness(.82) saturate(.75); }

/* Bright accents belong on instruments; the live readings need a dark panel. */
:root[data-theme="space"] .drive-strip {
  background: linear-gradient(180deg, #112a3d, #182240);
  color: var(--ink); border: 1px solid #72ddf766;
}
:root[data-theme="space"] .drive-strip :is(.ds-route, .ds-stat .l, .ds-power .lab) {
  color: var(--ink-2); opacity: 1;
}
:root[data-theme="space"] .drive-strip :is(.ds-chip:not(.brake):not(.hot), .ds-split .seg) {
  background: #101b2f; color: var(--ink); opacity: 1;
}
:root[data-theme="space"] .drive-strip .ds-split .seg em { color: var(--ink-2); opacity: 1; }
:root[data-theme="space"] .drive-strip .ds-split .seg.regen { color: var(--good-ink); }
:root[data-theme="space"] .drive-strip .ds-chip.brake { color: #080e1c; }
:root[data-theme="space"] .flow .node .l { color: var(--ink-2); }
/* Space cockpit, in portrait, landscape and browser fullscreen. Art and the
   other HUD palettes retain their original design. Decorations stay static. */
:root[data-theme="space"] .drive-hud {
  background: linear-gradient(90deg, #080e1c33, #080e1c99), url('/img/space-orbit.svg') center/cover, #080e1c;
  color: var(--ink);
}
:root[data-theme="space"] :is(.hud-num, .cl-num, .hud-cell b) { font-family: var(--display); letter-spacing: 0; }
:root[data-theme="space"] :is(.hud-cell, .cl-row) {
  background: #101b2fe6; border: 1px solid #72ddf733; border-radius: 12px;
}
:root[data-theme="space"] .cl-row { padding: 8px 12px; min-width: 0; }
:root[data-theme="space"] .cl-row span { font-family: var(--sans); color: var(--muted); }
:root[data-theme="space"] .cl-bg { fill: #0c1729; stroke: #72ddf744; }
:root[data-theme="space"] .cl-track { stroke: #bfdaff26; }
:root[data-theme="space"] :is(.cl-speed, .cl-needle) { stroke: var(--accent); }
:root[data-theme="space"] .cl-hub { fill: var(--accent); }
:root[data-theme="space"] .cl-power.drain { stroke: var(--accent-2); }
:root[data-theme="space"] .cl-pow { fill: var(--accent-2); }
:root[data-theme="space"] .cl-power.regen { stroke: var(--good-ink); }
:root[data-theme="space"] .cl-pow.regen { fill: var(--good-ink); }
:root[data-theme="space"] .cl-batt { stroke: var(--accent); }
:root[data-theme="space"] :is(.cl-tn, .cl-unit, .cl-sub) { fill: var(--ink-2); }
:root[data-theme="space"] :is(.hud-close, .hud-tool) { background: #101b2fe6; border-color: #72ddf744; color: var(--ink); }
:root[data-theme="space"] .hud-tool.on { background: var(--accent); color: #080e1c; }
:root[data-theme="space"] .drive-hud .ds-power .fill.drain { background: linear-gradient(90deg, var(--accent), var(--accent-2)); }
:root[data-theme="space"] .drive-hud .ds-power .fill.regen { background: linear-gradient(270deg, var(--accent), var(--good-ink)); }
:root[data-theme="space"] .login-card {
  background: url('/img/space-orbit.svg') top / auto 100% no-repeat, var(--surface);
  border-color: var(--hairline); box-shadow: var(--lift-3);
}
/* Keep all five appearances comfortably tappable in the narrow sidebar. */
.theme-switch { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); }
.theme-switch button { min-width: 0; padding-left: 2px; padding-right: 2px; font-size: 10px; }
@media (max-width: 940px) {
  :root[data-theme="space"] .hero { padding: 16px 14px 20px; }
  :root[data-theme="space"] .hero .car-shot { width: min(350px, calc(100vw - 212px)); }
  :root[data-theme="space"] .hero-info h1 { font-size: 30px; }
}
@media (max-width: 520px) {
  :root[data-theme="space"] .hero .car-shot { width: min(285px, calc(100vw - 168px)); }
  :root[data-theme="space"] .hero-info h1 { font-size: 26px; }
}

/* Two compositor-driven layers: stars drift over a slowly moving nebula.
   Hidden tabs pause; Reduce Motion retains a still star field. */
:root[data-theme="space"] body { isolation: isolate; }
:root[data-theme="space"] body::before,
:root[data-theme="space"] body::after {
  content: ''; position: fixed; inset: -32px; z-index: -1; pointer-events: none;
}
:root[data-theme="space"] body::before {
  background: radial-gradient(ellipse at 80% 24%, #43518c52, transparent 65%),
              radial-gradient(ellipse at 10% 80%, #1b5a7040, transparent 60%);
  animation: spaceNebula 48s ease-in-out infinite alternate;
}
:root[data-theme="space"] body::after {
  background: radial-gradient(circle at 19px 47px, #edf4ff80 0 .7px, transparent 1.3px) 0 0 / 173px 211px,
              radial-gradient(circle at 83px 131px, #a1b3ff70 0 .8px, transparent 1.5px) 0 0 / 307px 283px,
              radial-gradient(circle at 137px 17px, #edf4ff50 0 .5px, transparent 1px) 0 0 / 227px 157px;
  animation: spaceStars 36s ease-in-out infinite alternate;
}
@keyframes spaceNebula { to { transform: translate3d(-24px, 12px, 0); opacity: .65; } }
@keyframes spaceStars { to { transform: translate3d(10px, -12px, 0); opacity: .5; } }
:root[data-page-hidden="true"] body::before,
:root[data-page-hidden="true"] body::after,
:root[data-page-scrolling="true"] body::before,
:root[data-page-scrolling="true"] body::after { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
  :root[data-theme="space"] body::before,
  :root[data-theme="space"] body::after { animation: none !important; }
}

/* Main pages share the selected theme and a consistent hierarchy. */
.page-intro { display:flex; align-items:flex-start; gap:16px; margin:0 0 24px; }
.page-intro-icon { display:grid; place-items:center; width:50px; height:50px; flex:0 0 50px; color:var(--accent); background:var(--accent-wash); border-radius:16px; }
.page-intro-icon svg { width:26px; height:26px; }
.page-intro h1 { font:700 clamp(25px,3vw,32px)/1.2 var(--sans); color:var(--ink); margin:0 0 8px; }
.page-intro p { font-size:14px; line-height:1.5; color:var(--ink-2); margin:0; }
.page-categories { display:flex; flex-wrap:wrap; gap:8px; margin:0 0 20px; }
.page-categories .chip { min-height:40px; padding:10px 15px; font-size:13px; }
.page-filters { margin-bottom:16px; }
.page-filters .controls:last-child { margin-bottom:0; align-items:flex-start; gap:24px; }
.filter-label { display:block; color:var(--ink-2); font-size:12px; font-weight:600; margin-bottom:8px; }
.page-insights { background:var(--surface); border:1px solid var(--hairline-2); border-radius:18px; padding:18px; margin:16px 0; }
.page-insights > summary { cursor:pointer; font-size:16px; font-weight:600; color:var(--ink); padding:2px 0; }
.page-insights > summary span { font-size:13px; color:var(--ink-2); font-weight:400; margin-left:12px; }
.page-insights[open] > summary { margin-bottom:18px; }
.period-routes-card { margin-top:20px; }
.period-metric { display:inline-flex; align-items:center; gap:7px; margin-left:auto; }
.period-metric select { height:28px; padding:0 26px 0 9px; font-size:12px; }
/* The map takes the wheel and the trackpad only after a click; the ring says so. */
.period-routes-map.zoom-armed { box-shadow:0 0 0 2px var(--accent); }
/* One drive picked out on the period map: what it was, then step / details / back to all. */
.period-focus { display:flex; align-items:center; gap:10px 14px; flex-wrap:wrap; margin-top:10px; padding:10px 44px 10px 12px;
  border:1px solid var(--hairline); border-radius:var(--r2); background:var(--surface); position:relative; }
.period-focus [data-pfclear] { position:absolute; top:50%; right:6px; transform:translateY(-50%); }
.period-focus .pf-text { flex:1 1 260px; min-width:0; }
.period-focus .pf-text .title { color:var(--ink); font-weight:600; font-size:14px; }
.period-focus .pf-text .title .arrow { color:var(--faint); font-weight:400; }
.period-focus .pf-actions { display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.period-focus .pf-count { min-width:52px; text-align:center; font-variant-numeric:tabular-nums; }
.leaflet-tooltip.route-end-label { padding:3px 8px; border:0; border-radius:10px; background:#fff; color:#152333;
  font:600 11px/1.3 system-ui,sans-serif; box-shadow:0 1px 4px rgba(0,0,0,.28); }

/* Picking which drives count. Off until "Select"; the circles then lead each row. */
.drive-pick { display:none; width:20px; height:20px; padding:0; border-radius:50%; cursor:pointer;
  border:1.5px solid var(--faint); background:transparent; position:relative; flex:none; }
.drive-pick[aria-pressed="true"] { background:var(--accent); border-color:var(--accent); }
.drive-pick[aria-pressed="true"]::after { content:''; position:absolute; left:5.5px; top:2px; width:5px; height:10px;
  border:solid #fff; border-width:0 2px 2px 0; transform:rotate(45deg); }
.drive-pick:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
.drives-card.picking .drive-pick, .drives-card.subset .drive-pick { display:block; }
.drives-card.picking .log-row, .drives-card.subset .log-row { grid-template-columns:20px 92px 1fr auto; }
.drives-card.subset:not(.picking) .log-row.unpicked { opacity:.45; }
/* Bold and in the accent, so it reads as an action and not as part of the count. */
.drives-card h3 .drives-select { margin-left:10px; color:var(--accent); font-weight:700; }
.drives-pickbar { display:flex; align-items:center; gap:8px; padding:4px 4px 10px; }
.drives-pickbar .spacer { flex:1; }
.drives-scope { margin:-6px 0 14px; display:flex; align-items:center; gap:4px; flex-wrap:wrap; }
@media (max-width: 600px) {
  .period-focus [data-pfclear] { top:6px; transform:none; }
  /* The key sits above the map credit rather than under it. */
  .period-routes-map .leaflet-bottom.leaflet-left .map-consumption-key { margin-bottom:24px; }
}
@media (max-width: 940px) {
  .drives-card.picking .log-row, .drives-card.subset .log-row { grid-template-columns: 20px 72px 1fr auto; gap: 10px; }
  .period-metric { margin-left:0; }
}
.period-routes-map { height:420px; border-radius:16px; overflow:hidden; background:var(--surface-2); isolation:isolate; }
.period-routes-map.expanded { height:75vh; }
.route-popup { color:var(--ink); font:13px/1.4 var(--sans); }
.route-popup-drive + .route-popup-drive { border-top:1px solid var(--hairline); margin-top:16px; padding-top:16px; }
.route-popup-drive > p { color:var(--ink-2); margin:8px 0; }
.route-popup-stats { display:grid; grid-template-columns:1fr 1fr; gap:10px 16px; margin:12px 0; }
.route-popup-stats dt { font-size:11px; color:var(--ink-2); }
.route-popup-stats dd { margin:2px 0 0; font-weight:600; }
.route-popup-actions { display:flex; gap:8px; flex-wrap:wrap; }
.period-routes-map .leaflet-popup-content-wrapper, .period-routes-map .leaflet-popup-tip { background:var(--surface); color:var(--ink); }
.log-detail { scroll-margin-top:90px; }
.period-routes-map .empty { height:100%; display:flex; flex-direction:column; justify-content:center; }
#view[data-viewname="controls"] #ctlSections { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr)); grid-auto-rows: 1px; grid-auto-flow: row dense; column-gap: 20px; align-items: start; }
#view[data-viewname="controls"] .ctl-section { display: flow-root; margin: 0; padding-bottom: 20px; min-width: 0; }
#view[data-viewname="controls"] .ctl-section[hidden] { display: none; }
#ctlNone { grid-column: 1 / -1; grid-row-end: span 120; }
#view[data-viewname="controls"] .ctl-row { min-height:52px; font-size:14px; }
#view[data-viewname="controls"] .ctl-section h4 { color:var(--ink); font-size:16px; }
#view[data-viewname="automations"] .auto-card { padding:22px; border-radius:20px; }
#view[data-viewname="automations"] .auto-head .nm { font-size:17px; }
#view[data-viewname="automations"] .auto-sentence { font-size:14px; line-height:1.9; margin-top:14px; }
#view[data-viewname="automations"] .auto-meta { color:var(--ink-2); font-size:12px; margin-top:16px; }
#view[data-viewname="automations"] .auto-off { opacity:1; border-style:dashed; }
#view[data-viewname="drives"] .log-row, #view[data-viewname="charges"] .log-row { padding-top:18px; padding-bottom:18px; }
#view[data-viewname="drives"] .log-row .title, #view[data-viewname="charges"] .log-row .title { font-size:15px; line-height:1.5; }
#view[data-viewname="alerts"] .feed-item { padding-top:18px; padding-bottom:18px; }
@media(max-width:600px) {
  .period-routes-map { height:330px; }
  .page-insights > summary span { display:block; margin:6px 0 0; }
  /* On a phone the bottom bar already names the page: a one-line heading
     leaves the screen to the page itself. */
  .page-intro { gap:12px; align-items:center; margin:0 0 14px; }
  .page-intro-icon { width:34px; height:34px; flex-basis:34px; border-radius:11px; }
  .page-intro-icon svg { width:19px; height:19px; }
  .page-intro h1 { font-size:22px; margin:0; }
  .page-intro p { display:none; }
}

/* Controls: the car stays, sized so the tiles start on the first screen. */
#view[data-viewname="controls"] .controls-hero { margin-bottom:16px; }
#view[data-viewname="controls"] .controls-hero .hero-info h1 { font-size:26px; }
@media(max-width:600px) {
  #view[data-viewname="controls"] .page-intro,
  #view[data-viewname="controls"] .controls-hero .space-eyebrow { display:none; }
  #view[data-viewname="controls"] .controls-hero { flex-wrap:nowrap; gap:12px; padding:10px 14px 12px; margin-bottom:12px; }
  #view[data-viewname="controls"] .controls-hero .car-shot { width:min(150px, 38vw); margin:-8px -6px -10px -8px; }
  #view[data-viewname="controls"] .controls-hero .hero-info { flex-basis:auto; gap:6px; }
  #view[data-viewname="controls"] .controls-hero .hero-info h1 { font-size:20px; }
}

/* Detailed vehicle maps: the same front-up geometry on Dashboard and Diagnosis.
   Numeric labels live outside SVG so phone layouts keep readable text. */
.pressure-car-map { display:grid; grid-template-columns:minmax(0,1fr) 118px minmax(0,1fr); grid-template-rows:auto 1fr 1fr; align-items:center; gap:6px 5px; max-width:480px; width:100%; margin:8px auto; }
.car-front-label { color:var(--muted); font:500 10px var(--sans); text-align:center; letter-spacing:.08em; }
.pressure-car-map > .car-front-label { grid-column:1/-1; grid-row:1; }
.pressure-car { grid-column:2; grid-row:2/4; width:100%; height:auto; display:block; }
.wheel-fl { grid-column:1; grid-row:2; }.wheel-fr { grid-column:3; grid-row:2; }
.wheel-rl { grid-column:1; grid-row:3; }.wheel-rr { grid-column:3; grid-row:3; }
.wheel-reading { display:flex; flex-direction:column; align-items:center; gap:4px; text-align:center; min-width:0; color:var(--ink); font-variant-numeric:tabular-nums; }
.wheel-name { font:500 11px var(--sans); color:var(--muted); }
.wheel-pressure { display:flex; align-items:baseline; justify-content:center; gap:3px; }
.wheel-pressure b { font:600 20px var(--sans); letter-spacing:-.04em; }
.wheel-pressure small { font:400 10px var(--sans); color:var(--muted); }
.wheel-difference { font:400 10px/1.4 var(--sans); color:var(--muted); overflow-wrap:anywhere; }
.wheel-verdict { font:600 10px var(--sans); border-radius:20px; padding:3px 6px; background:color-mix(in srgb,var(--warning) 17%,transparent); }
.wheel-reading.crit .wheel-verdict { background:color-mix(in srgb,var(--critical) 17%,transparent); }
.wheel-status { fill:var(--faint); }.wheel-status.good { fill:var(--good); }.wheel-status.warn { fill:var(--warning); }.wheel-status.crit { fill:var(--critical); }
.car-map-reference { color:var(--muted); font:400 11px/1.5 var(--sans); text-align:center; margin:8px 0 0; }
.heat-none { --heat:var(--faint); }.heat-cold { --heat:#5aa9ff; }.heat-ok { --heat:var(--good); }.heat-warm { --heat:var(--warning); }.heat-hot { --heat:var(--critical); }
.car-component .heat-fill { fill:var(--heat); fill-opacity:.72; stroke:var(--heat); stroke-width:1.5; filter:drop-shadow(0 0 7px color-mix(in srgb,var(--heat) 35%,transparent)); }
.car-component path { fill:none; stroke:#00000040; stroke-width:1; }
:root[data-theme="art"] .wheel-pressure b { font-family:var(--display); font-weight:400; letter-spacing:.01em; }
@media (max-width:360px) { .pressure-car-map { grid-template-columns:minmax(0,1fr) 100px minmax(0,1fr); } }

.tmap-card { container-type:inline-size; }
.thermal-on-car { width:250px; max-width:100%; aspect-ratio:1/2; margin:8px auto 16px; position:relative; }
.thermal-on-car svg { width:100%; height:100%; display:block; }
.thermal-on-car.wide { display:none; }
.thermal-inline-reading { position:absolute; transform:translate(-50%,-50%); display:flex; flex-direction:column; align-items:center; gap:2px; padding:5px 3px; border:4px solid transparent; background:#0b1424db; background-clip:padding-box; border-radius:9px; color:#fff; font-variant-numeric:tabular-nums; text-align:center; }
.thermal-inline-reading span { font:500 10px/1.2 var(--sans); }
.thermal-inline-reading strong { font:600 16px/1.2 var(--sans); letter-spacing:-.03em; white-space:nowrap; }
.thermal-inline-reading small { font:400 9px/1.25 var(--sans); color:#d9e1ed; white-space:nowrap; }
.thermal-inline-reading.pack span { font-size:12px; }
.thermal-inline-reading.pack strong { font-size:25px; }
:root[data-theme="art"] .thermal-inline-reading strong { font-family:var(--display); font-weight:400; letter-spacing:.01em; }
@container (min-width:560px) {
 .thermal-on-car.portrait { display:none; }
 .thermal-on-car.wide { display:block; width:560px; aspect-ratio:2/1; }
 .thermal-inline-reading span { font-size:11px; }
 .thermal-inline-reading strong { font-size:18px; }
 .thermal-inline-reading small { font-size:10px; }
 .thermal-inline-reading.pack strong { font-size:28px; }
}
/* Admin usage: the same report in Settings and AdminStats, readable on phones. */
.cost-report { min-width:0; margin:16px 0; }
.cost-metrics { display:grid; grid-template-columns:repeat(auto-fit,minmax(140px,1fr)); gap:12px; margin:16px 0; }
.cost-metrics > div { padding:12px; border:1px solid var(--hairline); border-radius:12px; }
.cost-metrics strong { display:block; font-size:21px; color:var(--ink); font-variant-numeric:tabular-nums; }
.cost-metrics span,.cost-person span { display:block; font-size:11px; color:var(--muted); margin-top:4px; }
.cost-report summary { cursor:pointer; font-weight:600; padding:12px 0; }
.cost-person { display:grid; grid-template-columns:minmax(130px,1.8fr) repeat(2,minmax(60px,.6fr)) minmax(110px,1fr); gap:12px; padding:13px 0; border-bottom:1px solid var(--hairline); align-items:center; font-size:13px; }
.cost-person > div:not(:first-child) { text-align:right; font-variant-numeric:tabular-nums; }
.cost-who { min-width:0; overflow-wrap:anywhere; }
.cost-who small { display:block; margin-top:4px; color:var(--muted); line-height:1.5; }
.cost-person:last-child { font-weight:600; }
.cost-warning { color:var(--warning); font-size:13px; }
.cost-rates { flex-wrap:wrap; }
.cost-rates .f { flex:1 1 145px; min-width:0; }
@media (max-width:600px) {
  .cost-person { grid-template-columns:1fr 1fr 1.4fr; gap:10px; }
  .cost-who { grid-column:1 / -1; }
  .cost-person > div:not(:first-child) { text-align:left; }
  .cost-metrics { grid-template-columns:1fr 1fr; gap:8px; }
  .cost-metrics strong { font-size:18px; overflow-wrap:anywhere; }
  .cost-report .ai-usage-head { align-items:flex-start; flex-direction:column; gap:6px; }
}

/* One consumption key for tiles, replay and the interactive route overview. */
.route-legend { display:flex; align-items:center; gap:6px; padding:6px 8px; border-radius:8px;
  background:rgba(255,255,255,.94); color:#152333; font:500 10px/1.2 system-ui,sans-serif; }
.route-legend b { font-size:10px; white-space:nowrap; }
.route-legend span { flex:1; text-align:center; white-space:nowrap; }
.route-legend i { display:block; height:4px; border-radius:3px; margin-bottom:3px; min-width:22px; }
.tmap > .route-legend { position:absolute; bottom:22px; left:8px; right:8px; z-index:2; }
.map-consumption-key .route-legend { max-width:calc(100vw - 72px); }
.tmap-follow { position:absolute; top:8px; left:8px; z-index:2; border:0; border-radius:20px;
  padding:8px 10px; background:rgba(255,255,255,.94); color:#152333; font:600 11px system-ui,sans-serif; cursor:pointer; }
@media (orientation:portrait) {
  .drive-hud { overflow-y:auto; grid-template-rows:auto; grid-auto-rows:max-content; }
}

/* Let a horizontal gesture on the phone bar navigate; vertical gestures keep scrolling. */
@media (max-width: 940px) {
  .sidebar { touch-action: pan-y; }
}

/* ==========================================================================
   Settings → Import & export
   Two panes side by side (stacked on a phone). While a job runs a pane shows
   one progress ring that eases between readings, with the stages as a
   ticked list; a finished job draws its check mark. All of the motion is
   decoration and stops under prefers-reduced-motion.
   ========================================================================== */
.tx-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 12px; }
.tx-pane {
  display: flex; flex-direction: column; gap: 12px; min-width: 0;
  padding: 16px 16px 15px; border: 1px solid var(--hairline-2); border-radius: var(--r3);
  background: var(--surface);
}
.tx-pane > p, .tx-pane .form-row { margin: 0; }
.tx-pane.tx-enter > * { animation: txIn .34s cubic-bezier(.32,.72,0,1) backwards; }
.tx-pane.tx-enter > :nth-child(2) { animation-delay: 45ms; }
.tx-pane.tx-enter > :nth-child(3) { animation-delay: 90ms; }
.tx-pane.tx-enter > :nth-child(n+4) { animation-delay: 130ms; }
@keyframes txIn { from { opacity: 0; transform: translateY(7px); } }

.tx-pane-head { display: flex; align-items: center; gap: 12px; }
.tx-pane-head strong { display: block; font-size: 14.5px; font-weight: 600; color: var(--ink); letter-spacing: -.01em; }
.tx-pane-head div > span { display: block; font-size: 12px; color: var(--muted); margin-top: 1px; }
.tx-pane-icon {
  width: 40px; height: 40px; flex: none; border-radius: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-wash); color: var(--accent);
}

/* What is being imported: a compact segmented choice and one line of help. */
.tx-src { display: grid; gap: 7px; }
.tx-src .seg { align-self: flex-start; justify-self: start; max-width: 100%; }
.tx-src .seg button { padding: 5px 11px; font-size: 12px; white-space: nowrap; }
.tx-src p { margin: 0; min-height: 2.9em; }
.tx-fine { font-size: 11.5px; }

/* The drop zone. */
.tx-drop {
  display: flex; align-items: center; gap: 14px; padding: 18px 16px;
  border: 1.5px dashed var(--hairline); border-radius: var(--r2); background: var(--surface-2);
  cursor: pointer; transition: border-color .15s ease, background .15s ease, transform .18s cubic-bezier(.32,.72,0,1);
}
.tx-drop:hover, .tx-drop:focus-visible { border-color: var(--accent); outline: none; }
.tx-drop:focus-visible { box-shadow: 0 0 0 3px var(--accent-wash); }
.tx-drop.over { border-color: var(--accent); border-style: solid; background: var(--accent-wash); transform: scale(1.012); }
.tx-drop-icon {
  width: 46px; height: 46px; flex: none; border-radius: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface); color: var(--accent); box-shadow: var(--lift); border: 1px solid var(--hairline-2);
}
.tx-drop:hover .tx-drop-icon { transform: translateY(-1px); }
.tx-drop.over .tx-drop-icon { animation: txBob .7s ease-in-out infinite alternate; }
@keyframes txBob { to { transform: translateY(-4px); } }
.tx-drop-text strong { display: block; font-size: 13.5px; color: var(--ink); font-weight: 600; }
.tx-drop-text span { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }
.tx-drop-text u { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }

/* The progress ring and its stages. */
.tx-run { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 18px; align-items: start; }
.tx-ring { position: relative; width: 88px; height: 88px; flex: none; }
.tx-ring svg { width: 100%; height: 100%; transform: rotate(-90deg); overflow: visible; }
.tx-ring circle { fill: none; }
.tx-trk { stroke: var(--surface-3); stroke-width: 6.5; }
.tx-arc {
  stroke: var(--accent); stroke-width: 6.5; stroke-linecap: round;
  stroke-dasharray: 100 100; stroke-dashoffset: 100;
  filter: drop-shadow(0 0 5px color-mix(in srgb, var(--accent) 38%, transparent));
}
.tx-orbit {
  stroke: var(--accent); stroke-width: 1.6; stroke-linecap: round; opacity: .5;
  stroke-dasharray: 9 91; animation: txOrbit 1.8s linear infinite;
}
@keyframes txOrbit { to { stroke-dashoffset: -100; } }
.tx-pct { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.tx-pct b { font: 600 23px/1 var(--sans); letter-spacing: -.035em; color: var(--ink); font-variant-numeric: tabular-nums; }
.tx-pct small { font: 600 11px/1 var(--sans); color: var(--muted); margin: 6px 0 0 1px; align-self: flex-start; padding-top: 30px; }
.tx-run-title { font-size: 14px; font-weight: 600; color: var(--ink); padding-top: 4px; }
.tx-run-sub { font-size: 12px; color: var(--muted); margin: 3px 0 12px; min-height: 1.35em; font-variant-numeric: tabular-nums; }
.tx-stages { list-style: none; margin: 0; padding: 0; display: grid; }
.tx-stages li {
  position: relative; display: grid; grid-template-columns: 18px minmax(0, 1fr) auto; gap: 10px; align-items: center;
  padding: 4px 0; font-size: 12.5px; color: var(--faint); transition: color .25s ease;
}
/* The rail between two dots fills in as a stage completes. */
.tx-stages li:not(:last-child)::after {
  content: ''; position: absolute; left: 8.5px; top: 22px; bottom: -4px; width: 1.5px;
  background: linear-gradient(var(--accent), var(--accent)) no-repeat top / 100% 0, var(--hairline);
  transition: background-size .35s ease;
}
.tx-stages li.done:not(:last-child)::after { background-size: 100% 100%, auto; }
.tx-dot {
  position: relative; z-index: 1; width: 18px; height: 18px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1.5px solid var(--hairline); background: var(--surface);
  transition: background .25s ease, border-color .25s ease;
}
.tx-dot svg { width: 11px; height: 11px; color: var(--on-accent, #fff); opacity: 0; transform: scale(.3); transition: opacity .2s ease, transform .3s cubic-bezier(.34,1.56,.64,1); }
.tx-stages li.done { color: var(--ink-2); }
.tx-stages li.done .tx-dot { background: var(--accent); border-color: var(--accent); }
.tx-stages li.done .tx-dot svg { opacity: 1; transform: none; }
.tx-stages li.active { color: var(--ink); font-weight: 600; }
.tx-stages li.active .tx-dot { border-color: var(--accent); }
.tx-stages li.active .tx-dot::before {
  content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
  animation: txPulse 1.4s ease-in-out infinite;
}
@keyframes txPulse {
  0%, 100% { transform: scale(.72); box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent); }
  60% { transform: scale(1); box-shadow: 0 0 0 6px transparent; }
}
/* A light passing over the stage that is running. */
.tx-stages li.active .tx-lbl {
  background: linear-gradient(100deg, var(--ink) 0 38%, var(--accent) 50%, var(--ink) 62% 100%) 0 0 / 260% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: txShimmer 2.4s linear infinite;
}
@keyframes txShimmer { from { background-position: 100% 0; } to { background-position: -160% 0; } }
.tx-lbl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tx-meta { font-size: 11.5px; font-weight: 400; color: var(--faint); font-variant-numeric: tabular-nums; white-space: nowrap; }
.tx-actions { margin-top: 12px; }

/* A preview, and a finished job. */
.tx-preview, .tx-done { display: flex; flex-direction: column; gap: 12px; }
.tx-car { display: flex; align-items: baseline; justify-content: space-between; gap: 6px 12px; flex-wrap: wrap; }
.tx-car-name strong { font-size: 15px; color: var(--ink); margin-right: 8px; }
.tx-car-name .mono { font-size: 11px; color: var(--faint); letter-spacing: .03em; }
.tx-car-span { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.tx-pane .summary-row { margin: 0; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); }
.tx-pane .summary-row .cell { padding: 12px 13px; }
.tx-pane .summary-row .cell .v { font-size: 20px; white-space: nowrap; }
.tx-pane .summary-row .cell .v span { font-size: 12px !important; }
.tx-pane .summary-row .cell .l { font-size: 9.5px; line-height: 1.25; }
.tx-notes { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.tx-notes li {
  position: relative; padding: 8px 11px 8px 32px; border-radius: var(--r2);
  background: var(--surface-2); color: var(--ink-2); font-size: 12px; line-height: 1.45;
}
.tx-notes li::before {
  content: 'i'; position: absolute; left: 10px; top: 8.5px; width: 14px; height: 14px; border-radius: 50%;
  background: var(--accent-wash); color: var(--accent); font: 700 9.5px/14px var(--sans); text-align: center;
}
.tx-notes li.swap::before { content: '↻'; font-size: 10px; }
.tx-notes li.warn::before { content: '!'; background: color-mix(in srgb, var(--warning) 18%, transparent); color: var(--warning-ink); }
.tx-notes li.dup { background: var(--accent-wash); color: var(--ink); font-weight: 500; }
.tx-notes li.dup::before { content: '='; background: var(--accent); color: var(--on-accent, #fff); font-size: 11px; line-height: 13.5px; }
.tx-from { display: flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 600; color: var(--ink-2); }
.tx-from svg { width: 16px; height: 16px; color: var(--accent); flex: none; }
.tx-error-acts { display: flex; flex-wrap: wrap; gap: 8px; }
/* The export's period: presets, and two dates when it is Custom. */
.tx-period { display: grid; gap: 8px; }
.tx-period .g-lab { margin: 0; }
.tx-period .seg { justify-self: start; max-width: 100%; }
.tx-period .seg button { padding: 5px 11px; font-size: 12px; white-space: nowrap; }
.tx-dates { display: grid; grid-template-columns: repeat(2, minmax(0, 150px)); gap: 10px; }
.tx-dates[hidden] { display: none; }
.tx-dates input { width: 100%; }
.tx-period-note { margin: 0; min-height: 1.4em; }
.tx-period-note.bad { color: var(--critical-ink); }

.tx-done-mark { flex: none; }
.tx-done-mark svg { width: 44px; height: 44px; overflow: visible; display: block; }
.tx-done-mark circle {
  fill: var(--accent-wash); stroke: var(--accent); stroke-width: 2.5;
  stroke-dasharray: 145; stroke-dashoffset: 145; transform-origin: 50% 50%;
  animation: txDraw .55s cubic-bezier(.65,0,.35,1) forwards;
}
.tx-done-mark path {
  fill: none; stroke: var(--accent); stroke-width: 3.5; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 36; stroke-dashoffset: 36; animation: txDraw .32s .42s cubic-bezier(.65,0,.35,1) forwards;
}
.tx-done-mark { animation: txPop .45s .62s cubic-bezier(.34,1.56,.64,1) backwards; transform-origin: 50% 50%; }
@keyframes txDraw { to { stroke-dashoffset: 0; } }
@keyframes txPop { 40% { transform: scale(1.12); } }
.tx-done-head { display: flex; align-items: center; gap: 13px; }
.tx-done-head span { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }
.tx-done-title { font-size: 15px; font-weight: 600; color: var(--ink); letter-spacing: -.01em; }

.tx-chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.tx-chips li {
  font-size: 11.5px; padding: 4px 10px; border-radius: 20px;
  background: var(--surface-2); color: var(--ink-2); border: 1px solid var(--hairline-2);
}
.tx-file {
  display: flex; align-items: center; gap: 12px; padding: 10px 12px;
  border: 1px solid var(--hairline-2); border-radius: var(--r2); background: var(--surface-2);
}
.tx-file .tx-drop-icon { width: 38px; height: 38px; border-radius: 10px; }
.tx-file > div { flex: 1; min-width: 0; }
.tx-file strong { display: block; font-size: 12.5px; color: var(--ink); overflow-wrap: anywhere; line-height: 1.35; }
.tx-file span { display: block; font-size: 11.5px; color: var(--muted); margin-top: 1px; }
.tx-contents { list-style: none; margin: 0; padding: 0; display: grid; gap: 5px; }
.tx-contents li { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 2px 12px; font-size: 12px; color: var(--muted); }
.tx-contents strong { color: var(--ink-2); font-weight: 600; }

.tx-error {
  display: flex; gap: 12px; padding: 14px; border-radius: var(--r2);
  background: color-mix(in srgb, var(--critical) 7%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--critical) 24%, transparent);
  animation: txIn .3s cubic-bezier(.32,.72,0,1) backwards;
}
.tx-error-icon { color: var(--critical-ink); flex: none; padding-top: 1px; }
.tx-error strong { font-size: 13.5px; color: var(--ink); }
.tx-error p { margin: 3px 0 10px; color: var(--ink-2); }

.tx-history { margin-top: 16px; }
.tx-history .g-lab { margin-bottom: 4px; }
.tx-hrow {
  display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1.4fr) auto auto; gap: 6px 14px; align-items: center;
  padding: 11px 2px; border-top: 1px solid var(--hairline-2); font-size: 12.5px;
}
.tx-hrow:first-of-type { border-top: 0; }
.tx-hmain strong { display: block; color: var(--ink); }
.tx-hmain span, .tx-hcounts, .tx-hwhen { color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
.tx-history > p { margin: 8px 0 0; }

/* Art: poster numerals for the figures, mono caps for the pane captions. */
:root[data-theme="art"] .tx-pct b { font-family: var(--display); font-weight: 400; font-size: 27px; letter-spacing: .02em; }
:root[data-theme="art"] .tx-pane-head div > span,
:root[data-theme="art"] .tx-meta { font-family: var(--mono); font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase; }
:root[data-theme="art"] .tx-drop-icon { box-shadow: none; }
:root[data-theme="art"] .tx-from { font-family: var(--mono); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; }
:root[data-theme="art"] .tx-pane { border-color: var(--hairline); }
:root[data-theme="space"] .tx-pane { background: color-mix(in srgb, var(--surface-2) 55%, var(--surface)); }
:root[data-theme="space"] .tx-pct b { font-family: var(--display); }

@media (max-width: 560px) {
  .tx-pane { padding: 14px 13px; }
  .tx-src .seg, .tx-period .seg { display: flex; width: 100%; }
  .tx-src .seg button, .tx-period .seg button { flex: 1 1 auto; padding: 5px 7px; font-size: 11.5px; }
  .tx-run { grid-template-columns: 1fr; gap: 12px; }
  .tx-ring { width: 76px; height: 76px; }
  .tx-run { grid-template-areas: none; }
  .tx-hrow { grid-template-columns: minmax(0, 1fr) auto; }
  .tx-hcounts, .tx-hwhen { grid-column: 1; }
  .tx-hrow .btn { grid-column: 2; grid-row: 1 / span 3; }
}

@media (prefers-reduced-motion: reduce) {
  .tx-pane.tx-enter > *, .tx-orbit, .tx-stages li.active .tx-dot::before, .tx-drop.over .tx-drop-icon,
  .tx-done-mark, .tx-error { animation: none !important; }
  .tx-stages li.active .tx-lbl { animation: none; background: none; color: var(--ink); }
  .tx-done-mark circle, .tx-done-mark path { animation: none; stroke-dashoffset: 0; }
  .tx-orbit { display: none; }
}
