/**
 * App shell + primitives (ADR-007 v2.1, Phase 6a W-X2) — built only on
 * tokens.css variables; no colour literal here (test_tokens_shape.py).
 *
 * Two layouts, not one stretched (the brief, roadmap 6a):
 *   ≤ 767px   the PHONE layout — one column, the nav is a fixed bottom
 *             tab bar, the page title sits in a compact top bar.
 *   768–1279  the DESKTOP layout, contracted — the left rail shows icons
 *             only, the right panel (when a page has one) drops under
 *             the main column. Never the phone layout stretched.
 *   ≥ 1280px  the full desktop layout — 232px rail, main column, 340px
 *             right panel beside it.
 * The breakpoint literals are repeated in tests/app/test_tokens_shape.py
 * (MOBILE_MAX_PX, PANEL_MIN_PX); a media query cannot read a custom
 * property, so the test is what keeps the two in step.
 *
 * Every tappable control keeps `min-height: var(--mt-tap)` (44px).
 */

* { box-sizing: border-box; }

html, body {
  margin: 0;
  min-height: 100vh;
  background: var(--mt-color-bg);
  color: var(--mt-color-text);
  font-family: var(--mt-font-sans);
  font-size: var(--mt-text-base);
  line-height: var(--mt-leading-base);
  -webkit-font-smoothing: antialiased;
  /* `clip`, not `hidden` (review C1): `hidden` makes <body> its own scroll
     container, which defeats `position: sticky` on the rail. */
  overflow-x: clip;
}

a { color: var(--mt-color-accent); text-decoration: none; }
h1, h2, h3, p { margin: 0; }

/* ════════════════════════════ SHELL ═══════════════════════════════════ */

.app-shell {
  display: grid;
  grid-template-columns: var(--mt-rail-width) minmax(0, 1fr);
  min-height: 100vh;
}

/* The rail: wordmark, nav, footer slot. Sticky, full height, on desktop. */
.app-rail {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  gap: var(--mt-space-1);
  padding: var(--mt-space-6) var(--mt-space-4);
  background: var(--mt-color-surface);
  border-right: 1px solid var(--mt-color-border);
}

.app-wordmark {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: var(--mt-space-1) var(--mt-space-3) var(--mt-space-5);
  color: var(--mt-color-text);
  font-weight: 800;
  font-size: var(--mt-text-lg);
  letter-spacing: -0.02em;
}

.app-wordmark .mark {
  width: 26px;
  height: 26px;
  border-radius: 8px;
  background: var(--mt-color-accent);
  color: var(--mt-color-accent-contrast);
  display: inline-grid;
  place-items: center;
  flex: none;
}

.app-wordmark .mark svg { width: var(--mt-icon-sm); height: var(--mt-icon-sm); }

.app-nav { display: flex; flex-direction: column; gap: var(--mt-space-1); }

.app-nav a {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: var(--mt-tap);
  padding: 0 var(--mt-space-3);
  border-radius: var(--mt-radius-sm);
  color: var(--mt-color-text-muted);
  font-weight: 600;
}

.app-nav a svg { width: var(--mt-icon-md); height: var(--mt-icon-md); flex: none; }

.app-nav a[aria-current="page"] {
  background: var(--mt-color-surface-2);
  color: var(--mt-color-text);
}

.app-rail-foot {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: var(--mt-space-2);
  color: var(--mt-color-text-muted);
  font-size: var(--mt-text-sm);
}

.rail-account {
  display: block;
  padding: var(--mt-space-3);
  border-radius: var(--mt-radius-sm);
  background: var(--mt-color-surface-2);
  color: var(--mt-color-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The compact top bar is a PHONE-only element; hidden on desktop. */
.app-topbar { display: none; }

.app-main {
  padding: var(--mt-space-8) var(--mt-space-8) var(--mt-space-12);
  max-width: 1480px;
  width: 100%;
}

/* Main column + optional right panel. `.has-panel` is set by app_base.html
   when a page fills `{% block panel %}`. */
.app-columns { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--mt-space-6); align-items: start; }
.app-panel { min-width: 0; }

.app-page-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--mt-space-4);
  margin-bottom: var(--mt-space-6);
}

.app-page-head h1 {
  font-size: var(--mt-text-3xl);
  font-weight: 800;
  letter-spacing: -0.03em;
}

/* ── 768–1279px: the desktop layout contracts ─────────────────────────── */
@media (max-width: 1279px) {
  .app-shell { grid-template-columns: var(--mt-rail-width-narrow) minmax(0, 1fr); }
  .app-rail { padding: var(--mt-space-6) var(--mt-space-3); align-items: center; }
  .app-wordmark { padding: var(--mt-space-1) 0 var(--mt-space-5); }
  .app-wordmark .word, .app-rail-foot { display: none; }
  /* Labels stay in the accessibility tree (review C2): clipped, not removed. */
  .app-nav a .label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  .app-nav a { position: relative; justify-content: center; padding: 0; width: var(--mt-tap); }
  .app-main { padding: var(--mt-space-6) var(--mt-space-6) var(--mt-space-10); }
}

/* ── ≥ 1280px: the panel sits beside the main column ──────────────────── */
@media (min-width: 1280px) {
  .app-columns.has-panel { grid-template-columns: minmax(0, 1fr) var(--mt-panel-width); }
}

/* ── ≤ 767px: the PHONE layout ────────────────────────────────────────── */
@media (max-width: 767px) {
  .app-shell { display: block; }

  /* The rail becomes the bottom tab bar: wordmark and footer go, the nav
     turns into a row of icon + caption tabs. */
  .app-rail {
    position: fixed;
    inset: auto 0 0 0;
    height: auto;
    padding: 0;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    border-right: 0;
    border-top: 1px solid var(--mt-color-border);
    background: var(--mt-color-surface);
    z-index: 20;
    align-items: stretch; /* the 768–1279 rule centres the rail's children; the tab row must fill the width */
  }
  .app-wordmark, .app-rail-foot { display: none; }
  .app-nav { flex-direction: row; gap: 0; width: 100%; }
  .app-nav a {
    flex: 1;
    flex-direction: column;
    justify-content: center;
    gap: 3px;
    min-height: var(--mt-tabbar-height);
    padding: var(--mt-space-2) var(--mt-space-1);
    border-radius: 0;
    font-size: var(--mt-text-2xs);
    color: var(--mt-color-text-faint);
  }
  .app-nav a svg { width: var(--mt-icon-lg); height: var(--mt-icon-lg); }
  .app-nav a .label { position: static; width: auto; height: auto; overflow: visible; clip: auto; }
  .app-nav a { width: auto; }
  .app-nav a[aria-current="page"] { background: transparent; color: var(--mt-color-accent); }

  .app-topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--mt-space-3);
    padding: calc(var(--mt-space-3) + env(safe-area-inset-top, 0px)) var(--mt-space-1) var(--mt-space-2);
  }
  .app-topbar h1 { font-size: var(--mt-text-2xl); font-weight: 800; letter-spacing: -0.03em; }

  .app-main {
    padding: var(--mt-space-2) var(--mt-space-4) calc(var(--mt-tabbar-height) + var(--mt-space-6) + env(safe-area-inset-bottom, 0px));
  }
  .app-page-head { display: none; } /* the topbar carries the title on a phone */
}

/* ════════════════════════════ PRIMITIVES ═════════════════════════════ */

.card {
  background: var(--mt-color-surface);
  border: 1px solid var(--mt-color-border);
  border-radius: var(--mt-radius-lg);
  box-shadow: var(--mt-shadow-card);
  padding: var(--mt-space-5) var(--mt-space-6);
  margin-bottom: var(--mt-space-4);
  /* Long unbroken tokens (a VIN, a UUID) must wrap, never push the card
     wider than the viewport. */
  overflow-wrap: anywhere;
}

@media (max-width: 767px) {
  .card { padding: var(--mt-space-4); }
}

.card h2 {
  margin: 0 0 var(--mt-space-3);
  font-size: var(--mt-text-lg);
  font-weight: 700;
  letter-spacing: -0.01em;
}

.card h3 {
  margin: var(--mt-space-5) 0 var(--mt-space-2);
  font-size: var(--mt-text-xs);
  color: var(--mt-color-text-faint);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}


.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 1.75rem;
  padding: 0 var(--mt-space-3);
  border-radius: var(--mt-radius-pill);
  background: var(--mt-color-surface-2);
  color: var(--mt-color-text-muted);
  font-size: var(--mt-text-sm);
  font-weight: 600;
}

.badge.success { background: var(--mt-color-success-bg); color: var(--mt-color-success-text); }
.badge.warning { background: var(--mt-color-warning-bg); color: var(--mt-color-warning-text); }
.badge.danger { background: var(--mt-color-danger-bg); color: var(--mt-color-danger-text); }

.error-message {
  border: 1px solid var(--mt-color-warning-text);
  background: var(--mt-color-warning-bg);
  color: var(--mt-color-warning-text);
  border-radius: var(--mt-radius-sm);
  padding: var(--mt-space-3) var(--mt-space-4);
  margin: 0 0 var(--mt-space-3);
  font-size: var(--mt-text-sm);
}

form.control-form {
  display: flex;
  flex-direction: column;
  gap: var(--mt-space-3);
}

.field-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--mt-space-3);
  align-items: flex-end;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  flex: 1 1 8rem;
}

.field label {
  font-size: var(--mt-text-xs);
  color: var(--mt-color-text-faint);
  font-weight: 600;
}

.toggle-row {
  display: flex;
  align-items: center;
  gap: var(--mt-space-3);
  min-height: var(--mt-tap);
}

.toggle-row input[type="checkbox"] {
  width: 1.25rem;
  height: 1.25rem;
  flex-shrink: 0;
  accent-color: var(--mt-color-accent);
}

input[type="text"], input[type="email"], input[type="number"], input:not([type]), select, textarea {
  min-height: var(--mt-tap);
  width: 100%;
  font: inherit;
  color: var(--mt-color-text);
  background: var(--mt-color-bg);
  border: 1px solid var(--mt-color-border-strong);
  border-radius: var(--mt-radius-sm);
  padding: 0 var(--mt-space-4);
}

textarea { padding: var(--mt-space-3) var(--mt-space-4); }

button, .button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--mt-space-2);
  min-height: var(--mt-tap);
  padding: 0 var(--mt-space-5);
  border-radius: var(--mt-radius-pill);
  border: 1px solid var(--mt-color-border-strong);
  background: var(--mt-color-surface-2);
  color: var(--mt-color-text);
  font: inherit;
  font-size: var(--mt-text-sm);
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
}

button.primary, .button.primary {
  background: var(--mt-color-accent);
  border-color: var(--mt-color-accent);
  color: var(--mt-color-accent-contrast);
}

button.ghost, .button.ghost {
  background: transparent;
  border-color: var(--mt-color-border);
  color: var(--mt-color-text-muted);
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--mt-space-2);
  align-items: center;
}

.help-toggle summary {
  cursor: pointer;
  color: var(--mt-color-text-muted);
  min-height: var(--mt-tap);
  display: inline-flex;
  align-items: center;
}

.help-toggle div {
  font-size: var(--mt-text-sm);
  color: var(--mt-color-text-muted);
  margin-top: var(--mt-space-1);
}

.muted { color: var(--mt-color-text-muted); font-size: var(--mt-text-sm); }
.faint { color: var(--mt-color-text-faint); font-size: var(--mt-text-sm); }
.mono { font-family: var(--mt-font-mono); font-variant-numeric: tabular-nums; }


.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--mt-space-2);
  margin-bottom: var(--mt-space-4);
}

.filter-bar select { min-width: 9rem; flex: 1 1 9rem; }

.device-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--mt-space-3);
  padding: var(--mt-space-3) 0;
  border-bottom: 1px solid var(--mt-color-border);
}

.device-row:last-child { border-bottom: none; }


/* ════════════════════ PAGES (Phase 6a W-X3) ══════════════════════════ */
/* The approved "Car + Automations" layout: a hero per car, one Automations
   list (status rows; forms stay on Recipes), an Activity panel. All on
   tokens; the few 1–3px hairline gaps below are layout constants, not
   design values. */

.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--mt-space-3);
  margin-bottom: var(--mt-space-4);
}

.icon { width: var(--mt-icon-sm); height: var(--mt-icon-sm); flex: none; }

/* ── hero ─────────────────────────────────────────────────────────────── */
.hero { background: var(--mt-gradient-hero); padding: var(--mt-space-6); }
.hero-top { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--mt-space-4); }
.hero h2 { margin: 0; font-size: var(--mt-text-2xl); font-weight: 800; letter-spacing: -0.02em; }
.hero-sub { color: var(--mt-color-text-muted); font-size: var(--mt-text-sm); margin-top: 2px; }
.hero-num { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--mt-space-3); margin-top: var(--mt-space-5); }
.hero-num .n {
  font-size: var(--mt-text-hero);
  font-weight: 800;
  line-height: var(--mt-leading-tight);
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
}
.hero-num .n small { font-size: var(--mt-text-xl); color: var(--mt-color-text-faint); letter-spacing: 0; margin-left: 2px; }
.hero-num .s { color: var(--mt-color-text-muted); }
.hero-num .s b { color: var(--mt-color-text); font-weight: 600; }

.battery { position: relative; height: 14px; border-radius: 7px; background: var(--mt-color-battery-track); margin-top: var(--mt-space-4); }
.battery .fill { position: absolute; inset: 0 auto 0 0; border-radius: 7px; background: var(--mt-color-battery); }
.battery .tick { position: absolute; top: -5px; bottom: -5px; width: 3px; border-radius: 2px; background: var(--mt-color-text); }
.battery .tick.soft { opacity: 0.35; width: 2px; }

.facts { display: flex; flex-wrap: wrap; gap: var(--mt-space-5); margin-top: var(--mt-space-5); }
.fact .v { font-weight: 700; font-variant-numeric: tabular-nums; }
.fact .k { color: var(--mt-color-text-muted); font-size: var(--mt-text-xs); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 600; }

.pills { display: flex; flex-wrap: wrap; gap: var(--mt-space-2); margin-top: var(--mt-space-5); }
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--mt-space-2); margin-top: var(--mt-space-5); }
.hero-actions form { margin: 0; }

/* ── automation rows ──────────────────────────────────────────────────── */
.card-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--mt-space-3); margin-bottom: var(--mt-space-2); }
.card-head h2 { margin: 0; }
.card-head a { font-size: var(--mt-text-sm); font-weight: 600; }

.row {
  display: grid;
  grid-template-columns: var(--mt-icon-tile) minmax(0, 1fr) auto;
  gap: var(--mt-space-4);
  align-items: center;
  min-height: var(--mt-row-height);
  padding: var(--mt-space-3) 0;
  border-top: 1px solid var(--mt-color-border);
}
.rows > .row:first-child { border-top: 0; }
.row .tile {
  width: var(--mt-icon-tile);
  height: var(--mt-icon-tile);
  border-radius: var(--mt-radius-sm);
  background: var(--mt-color-surface-2);
  color: var(--mt-color-text-muted);
  display: grid;
  place-items: center;
}
.row .tile svg { width: var(--mt-icon-md); height: var(--mt-icon-md); }
.row.on .tile { background: color-mix(in srgb, var(--mt-color-accent) 16%, transparent); color: var(--mt-color-accent); }
.row.live .tile { background: var(--mt-color-success-bg); color: var(--mt-color-success-text); }
.row .main { min-width: 0; }
.row .t { font-weight: 700; display: flex; flex-wrap: wrap; align-items: center; gap: var(--mt-space-2); }
.row .s { color: var(--mt-color-text-muted); font-size: var(--mt-text-sm); margin-top: 1px; }
.row .side { display: flex; flex-wrap: wrap; gap: var(--mt-space-2); align-items: center; justify-content: flex-end; }
.row .side form { margin: 0; }

/* A live override rides on its recipe's row (the top-off on Charge at home). */
.inset {
  margin: 0 0 var(--mt-space-3) calc(var(--mt-icon-tile) + var(--mt-space-4));
  padding: var(--mt-space-3) var(--mt-space-4);
  border-radius: var(--mt-radius-sm);
  background: var(--mt-color-success-bg);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--mt-space-3);
}
.inset.pending { background: var(--mt-color-warning-bg); }
.inset p { margin: 0; }
.inset .t { font-weight: 600; font-size: var(--mt-text-sm); }
.inset .s { color: var(--mt-color-text-muted); font-size: var(--mt-text-sm); }
.inset form { margin: 0; }
.note { margin: 0 0 var(--mt-space-3) calc(var(--mt-icon-tile) + var(--mt-space-4)); }

/* ── activity ─────────────────────────────────────────────────────────── */
.activity-day {
  padding: var(--mt-space-4) 0 var(--mt-space-1);
  font-size: var(--mt-text-xs);
  color: var(--mt-color-text-muted);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.activity-row {
  display: grid;
  grid-template-columns: var(--mt-space-2) minmax(0, 1fr) auto;
  gap: var(--mt-space-3);
  align-items: start;
  padding: var(--mt-space-3) 0;
  border-top: 1px solid var(--mt-color-border);
  font-size: var(--mt-text-sm);
}
.activity-row .dot { width: var(--mt-space-2); height: var(--mt-space-2); border-radius: 50%; margin-top: 6px; background: var(--mt-color-border-strong); }
.activity-row.fired .dot { background: var(--mt-color-success-text); }
.activity-row.skipped .dot { background: var(--mt-color-warning-text); }
.activity-row.failed .dot { background: var(--mt-color-danger-text); }
.activity-row .h { font-weight: 600; font-size: var(--mt-text-base); }
.activity-row .x { color: var(--mt-color-text-muted); }
.activity-row .time { font-family: var(--mt-font-mono); font-size: var(--mt-text-xs); color: var(--mt-color-text-muted); padding-top: 3px; white-space: nowrap; }

.meter { height: 6px; border-radius: 3px; background: var(--mt-color-battery-track); overflow: hidden; margin-top: var(--mt-space-2); }
.meter i { display: block; height: 100%; background: var(--mt-color-accent); }

/* ── recipes: a grid of cards; checkboxes drawn as switches ───────────── */
.page-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--mt-space-4); align-items: start; }
.page-grid > .card { margin-bottom: 0; }
@media (min-width: 1280px) {
  .page-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.vehicle-label { margin: var(--mt-space-2) 0 var(--mt-space-3); font-weight: 700; color: var(--mt-color-text-muted); }
.recipe-head { display: grid; grid-template-columns: var(--mt-icon-tile) minmax(0, 1fr); gap: var(--mt-space-4); align-items: center; margin-bottom: var(--mt-space-3); }
.recipe-head .tile {
  width: var(--mt-icon-tile);
  height: var(--mt-icon-tile);
  border-radius: var(--mt-radius-sm);
  background: color-mix(in srgb, var(--mt-color-accent) 16%, transparent);
  color: var(--mt-color-accent);
  display: grid;
  place-items: center;
}
.recipe-head .tile svg { width: var(--mt-icon-md); height: var(--mt-icon-md); }
.recipe-head h2 { margin: 0; }
.recipe-head h2 .help-toggle { display: inline-block; vertical-align: middle; font-weight: 400; }
.recipe-head .s { color: var(--mt-color-text-muted); font-size: var(--mt-text-sm); }

.toggle-row { justify-content: space-between; }
.toggle-row input[type="checkbox"] {
  order: 2;
  appearance: none;
  -webkit-appearance: none;
  width: 48px;
  height: 28px;
  margin: 0;
  border-radius: var(--mt-radius-pill);
  /* The knob is a gradient on the input itself, not an ::after — Firefox
     does not reliably draw pseudo-elements on form controls. */
  background:
    radial-gradient(circle 11px at 14px 50%, var(--mt-color-accent-contrast) 10px, transparent 11px),
    var(--mt-color-border-strong);
  /* A visible edge in both themes (review MED-2: the bare track was 1.6:1
     on the light surface — WCAG 1.4.11 wants 3:1 for a control's state). */
  border: 1px solid var(--mt-color-text-muted);
  cursor: pointer;
  flex: none;
}
@media (forced-colors: active) {
  .toggle-row input[type="checkbox"] { border-color: CanvasText; forced-color-adjust: none; }
}
.toggle-row input[type="checkbox"]:checked {
  border-color: var(--mt-color-accent);
  background:
    radial-gradient(circle 11px at 34px 50%, var(--mt-color-accent-contrast) 10px, transparent 11px),
    var(--mt-color-accent);
}
.toggle-row input[type="checkbox"]:focus-visible { outline: 2px solid var(--mt-color-accent); outline-offset: 2px; }

/* ── settings + history ───────────────────────────────────────────────── */
.device-row .who { min-width: 0; flex: 1 1 16rem; }
.device-row .ua { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vin { margin-top: var(--mt-space-3); }
.legend-note { margin-top: var(--mt-space-4); }
.legend { display: grid; gap: var(--mt-space-2); font-size: var(--mt-text-sm); color: var(--mt-color-text-muted); }

@media (max-width: 767px) {
  .hero { padding: var(--mt-space-5); }
  .hero-num .n { font-size: var(--mt-text-3xl); }
  /* Rows keep their badge on the right; only a row whose side holds a
     form (live warm-up, pending door command) wraps it under the text. */
  .row:has(.side form) { grid-template-columns: var(--mt-icon-tile) minmax(0, 1fr); }
  .row:has(.side form) .side { grid-column: 2; justify-content: flex-start; }
  .inset, .note { margin-left: 0; }
}

/* ════════════════════ STANDALONE PAGES (sign-in, redirect) ═══════════ */

.auth-page {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: var(--mt-space-6);
}

.auth-card {
  width: 100%;
  max-width: 24rem;
  padding: var(--mt-space-8);
}

.auth-card.wide { max-width: 40rem; }

/* The standalone pages reuse `.app-wordmark`; the rail's breakpoint rules
   must not hide it here (no rail, no tab bar on these pages). */
.auth-card .app-wordmark { display: flex; padding: 0; }
.auth-card .app-wordmark .word { display: inline; }

.auth-card h1 {
  font-size: var(--mt-text-2xl);
  font-weight: 800;
  letter-spacing: -0.02em;
  margin: var(--mt-space-5) 0 var(--mt-space-2);
}

.auth-card p { color: var(--mt-color-text-muted); margin-bottom: var(--mt-space-4); }

.auth-card form { display: grid; gap: var(--mt-space-3); }

.auth-card .notice {
  border: 1px solid var(--mt-color-success-text);
  background: var(--mt-color-success-bg);
  color: var(--mt-color-success-text);
  padding: var(--mt-space-3) var(--mt-space-4);
  border-radius: var(--mt-radius-sm);
  margin: 0 0 var(--mt-space-4);
  font-size: var(--mt-text-sm);
}

.auth-card .error {
  border: 1px solid var(--mt-color-danger-text);
  background: var(--mt-color-danger-bg);
  color: var(--mt-color-danger-text);
  padding: var(--mt-space-3) var(--mt-space-4);
  border-radius: var(--mt-radius-sm);
  margin: 0 0 var(--mt-space-4);
}

/* ════════════════════ IN-FLIGHT + DEGRADED MODE (unchanged) ══════════ */

/* W-U4: in-flight state while a .js-live submission is pending against
   its swap TARGET (static/app.js adds/removes `.is-saving` there). Dims
   the target, blocks further clicks on it (the seq-stamp already drops a
   stale RESPONSE; this additionally stops a second click from firing a
   second REQUEST while the first is still in the air). NOT `.card`-
   scoped (fix cycle 1, Reviewer): several forms swap `#app-content` or
   `#vehicle-<id>` -- neither is a `.card` -- and those got the class
   with zero visual effect, worst of all on poll-now/refresh, the app's
   slowest actions. */
.is-saving {
  opacity: 0.6;
  pointer-events: none;
}

/* The "Saving…" LABEL, unlike the dim/block rule above, stays `.card`-
   scoped on purpose: it's positioned assuming a card-sized box, and a
   label floating in the corner of a full-page `#app-content` swap would
   read wrong. */
.card.is-saving {
  position: relative;
}

.card.is-saving::after {
  content: "Saving…";
  position: absolute;
  top: var(--mt-space-4);
  right: var(--mt-space-4);
  font-size: var(--mt-text-xs);
  color: var(--mt-color-text-muted);
}

/* ADR-007 v2.0 ("the remedy is a reload, not a fallback path"):
   app_base.html carries ONE static notice, hidden until either the
   delayed-reveal below fires or this rule hides it, for the case where
   app.js is absent/stale/broken (the service worker can serve a bad copy
   past a reload -- see app.js's header). This rule hides it ONLY once
   app.js finishes binding every .js-live form and marks `<html
   class="js">` (the LAST statement in its DOMContentLoaded handler) --
   so a throw during binding leaves the notice showing instead of
   silently vanishing. No per-control fallback buttons: this banner is
   the entire degraded-mode surface. */
.js .js-load-notice {
  display: none;
}

/* Reviewer finding (fix cycle 4): the notice must not sit in NORMAL FLOW
   -- an in-flow element at `opacity: 0` still occupies its ~80px box (a
   layout jump when `.js` later collapses it) and still sits in the
   accessibility tree the whole time (a screen-reader user hears a false
   failure message on a healthy page, `opacity` not being enough to pull
   it out). `position: fixed` (same pattern as `.app-nav` above) removes
   it from flow entirely -- no box for a healthy load to ever reserve.

   Zero-JS delayed reveal, on `visibility` (discretely animatable, and
   genuinely removes the element from the a11y tree while hidden) rather
   than `opacity`: hidden at first paint, animates to visible only after
   a 1.8s delay, at which point `.js .js-load-notice`'s `display: none`
   above -- which wins regardless of the animation, since a non-rendered
   box has no visibility to override -- permanently overrides it IF `.js`
   is already set. `forwards` keeps the end state (visible) if the delay
   elapses with no `.js` class -- the broken-script case this banner
   exists for. Out-of-flow removes the LAYOUT-JUMP residual entirely;
   this reduces the VISIBILITY-flash residual, it does not eliminate it:
   the 1.8s delay is a wall-clock timer from first paint, independent of
   when DOMContentLoaded + binding actually finishes. A genuinely slow
   device/network where binding completes AFTER 1.8s still sees the
   notice flash briefly before `.js` lands and hides it. 1.8s is the
   chosen trade point (comfortably longer than a normal bind, not a
   guarantee for every device) -- move it if that trade turns out wrong
   in practice. */
.js-load-notice {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  /* Nothing else in this file sets z-index; picked high enough that a
     future positioned element added without checking this file still
     loses to a genuine load failure. */
  z-index: 1000;
  background: var(--mt-color-warning-bg);
  color: var(--mt-color-warning-text);
  padding: var(--mt-space-3) var(--mt-space-4);
  text-align: center;
  font-size: var(--mt-text-sm);
  font-weight: 600;
  visibility: hidden;
  animation: js-load-notice-reveal 0s ease-in 1.8s forwards;
}

@keyframes js-load-notice-reveal {
  to {
    visibility: visible;
  }
}

/* Reviewer finding: deliberately the FAIL-LOUD branch, not a suppression.
   A reduced-motion user's agent (or a future global override) commonly
   disables animations with something like `animation: none !important`
   -- if that reached `.js-load-notice` unmodified, the notice would be
   pinned at its `visibility: hidden` START state FOREVER, turning a
   genuine load failure completely silent for exactly the users who
   already asked for less motion, not less information. So: no delayed
   reveal for these users at all -- skip straight to visible. The cost is
   a brief visible flash on every healthy load for this one preference
   group (the out-of-flow fix above means it's at least never a layout
   jump); the alternative cost -- an unrecoverable silent failure mode --
   is worse. Do not "fix" this flash by suppressing it again. */
@media (prefers-reduced-motion: reduce) {
  .js-load-notice {
    animation: none;
    visibility: visible;
  }
}

