/* ═══════════════════════════════════════════════════════════════════════
   GARAGE · Concept C   (b174)
   Loaded after design-v2-app.css, profile-c.css and trips-c.css.

   Gated on `body.gv-on`, set by show() for the Garage view only. Every rule
   below is either prefixed with that class or confined to the `.gv-`
   namespace, so nothing here can reach another screen — the same discipline
   profile-c.css and trips-c.css use.

   WHY THIS FILE EXISTS AT ALL, and why it could not have existed sooner.
   The Car section was copied from the Pets hub in b146 and never revisited
   when Grocery, Profile and Travel moved to Concept C. That was not an
   oversight anybody could have fixed by writing CSS: show() listed
   `dashboard`, `profile` and the three trip views in `dv2Views`, and `car`
   was not among them — so the section never carried `body.dv2-app`, the
   class the `--dv2-*` tokens are HOSTED on. A `var(--dv2-text-primary)`
   written here would have resolved to nothing and voided its whole
   declaration, silently. The one-line re-gate in show() is what unlocked
   this file. It is the same trap trips-c.css calls C-6 at the top.

   THE BASE STANDARD IS THE ENTITY TILE (set Aug 2026, see profile-c.css):
   every entity gets its own discrete rounded tab, and the container behind
   those tabs is transparent. A task, a document, a timeline entry — each is
   a tab.

   AND THE ONE DELIBERATE EXCEPTION. Concept C's premise is that a vehicle is
   not a record with a photo attached, it is a thing you own and look after.
   So on the Garage home a VEHICLE is not a tile with a thumbnail beside its
   name — it is the photograph, full width, with its plate and one sentence
   of status laid over it. Everything else on every screen is a tile. Making
   the exception in exactly one place is what stops it reading as a different
   design language.
   ═══════════════════════════════════════════════════════════════════════ */

body.gv-on {
  /* Garage reuses Profile's and Trips' tile tokens rather than defining a
     third set — three token sets for one visual standard is how they drift. */
  --gv-tile-bg: rgba(255,255,255,0.72);
  --gv-tile-bd: rgba(20,16,38,0.07);
  --gv-shadow: 0 1px 3px rgba(20,20,40,0.05);
  --gv-rad: 16px;
  --gv-sheet-bg: rgba(255,255,255,0.86);
  --gv-ink-on-dark: #ffffff;
  /* The scrim under the plate and title on a photo. Fixed, not themed: it
     sits on a photograph, not on the app's ground. */
  --gv-scrim: linear-gradient(180deg, rgba(0,0,0,0) 34%, rgba(0,0,0,0.62) 100%);
}
html[data-theme="dark"] body.gv-on {
  --gv-tile-bg: rgba(255,255,255,0.07);
  --gv-tile-bd: rgba(255,255,255,0.12);
  --gv-shadow: 0 1px 3px rgba(0,0,0,0.35);
  --gv-sheet-bg: #16182099;
  --gv-ink-on-dark: #14121F;
}
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.gv-on {
    --gv-tile-bg: rgba(255,255,255,0.07);
    --gv-tile-bd: rgba(255,255,255,0.12);
    --gv-shadow: 0 1px 3px rgba(0,0,0,0.35);
    --gv-sheet-bg: #16182099;
    --gv-ink-on-dark: #14121F;
  }
}

/* ── The ground ─────────────────────────────────────────────────────────
   style.css paints #view-car and .pets-body on the old flat surface. The
   aurora comes from the tokens now, so those fills have to stand down or
   they sit on top of it as an opaque slab — the same mistake #shops-box made
   in Profile and #trip-list made in Travel. */
body.gv-on #view-car,
body.gv-on .pets-body,
body.gv-on #car-hub {
  background: transparent;
}
body.gv-on {
  background: var(--dv2-bg-grad, #F4F2FA);
  background-attachment: fixed;
  color: var(--dv2-text-primary);
}

/* ── Header ─────────────────────────────────────────────────────────────
   .topbar is the legacy chrome. Glass, matching the rest of the app. */
body.gv-on .topbar.glass {
  background: var(--dv2-g-07-bg, rgba(255,255,255,0.5));
  border-bottom: 1px solid var(--dv2-g-07-bd, rgba(255,255,255,0.75));
  backdrop-filter: saturate(160%) blur(18px);
  -webkit-backdrop-filter: saturate(160%) blur(18px);
  box-shadow: none;
}
/* b232 · R9 — the twin of School's 19px title, and identical to it. Deleted
   for the same reason; the canon rule at the end of style.css owns the size. */
body.gv-on .topbar .lp-name {
  color: var(--dv2-text-primary);
}
body.gv-on .topbar .back-btn { color: var(--dv2-text-primary); }

/* b219 — THE CONTROLS WERE ON THE EDGE. Measured at 390px: the back arrow sat
   0px from the left and the add-vehicle button 2px from the right, against 16px
   in Pets and School. Those two wrap their controls in a `.pv-top-side` /
   `.sv-top-side` span that carries the inset; `#view-car` has bare `.back-btn`
   children in a `space-between` row, so they go all the way out.
   Fixed here rather than in the markup because the span exists to group several
   controls, and Garage has exactly one each side — the inset is all it needed.
   Shop was built from this markup and inherited the same fault (shop-c.css). */
body.gv-on #view-car .topbar-row {
  padding-left: 16px;
  padding-right: 16px;
  gap: 8px;
}

/* ── Section headings ───────────────────────────────────────────────────
   Same scale as Travel's .tv-sec, so the two sections read as one app. */
body.gv-on .gv-sec,
body.gv-on .sheet h3.gv-sec {
  font-size: 11px; font-weight: 700; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--dv2-text-sub);
  margin: 18px 2px 8px;
  display: flex; align-items: center; gap: 8px;
}
body.gv-on .gv-sec .gv-sec-grow { flex: 1; }
body.gv-on .gv-sec button {
  background: none; border: 0; padding: 2px; color: var(--dv2-text-sub);
  display: flex; cursor: pointer; font-family: inherit;
}
body.gv-on .gv-sec button:active { transform: scale(.92); }

/* ── THE ENTITY TILE ────────────────────────────────────────────────────
   One primitive. New components reach for `.gv-tile` rather than inventing
   a row style. */
body.gv-on .gv-tile {
  display: block;
  background: var(--gv-tile-bg);
  border: 1px solid var(--gv-tile-bd);
  border-radius: var(--gv-rad);
  padding: 12px 13px;
  margin: 0 0 9px;
  box-shadow: var(--gv-shadow);
  width: 100%;
  text-align: left;
  color: var(--dv2-text-primary);
  font-family: inherit;
}
body.gv-on .gv-tile:last-child { margin-bottom: 0; }
body.gv-on button.gv-tile { cursor: pointer; }
body.gv-on button.gv-tile:active { transform: scale(.985); }
@media (prefers-reduced-motion: reduce) {
  body.gv-on button.gv-tile:active { transform: none; }
}
body.gv-on .gv-tile.dashed {
  border-style: dashed; background: transparent; box-shadow: none;
}

body.gv-on .gv-row { display: flex; align-items: center; gap: 11px; }
body.gv-on .gv-grow { flex: 1; min-width: 0; }
body.gv-on .gv-t { font-size: 15px; font-weight: 650; letter-spacing: -0.01em; }
body.gv-on .gv-s { font-size: 12.5px; color: var(--dv2-text-sub); margin-top: 1px; }
body.gv-on .gv-v { font-size: 13px; color: var(--dv2-text-sub); font-weight: 550; }
body.gv-on .gv-strike { text-decoration: line-through; opacity: .55; }

/* ── Status pills ───────────────────────────────────────────────────────
   The same four states Travel uses, and the fourth is the one that matters:
   UNKNOWN. A vehicle with no insurance date on file used to render nothing
   at all, which reads as approval. */
body.gv-on .gv-pill {
  font-size: 11px; font-weight: 700; padding: 3px 9px; border-radius: 100px;
  white-space: nowrap; letter-spacing: 0.01em; flex: none;
}
body.gv-on .gv-pill.good { color: #16704A; background: rgba(22,112,74,0.11); }
body.gv-on .gv-pill.warn { color: #A25C00; background: rgba(162,92,0,0.12); }
body.gv-on .gv-pill.bad  { color: #B3312A; background: rgba(179,49,42,0.10); }
/* b232 — ONE FALLBACK FOR ONE TOKEN. `var(--dv2-link, …)` carried #6341E8 in
   garage-c.css and trips-c.css (30 uses) and #5b4fe0 in pets-c.css,
   health-c.css, home-c.css and one line of THIS file (22 uses) — two different
   literals standing in for the same token, so a screen that lost `--dv2-link`
   fell back to a different indigo depending on which file drew it.
   #5b4fe0 wins because it is `--accent` (style.css:39), a value the app
   already names; #6341E8 appeared nowhere else. The companion washes
   `rgba(99,65,232,…)` are that same literal in decimal and moved with it. */
body.gv-on .gv-pill.info { color: var(--dv2-link, #5b4fe0); background: rgba(91,79,224,0.10); }
body.gv-on .gv-pill.unknown {
  color: var(--dv2-text-sub); background: transparent;
  border: 1px dashed var(--gv-tile-bd);
}
html[data-theme="dark"] body.gv-on .gv-pill.good { color: #5DD48F; background: rgba(93,212,143,0.14); }
html[data-theme="dark"] body.gv-on .gv-pill.warn { color: #E5A44A; background: rgba(229,164,74,0.15); }
html[data-theme="dark"] body.gv-on .gv-pill.bad  { color: #FF8A80; background: rgba(255,138,128,0.13); }
html[data-theme="dark"] body.gv-on .gv-pill.info { color: #9B85FF; background: rgba(155,133,255,0.15); }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.gv-on .gv-pill.good { color: #5DD48F; background: rgba(93,212,143,0.14); }
  html:not([data-theme="light"]) body.gv-on .gv-pill.warn { color: #E5A44A; background: rgba(229,164,74,0.15); }
  html:not([data-theme="light"]) body.gv-on .gv-pill.bad  { color: #FF8A80; background: rgba(255,138,128,0.13); }
  html:not([data-theme="light"]) body.gv-on .gv-pill.info { color: #9B85FF; background: rgba(155,133,255,0.15); }
}

/* ── Line icons ─────────────────────────────────────────────────────────
   One icon system. `.dv2-lineic` carries the stroke properties and
   deliberately no size, so callers own that. */
body.gv-on .gv-ic {
  width: 21px; height: 21px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.9;
  stroke-linecap: round; stroke-linejoin: round;
}
body.gv-on .gv-ic-sm { width: 17px; height: 17px; }
body.gv-on .gv-ic-lg { width: 26px; height: 26px; }

/* ── THE NUMBER PLATE ───────────────────────────────────────────────────
   The section's identity object. style.css already ships `.veh-plate` with
   the blue band; this is the Concept C sizing, and it is namespaced so the
   two cannot collide.

   Deliberately NOT themed. A plate is a physical object with fixed colours —
   white front, yellow rear, blue band — and recolouring it for dark mode
   would make it stop being a plate. */
body.gv-on .gv-plate {
  display: inline-flex; align-items: stretch; border-radius: 6px;
  overflow: hidden; flex: none;
  border: 1px solid rgba(0,0,0,0.22);
  box-shadow: 0 1px 4px rgba(0,0,0,0.24);
}
body.gv-on .gv-plate .gb {
  background: #0b3fae; color: #fff; width: 17px;
  display: flex; align-items: center; justify-content: center;
}
body.gv-on .gv-plate .gb b { font-size: 7.5px; font-weight: 800; letter-spacing: .2px; }
body.gv-on .gv-plate .reg {
  background: #fff; color: #111; font-weight: 800; font-size: 19px;
  letter-spacing: 1.5px; padding: 3px 9px;
  font-family: "Arial Narrow", Arial, sans-serif;
  font-variant-numeric: tabular-nums;
}
body.gv-on .gv-plate.sm .reg { font-size: 14px; padding: 3px 7px; letter-spacing: 1px; }
body.gv-on .gv-plate.sm .gb { width: 14px; }
body.gv-on .gv-plate.sm .gb b { font-size: 6.5px; }
body.gv-on .gv-plate.xl .reg { font-size: 32px; padding: 12px 16px; letter-spacing: 2px; }
body.gv-on .gv-plate.xl .gb { width: 30px; }
body.gv-on .gv-plate.xl .gb b { font-size: 11px; }
/* The REAR plate is yellow. Used for the input, because that is the one
   people picture when they think "my reg". */
body.gv-on .gv-plate.rear .reg { background: #FBD413; }
body.gv-on .gv-plate .reg.ph { color: rgba(0,0,0,0.26); }

/* The plate as a text INPUT — the whole first screen of the add flow.
   THE WIDTH IS IN CHARACTERS, not per cent. `width: 100%` on an input inside
   an inline-flex plate resolved against the sheet rather than the plate, so
   the plate stretched past both edges of a 390px screen and the blue band
   bled off the left. A plate is exactly as wide as its longest legal
   registration, which is what `ch` says. */
body.gv-on .gv-plate-input {
  border: 0; outline: none; background: transparent;
  font: inherit; color: inherit; text-align: center;
  text-transform: uppercase; width: 8.6ch; min-width: 0;
  padding: 0; letter-spacing: inherit;
  -webkit-appearance: none; appearance: none; border-radius: 0;
}
/* `ch` measures a zero-width advance and knows nothing about letter-spacing,
   so eight characters at 2px tracking need those 16px adding back or the last
   letter of the placeholder is clipped. */
body.gv-on .gv-plate.xl .gv-plate-input { width: calc(8.6ch + 18px); }
body.gv-on .gv-plate:not(.xl) .gv-plate-input { width: calc(8.6ch + 12px); }
body.gv-on .gv-plate-input::placeholder { color: rgba(0,0,0,0.26); }
body.gv-on .gv-plate-input:focus,
body.gv-on .gv-plate-input:focus-visible { outline: none; box-shadow: none; }
/* The focus ring goes on the PLATE, not on a box drawn inside it — a rounded
   rectangle floating in the middle of a number plate reads as a rendering
   fault, not as focus. */
body.gv-on .gv-plate:focus-within {
  box-shadow: 0 0 0 3px rgba(91,79,224,0.40), 0 1px 4px rgba(0,0,0,0.24);
}
body.gv-on .gv-plate-wrap { display: flex; justify-content: center; max-width: 100%; }
body.gv-on .gv-plate { max-width: 100%; }

/* ── THE VEHICLE CARD — the one exception to the tile ───────────────────
   Full-bleed photograph, plate and one status sentence on a scrim. */
body.gv-on .gv-veh {
  display: block; width: 100%; position: relative;
  border-radius: 20px; overflow: hidden;
  margin: 0 0 14px; padding: 0; border: 0; cursor: pointer;
  background: linear-gradient(160deg, #2E3A5C, #6E6890);
  box-shadow: 0 6px 22px rgba(20,20,60,0.14);
  text-align: left; font-family: inherit;
  min-height: 172px;
}
body.gv-on .gv-veh:active { transform: scale(.99); }
@media (prefers-reduced-motion: reduce) { body.gv-on .gv-veh:active { transform: none; } }
body.gv-on .gv-veh-img {
  width: 100%; height: 190px; object-fit: cover; display: block;
}
body.gv-on .gv-veh.sm .gv-veh-img { height: 128px; }
body.gv-on .gv-veh::after {
  content: ""; position: absolute; inset: 0; background: var(--gv-scrim);
  pointer-events: none;
}
body.gv-on .gv-veh-ovl {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: 13px 14px; display: flex; flex-direction: column; gap: 7px;
  align-items: flex-start;
}
body.gv-on .gv-veh-name {
  color: #fff; font-size: 17px; font-weight: 720; letter-spacing: -0.02em;
  text-shadow: 0 1px 8px rgba(0,0,0,0.42);
}
body.gv-on .gv-veh-line {
  color: rgba(255,255,255,0.88); font-size: 12.5px; font-weight: 560;
  text-shadow: 0 1px 6px rgba(0,0,0,0.4);
}
body.gv-on .gv-veh-line.warn { color: #FFD79A; }
body.gv-on .gv-veh-line.bad  { color: #FFB4AE; }

/* EVERY Garage sheet owns its header, so none of them wants the framework's
   absolutely-positioned ✕. */
body.gv-on #sheet-vehicle > .sheet-closerail,
body.gv-on #sheet-vehicle-papers > .sheet-closerail,
body.gv-on #sheet-vehicle-care > .sheet-closerail,
body.gv-on #sheet-vehicle-event > .sheet-closerail,
body.gv-on #sheet-vehicle-delete > .sheet-closerail,
body.gv-on #sheet-add-vehicle > .sheet-closerail,
body.gv-on #sheet-pet-activity > .sheet-closerail,
body.gv-on #sheet-cropper > .sheet-closerail { display: none; }
/* NOT #sheet-history. It is a shared sheet with no header of its own, so the
   framework's ✕ is the only close control it has — suppressing it left the
   sheet with none, which the audit caught immediately. A blanket rule applied
   to a sheet the section does not own is how you break someone else's screen. */

/* ── The detail hero ────────────────────────────────────────────────────
   Runs under the header, which is why the back and menu buttons are drawn
   INSIDE it rather than in a bar above it. */
/* ── THE COVER PHOTO STARTS BELOW THE STATUS BAR ────────────────────────
   b177 ran it full-bleed to y=0 on the argument that a photograph belongs
   under the status bar. On a device with a Dynamic Island it does not: the
   clock, the signal bars and the island itself sit ON the vehicle, the back
   and edit buttons compete with them for the same 60px, and which of the two
   you can read depends on how light that part of the photograph happens to
   be. A cover image must never overlap a control or a title.
   The safe area is filled with the sheet's own ground; the photo starts under
   it and is still full width. */
body.gv-on .gv-hero {
  position: relative;
  margin: 0 -14px 0;
  padding-top: env(safe-area-inset-top);
  background: var(--gv-sheet-bg);
}
body.gv-on .gv-hero-img { width: 100%; height: 236px; object-fit: cover; display: block; }
/* The scrim covers the PHOTO, not the inset strip above it — running it to
   inset:0 darkened the status-bar area into a grey band. */
body.gv-on .gv-hero::after {
  content: ""; position: absolute;
  inset: env(safe-area-inset-top) 0 0 0;
  background: var(--gv-scrim); pointer-events: none;
}
/* The parent now carries the inset, so the buttons only need their own
   padding — and they sit on the photo, clear of the status bar. */
body.gv-on .gv-hero-top {
  position: absolute; top: env(safe-area-inset-top); left: 0; right: 0; z-index: 3;
  display: flex; justify-content: space-between;
  padding: 10px 14px 12px;
}
body.gv-on .gv-hero-img { min-height: 180px; }
body.gv-on .gv-hero-btn {
  /* 44px, like every other close control in the section. */
  width: 44px; height: 44px; border-radius: 50%; border: 0; cursor: pointer;
  display: grid; place-items: center; color: #fff;
  /* Dark enough to read against a white van in bright sunlight. The scrim
     only starts at 34% down, so these two get no help from it. */
  background: rgba(0,0,0,0.5); backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
body.gv-on .gv-hero-ovl {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  /* The right padding is the width of the photo pill plus its inset. Without
     it the "yours for 7 years" line ran underneath the button. */
  padding: 14px 148px 14px 14px;
  display: flex; flex-direction: column; gap: 8px;
  align-items: flex-start;
}
body.gv-on .gv-hero-photo {
  position: absolute; right: 14px; bottom: 14px; z-index: 3;
  display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
  padding: 7px 12px; border-radius: 999px; border: 0;
  background: rgba(255,255,255,0.86); color: #2a2a34;
  font-size: 12.5px; font-weight: 700; font-family: inherit;
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
}

/* ── THE STATUS ROW, WITH ITS OWN HISTORY INSIDE IT ─────────────────────
   MOT, Insurance and Tax each state where they stand AND hold the run of
   records behind that standing. They used to be a status row up here and a
   separate history section further down the page — the same subject in two
   places, with the vehicle's details and a duplicate activity stream pushed
   below both. One thing, one place, closed until asked for. */
body.gv-on .gv-st { padding: 0; overflow: hidden; }
body.gv-on .gv-st-head {
  display: flex; align-items: center; gap: 11px; width: 100%;
  background: none; border: 0; cursor: pointer; font-family: inherit;
  color: inherit; text-align: left; padding: 12px 13px;
}
body.gv-on .gv-st-head:active { transform: scale(.99); }
@media (prefers-reduced-motion: reduce) {
  body.gv-on .gv-st-head:active { transform: none; }
}
/* Requirement 7 — the count is on the CLOSED row, so "MOT · 4" tells you what
   is inside without opening it. */
body.gv-on .gv-st-count {
  flex: none; font-size: 11px; font-weight: 700; letter-spacing: 0;
  color: var(--dv2-text-muted); font-variant-numeric: tabular-nums;
}
body.gv-on .gv-st-chev {
  width: 16px; height: 16px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
  color: var(--dv2-text-muted); transition: transform .18s ease;
}
body.gv-on .gv-st.open .gv-st-chev { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) {
  body.gv-on .gv-st-chev { transition: none; }
}
body.gv-on .gv-st-body { display: none; padding: 0 13px 12px; }
body.gv-on .gv-st.open .gv-st-body { display: block; }
body.gv-on .gv-st-body::before {
  content: ""; display: block; height: 1px; margin: 0 0 10px;
  background: var(--gv-tile-bd);
}
/* Records inside a status row sit on the sheet, not on a second card — a
   tile inside a tile is the "box on a box" this section was pulled up for. */
body.gv-on .gv-st-body .gv-rec {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 0; border-bottom: 1px solid var(--gv-tile-bd);
}
body.gv-on .gv-st-body .gv-rec:last-of-type { border-bottom: 0; }
body.gv-on .gv-st-body .gv-rec-t { display: block; font-size: 13.5px; font-weight: 620; }
body.gv-on .gv-st-body .gv-rec-s {
  display: block; font-size: 11.5px; color: var(--dv2-text-sub); margin-top: 1px;
  font-variant-numeric: tabular-nums;
}
/* ── WHO TO RING ────────────────────────────────────────────────────────
   The first thing inside a status row, because it is the thing you open the
   row FOR when something has gone wrong. The phone is an anchor, not a
   button: `tel:` is what makes one tap dial it. */
body.gv-on .gv-contact {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 11px; margin-bottom: 10px;
  border-radius: 12px; background: var(--band-soft, rgba(91,79,224,0.07));
  border: 1px solid var(--gv-tile-bd);
}
html[data-theme="dark"] body.gv-on .gv-contact { background: rgba(155,133,255,0.10); }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.gv-on .gv-contact { background: rgba(155,133,255,0.10); }
}
body.gv-on .gv-contact-main {
  flex: 1; min-width: 0; display: flex; flex-direction: column;
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: inherit; color: inherit; text-align: left;
}
body.gv-on .gv-contact-who {
  font-size: 9px; font-weight: 750; letter-spacing: .1em;
  text-transform: uppercase; color: var(--dv2-text-muted);
}
/* A company name may WRAP rather than truncate. "Sterling Insurance Servic…"
   is the one thing on this card you might need to read back to someone. */
body.gv-on .gv-contact-name {
  font-size: 13.5px; font-weight: 650; color: var(--dv2-text-primary);
  margin-top: 1px; line-height: 1.25;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
body.gv-on .gv-contact-tel {
  font-size: 12px; color: var(--dv2-text-sub); margin-top: 1px;
  font-variant-numeric: tabular-nums;
}
body.gv-on .gv-contact-acts { display: flex; gap: 7px; flex: none; }
body.gv-on .gv-contact-btn {
  width: 38px; height: 38px; border-radius: 50%; flex: none;
  display: grid; place-items: center; text-decoration: none;
  background: var(--gv-tile-bg); border: 1px solid var(--gv-tile-bd);
  color: var(--dv2-text-primary);
}
/* The call button is the one that matters at the roadside, so it is the one
   that looks like a button. */
body.gv-on .gv-contact-btn.call {
  background: #16704A; border-color: #16704A; color: #fff;
}
html[data-theme="dark"] body.gv-on .gv-contact-btn.call { background: #2F8F5F; border-color: #2F8F5F; }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.gv-on .gv-contact-btn.call { background: #2F8F5F; border-color: #2F8F5F; }
}
body.gv-on .gv-contact-btn:active { transform: scale(.92); }
@media (prefers-reduced-motion: reduce) {
  body.gv-on .gv-contact-btn:active { transform: none; }
}
body.gv-on .gv-contact.gv-contact-empty {
  width: 100%; cursor: pointer; font-family: inherit; text-align: left;
  background: transparent; border-style: dashed; color: var(--dv2-text-sub);
}
body.gv-on .gv-contact-empty .gv-contact-name {
  color: var(--dv2-link, #5b4fe0); font-weight: 620;
}

/* The facts the collapsed row no longer carries, stated once at the top of
   what it opens into. */
body.gv-on .gv-st-facts {
  font-size: 12px; color: var(--dv2-text-sub); padding-bottom: 8px;
}
body.gv-on .gv-st-add + .gv-st-add { margin-top: 7px; }
/* ── UPLOAD · SCAN · VIEW, on every documented status ───────────────────
   Requirement 6: a household should never have to leave Garage to keep a
   vehicle's records. Scan is the camera, Upload is the file picker — on a
   phone those are genuinely different actions, and offering only one of them
   is why "Scan it instead" was the only route in. */
body.gv-on .gv-docacts {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: 7px; margin-bottom: 10px;
}
body.gv-on .gv-docact {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 9px 4px; cursor: pointer; font-family: inherit;
  font-size: 11px; font-weight: 640; color: var(--dv2-text-sub);
  background: var(--gv-tile-bg); border: 1px solid var(--gv-tile-bd);
  border-radius: 11px; text-decoration: none;
}
body.gv-on .gv-docact .gv-ic { color: var(--dv2-link, #5b4fe0); }
body.gv-on .gv-docact:active { transform: scale(.96); }
@media (prefers-reduced-motion: reduce) {
  body.gv-on .gv-docact:active { transform: none; }
}

body.gv-on .gv-st-add {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  width: 100%; margin-top: 10px; padding: 9px; cursor: pointer;
  background: none; font-family: inherit; font-size: 12.5px; font-weight: 650;
  color: var(--dv2-link, #5b4fe0);
  border: 1px dashed var(--gv-tile-bd); border-radius: 11px;
}
body.gv-on .gv-st-empty {
  font-size: 12.5px; color: var(--dv2-text-sub); padding: 2px 0 4px;
}

/* ── The one status sentence ────────────────────────────────────────────
   Concept C collapses three countdown gauges into one line of English. The
   gauges it replaces wrote literal iOS hexes into their SVG — track #ececf3,
   label #15151f — so the number was near-black on near-black in dark mode. */
body.gv-on .gv-status { display: flex; align-items: center; gap: 11px; }
body.gv-on .gv-status .gv-ic { color: #16704A; }
body.gv-on .gv-status.warn .gv-ic { color: #A25C00; }
body.gv-on .gv-status.bad .gv-ic  { color: #B3312A; }
html[data-theme="dark"] body.gv-on .gv-status .gv-ic { color: #5DD48F; }
html[data-theme="dark"] body.gv-on .gv-status.warn .gv-ic { color: #E5A44A; }
html[data-theme="dark"] body.gv-on .gv-status.bad .gv-ic  { color: #FF8A80; }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.gv-on .gv-status .gv-ic { color: #5DD48F; }
  html:not([data-theme="light"]) body.gv-on .gv-status.warn .gv-ic { color: #E5A44A; }
  html:not([data-theme="light"]) body.gv-on .gv-status.bad .gv-ic  { color: #FF8A80; }
}
body.gv-on .gv-status-sub {
  font-size: 12.5px; color: var(--dv2-text-sub); margin-top: 2px;
}

/* ── THE TIMELINE — "Its life so far" ───────────────────────────────────
   The screen Concept C exists for. Every entry is still a tile; the rail and
   dot sit outside it, so the tile standard holds. */
body.gv-on .gv-tl { display: flex; flex-direction: column; }
body.gv-on .gv-tl-row {
  display: flex; gap: 12px; position: relative; padding-bottom: 9px;
}
body.gv-on .gv-tl-rail {
  position: relative; width: 12px; flex: none; display: flex; justify-content: center;
}
body.gv-on .gv-tl-rail::before {
  content: ""; position: absolute; top: 17px; bottom: -9px; width: 1.5px;
  background: var(--gv-tile-bd);
}
body.gv-on .gv-tl-row:last-child .gv-tl-rail::before { display: none; }
body.gv-on .gv-tl-dot {
  width: 11px; height: 11px; border-radius: 50%; margin-top: 5px; z-index: 1;
  border: 2px solid #16704A; background: var(--dv2-bg, transparent);
  box-shadow: 0 0 0 3px var(--dv2-bg-grad, #F4F2FA);
}
body.gv-on .gv-tl-dot.next { border-color: #A25C00; border-style: dashed; }
body.gv-on .gv-tl-dot.doc  { border-color: var(--dv2-link, #5b4fe0); }
body.gv-on .gv-tl-dot.bad  { border-color: #B3312A; }
html[data-theme="dark"] body.gv-on .gv-tl-dot { border-color: #5DD48F; }
html[data-theme="dark"] body.gv-on .gv-tl-dot.next { border-color: #E5A44A; }
html[data-theme="dark"] body.gv-on .gv-tl-dot.bad { border-color: #FF8A80; }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.gv-on .gv-tl-dot { border-color: #5DD48F; }
  html:not([data-theme="light"]) body.gv-on .gv-tl-dot.next { border-color: #E5A44A; }
  html:not([data-theme="light"]) body.gv-on .gv-tl-dot.bad { border-color: #FF8A80; }
}
body.gv-on .gv-tl-body { flex: 1; min-width: 0; }
body.gv-on .gv-tl-when {
  font-size: 11.5px; color: var(--dv2-text-muted); font-variant-numeric: tabular-nums;
}
body.gv-on .gv-src {
  font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--dv2-text-muted); border: 1px solid var(--gv-tile-bd);
  border-radius: 100px; padding: 1px 6px; flex: none;
}

/* ── The pride count ────────────────────────────────────────────────────
   "Looking after it" opens with what has been DONE, not what is owed. */
body.gv-on .gv-count { text-align: center; padding: 20px 14px; }
body.gv-on .gv-count-n {
  font-size: 40px; font-weight: 800; letter-spacing: -0.04em; line-height: 1;
  color: #16704A; font-variant-numeric: tabular-nums;
}
html[data-theme="dark"] body.gv-on .gv-count-n { color: #5DD48F; }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.gv-on .gv-count-n { color: #5DD48F; }
}
body.gv-on .gv-count-t {
  font-size: 13px; color: var(--dv2-text-sub); margin-top: 6px; line-height: 1.45;
}

/* ── Type picker + template chips ───────────────────────────────────────*/
body.gv-on .gv-seg {
  display: flex; gap: 4px; padding: 4px; width: 100%;
  background: var(--gv-tile-bg); border: 1px solid var(--gv-tile-bd);
  border-radius: 14px;
}
body.gv-on .gv-seg button {
  flex: 1; border: 0; cursor: pointer; font-family: inherit;
  background: transparent; color: var(--dv2-text-sub);
  font-size: 13px; font-weight: 640; padding: 9px 0; border-radius: 10px;
  display: flex; align-items: center; justify-content: center; gap: 6px;
}
body.gv-on .gv-seg button.on {
  background: var(--dv2-link, #5b4fe0); color: #fff;
}
body.gv-on .gv-chips { display: flex; flex-wrap: wrap; gap: 7px; }
body.gv-on .gv-chip {
  font-size: 12.5px; font-weight: 620; padding: 7px 12px; border-radius: 100px;
  border: 1px solid var(--gv-tile-bd); background: var(--gv-tile-bg);
  color: var(--dv2-text-sub); cursor: pointer; font-family: inherit;
}
body.gv-on .gv-chip.on {
  background: var(--dv2-link, #5b4fe0); border-color: var(--dv2-link, #5b4fe0);
  color: #fff;
}

/* ── Buttons ────────────────────────────────────────────────────────────*/
body.gv-on .gv-btn {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  width: 100%; border: 0; cursor: pointer; font-family: inherit;
  background: var(--dv2-link, #5b4fe0); color: #fff;
  border-radius: 14px; padding: 14px; font-size: 15px; font-weight: 700;
  letter-spacing: -0.01em;
}
body.gv-on .gv-btn:disabled { opacity: .45; }
body.gv-on .gv-btn.ghost {
  background: var(--gv-tile-bg); border: 1px solid var(--gv-tile-bd);
  color: var(--dv2-text-primary);
}
body.gv-on .gv-btn.danger { background: rgba(179,49,42,0.10); color: #B3312A; }
html[data-theme="dark"] body.gv-on .gv-btn.danger { background: rgba(255,138,128,0.13); color: #FF8A80; }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.gv-on .gv-btn.danger { background: rgba(255,138,128,0.13); color: #FF8A80; }
}
body.gv-on .gv-btns { display: flex; flex-direction: column; gap: 9px; }

/* ── Banners ────────────────────────────────────────────────────────────*/
body.gv-on .gv-note {
  display: flex; gap: 9px; align-items: flex-start;
  border-radius: 13px; padding: 11px 12px; font-size: 12.5px;
  font-weight: 560; line-height: 1.45;
  color: var(--dv2-link, #5b4fe0); background: rgba(91,79,224,0.10);
}
body.gv-on .gv-note.warn { color: #A25C00; background: rgba(162,92,0,0.12); }
body.gv-on .gv-note.good { color: #16704A; background: rgba(22,112,74,0.11); }
html[data-theme="dark"] body.gv-on .gv-note { color: #9B85FF; background: rgba(155,133,255,0.15); }
html[data-theme="dark"] body.gv-on .gv-note.warn { color: #E5A44A; background: rgba(229,164,74,0.15); }
html[data-theme="dark"] body.gv-on .gv-note.good { color: #5DD48F; background: rgba(93,212,143,0.14); }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.gv-on .gv-note { color: #9B85FF; background: rgba(155,133,255,0.15); }
  html:not([data-theme="light"]) body.gv-on .gv-note.warn { color: #E5A44A; background: rgba(229,164,74,0.15); }
  html:not([data-theme="light"]) body.gv-on .gv-note.good { color: #5DD48F; background: rgba(93,212,143,0.14); }
}

/* ── Sheets ─────────────────────────────────────────────────────────────
   The Garage sheets are the old .sheet.glass. Same glass as everything else,
   and their menus become tiles. */
body.gv-on .sheet.glass {
  background: var(--gv-sheet-bg);
  -webkit-backdrop-filter: blur(22px) saturate(180%);
  backdrop-filter: blur(22px) saturate(180%);
  border-top: 1px solid var(--gv-tile-bd);
  color: var(--dv2-text-primary);
}
body.gv-on .sheet.glass .sheet-head h2,
body.gv-on .sheet.glass .pe-title { color: var(--dv2-text-primary); }

/* The three FULL-PAGE Garage sheets. `.sheet.profile-page:not(.compact)…`
   in style.css paints them `var(--bg)` at a higher specificity than
   `.sheet.glass`, which in dark mode is flat black — so the vehicle, its
   papers and its routine sat on a different ground to the Garage they were
   opened from. Same aurora, so the section reads as one place. */
body.gv-on #sheet-vehicle.profile-page,
body.gv-on #sheet-vehicle-papers.profile-page,
body.gv-on #sheet-vehicle-care.profile-page,
body.gv-on #sheet-vehicle.profile-page:not(.compact):not([hidden]),
body.gv-on #sheet-vehicle-papers.profile-page:not(.compact):not([hidden]),
body.gv-on #sheet-vehicle-care.profile-page:not(.compact):not([hidden]) {
  background: var(--dv2-bg-grad, #F4F2FA);
  background-attachment: fixed;
  color: var(--dv2-text-primary);
}
body.gv-on .gv-body {
  padding: 0 14px 40px;
  /* iOS inflates text inside wide blocks unless told not to, which is why the
     full-width field labels ("WHAT IT IS", "NOTES") rendered visibly larger
     than the ones sitting in a two-up row on the same form. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* ═══════════════════════════════════════════════════════════════════════
   THE GARAGE SHEET HEADER — ONE COMPONENT, EVERY SHEET
   ═══════════════════════════════════════════════════════════════════════

   WHY THIS IS NOW A FRAMEWORK AND NOT A STYLE. The same header defect was
   reported four releases running and fixed four times on whichever screen it
   was reported from. Measuring every Garage sheet at once (devtools/
   audit_garage_modals.py) showed why that could never converge: THREE close
   conventions were in play at the same time, and a Garage sheet could be
   using any of them.

     `.sheet-close`   30x30, position:absolute, offset by
                      `calc(14px + env(safe-area-inset-top))` — a VIEWPORT
                      inset applied to a sheet that starts half way down the
                      screen, so it floated 59px lower than anything laid out
                      in flow beside it. That is the 35px title/close gap the
                      audit found on all five add sheets, and no amount of
                      padding on the title can fix it, because the two are
                      positioned by unrelated systems.

     `.pe-x` / `.pe-head`  the Pets-era header, which Garage inherits on the
                      task form and the photo cropper.

     `.gv-head` buttons at 34x34 — mine, and under the 44px minimum.

   Worse: `ensureSheetCloseButtons()` skips any sheet containing a
   `.pet-edit-body`, which is exactly Add vehicle and Edit vehicle. b180
   removed their own ✕ on the assumption the framework would supply one. It
   does not. Those two screens shipped with NO close control at all.

   So: ONE header. Leading control, centred title, optional trailing action,
   all flex siblings — alignment is then a property of the layout rather than
   an arithmetic coincidence. 44px targets. The framework's absolute ✕ is
   suppressed on every Garage sheet, because a control positioned against the
   viewport cannot be aligned with one positioned in flow. */
body.gv-on .gv-head {
  display: flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 6px 0 14px;
}
body.gv-on .gv-head > button,
body.gv-on .gv-head > .gv-head-sp {
  width: 44px; height: 44px; flex: none;
}
body.gv-on .gv-head > button {
  border: 0; border-radius: 50%; cursor: pointer; padding: 0;
  display: grid; place-items: center; font-family: inherit;
  background: var(--gv-tile-bg); color: var(--dv2-text-primary);
}
body.gv-on .gv-head > button:active { transform: scale(.92); }
@media (prefers-reduced-motion: reduce) {
  body.gv-on .gv-head > button:active { transform: none; }
}

/* THE PETS-ERA HEADER, brought onto the same geometry. Garage opens two
   sheets it did not author — the shared task form and the photo cropper — and
   a section is only consistent if the screens it borrows are too. */
body.gv-on .pe-head {
  display: flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 6px 0 14px; margin: 0;
}
body.gv-on .pe-head .pe-x,
body.gv-on .pe-head .pe-save,
body.gv-on .crop-head .pe-x,
body.gv-on .crop-head .pe-save {
  width: 44px; height: 44px; flex: none;
  border-radius: 50%; display: grid; place-items: center;
}
body.gv-on .pe-head .pe-title,
body.gv-on .crop-head .pe-title {
  flex: 1; min-width: 0; text-align: center;
  font-size: 17px; font-weight: 700; letter-spacing: -0.025em;
}
body.gv-on .crop-head { min-height: 44px; align-items: center; }

/* ONLY A FULL PAGE NEEDS THE TREATMENT. `.sheet.profile-page` is
   `top: 0; height: 100dvh`, so its first child genuinely does start
   underneath the notch and the carrier bar — that is what put the back
   arrow out of reach. There it gets the safe-area inset AND sticky, so it
   stays reachable however far down you have scrolled. `.pet-topbar` has
   been sticky since b152 for the same reason. */
body.gv-on .sheet.profile-page .gv-head {
  position: sticky; top: 0; z-index: 6;
  margin: 0 -14px;
  padding: calc(env(safe-area-inset-top) + 8px) 14px 10px;
  background: var(--gv-sheet-bg);
  -webkit-backdrop-filter: blur(18px) saturate(180%);
  backdrop-filter: blur(18px) saturate(180%);
  border-bottom: 1px solid var(--gv-tile-bd);
}
body.gv-on .gv-head .gv-head-t {
  flex: 1; text-align: center; font-size: 17px; font-weight: 700;
  letter-spacing: -0.025em; color: var(--dv2-text-primary);
}
/* b181 — the old 34x34 header button block lived here. It had the same
   specificity as the 44px rule above and came LATER in the file, so it won on
   every Garage sheet at once: the audit reported "close is 34x34" thirteen
   times from one duplicated declaration. Deleted rather than adjusted, because
   two rules for one thing is how they disagree in the first place. */

/* ── Form fields ────────────────────────────────────────────────────────*/
body.gv-on .gv-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
body.gv-on .gv-label {
  font-size: 11px; font-weight: 700; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--dv2-text-sub);
}
body.gv-on .gv-input {
  background: var(--gv-tile-bg); border: 1px solid var(--gv-tile-bd);
  border-radius: 13px; padding: 12px 13px; font-size: 15px;
  color: var(--dv2-text-primary); font-family: inherit; width: 100%;
}
body.gv-on .gv-input:focus-visible {
  outline: 2px solid var(--dv2-link, #5b4fe0); outline-offset: 1px;
}
/* ── EMPTY DATE FIELDS ──────────────────────────────────────────────────
   A native date input with no value renders as blank on iOS, so "COVER ENDS"
   read as a broken white sliver beside a filled field. The previous attempt
   put the placeholder in `::before` ON THE INPUT — which can never render,
   because a form control is a replaced element and has no generated content
   box. This one is a real sibling, laid over the field and ignoring pointer
   events, shown only while the input is empty. */
/* ── THE DATE CONTROL MUST BE CONTAINED ─────────────────────────────────
   ROOT CAUSE of the overlap reported on Add a policy, Add road tax and Add a
   service. `input[type="date"]` keeps its native `-webkit-appearance` unless
   told otherwise, and a native date control on iOS Safari SIZES ITSELF TO ITS
   CONTENT — `width: 100%` is advisory and loses. So each date input rendered
   at its intrinsic width (~184pt for "27 Aug 2026" plus the picker glyph)
   inside a 160pt grid column, spilled ~24pt to the right, and the left column
   ran underneath the right one. That is the "overlap", the "right column
   clips" and the "broken divider" — one cause, three symptoms.
   It never appeared in the audit because Chrome's date control is NARROWER
   than the column, so the overflow had nothing to overflow by. The audit now
   simulates content sizing (R7) so it cannot hide again.
   `appearance: none` makes the control an ordinary box; `max-width` contains
   it even if a future engine ignores `width` too. */
body.gv-on input.gv-input[type="date"],
body.gv-on input.gv-input[type="time"] {
  -webkit-appearance: none; appearance: none;
  box-sizing: border-box;
  display: block; width: 100%; max-width: 100%; min-width: 0;
  min-height: 46px;
}
/* The value sat CENTRED in the box while every other field's text was left
   aligned — the native date control centres its value on iOS. */
body.gv-on input.gv-input[type="date"]::-webkit-date-and-time-value,
body.gv-on input.gv-input[type="time"]::-webkit-date-and-time-value {
  text-align: left; margin: 0;
}
body.gv-on input.gv-input[type="date"]::-webkit-calendar-picker-indicator {
  margin-left: auto; margin-right: 0; flex: none;
}
/* The same containment for the Pets-era form Garage borrows. */
body.gv-on .pe-input[type="date"],
body.gv-on .pe-input[type="time"] {
  -webkit-appearance: none; appearance: none;
  box-sizing: border-box; width: 100%; max-width: 100%; min-width: 0;
}
body.gv-on .gv-datefield { position: relative; }
body.gv-on .gv-dateph {
  position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
  font-size: 15px; color: var(--dv2-text-muted);
  pointer-events: none; display: none;
}
body.gv-on .gv-datefield input[data-empty="1"] ~ .gv-dateph { display: block; }
body.gv-on .gv-datefield input[data-empty="1"] { color: transparent; }
body.gv-on .gv-datefield input[data-empty="1"]:focus { color: inherit; }

/* The shared task form is Pets-era and its date input is 28px tall against a
   47px text field on the same row. Garage opens it, so Garage fixes it. */
body.gv-on .pe-input[type="date"],
body.gv-on .pe-input[type="time"] { min-height: 46px; }
/* Belt and braces for EVERY control in a split row: nothing may exceed the
   column it was given, whatever its intrinsic size. */
body.gv-on .gv-two input,
body.gv-on .gv-two textarea,
body.gv-on .gv-two select {
  max-width: 100%; min-width: 0; box-sizing: border-box;
}
/* And its last control ended 4px inside the bottom inset — reachable only by
   over-scrolling, which on iOS bounces back. The padding goes on the SHEET,
   which is the scroll container; `.pe-scroll` is a plain block inside it and
   padding there is scrolled away with everything else. */
body.gv-on #sheet-pet-activity,
body.gv-on #sheet-pet-suggest {
  padding-bottom: calc(env(safe-area-inset-bottom) + 46px);
}

/* ── THE SHARED HISTORY SHEET ───────────────────────────────────────────
   Garage opens `#sheet-history`, which it does not own: a heading in flow and
   the framework's ✕ absolutely positioned against the VIEWPORT safe area,
   which put them on different rows, 18px out of line and overlapping.
   Restyled only while a Garage screen is up, so Pets, School and the task
   lists keep exactly what they have. The rail is pulled into flow as a 44px
   row and the heading is given the same height, so the two centre on each
   other by construction. */
body.gv-on #sheet-history > .sheet-closerail {
  position: static; display: flex; justify-content: flex-end;
  align-items: center; height: 44px; margin-bottom: -44px;
}
/* The grabber sits BETWEEN the rail and the heading in the DOM, so the rail's
   negative margin pulled the grabber up and the heading stayed 17px below it.
   It is decorative — nothing in app.js binds to it, and the sheet is dismissed
   from the ✕ or by swiping the sheet itself — so on this one sheet, while a
   Garage screen is up, it stands down and the header is one row. */
body.gv-on #sheet-history > .grabber { display: none; }
body.gv-on #sheet-history .sheet-close {
  position: static; width: 44px; height: 44px; top: auto; right: auto;
}
/* MARGIN, not padding. `padding-right: 54px` reserves the space visually but
   leaves the heading's BOX running under the ✕ — which is a real overlap for
   anything that hit-tests, and is what the audit measures. Margin ends the box
   where the clearance starts. */
body.gv-on #sheet-history > h2:first-of-type,
body.gv-on #sheet-history #history-title {
  min-height: 44px; display: flex; align-items: center;
  padding-right: 0; margin-right: 56px; margin-bottom: 2px;
}
/* `1fr 1fr` is only equal if the items may shrink. A grid item defaults to
   `min-width: auto`, so a column holding a date input — which has an
   intrinsic width — refused to go below it and took 161px against its
   neighbour's 154px. Two columns that are meant to match must be told they
   may. */
body.gv-on .gv-two { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
body.gv-on .gv-two > * { min-width: 0; }
body.gv-on .gv-two .gv-field { margin-bottom: 0; }
body.gv-on .gv-two .gv-input { width: 100%; min-width: 0; }
body.gv-on textarea.gv-input { resize: vertical; min-height: 76px; line-height: 1.5; }

/* ── Empty states ───────────────────────────────────────────────────────
   Every list in the section has one. b173 shipped a Reminders section that
   set innerHTML to '' when empty, leaving a heading with nothing under it. */
body.gv-on .gv-empty {
  text-align: center; padding: 22px 16px;
  border: 1px dashed var(--gv-tile-bd); border-radius: var(--gv-rad);
  color: var(--dv2-text-sub); font-size: 13px; line-height: 1.5;
}
body.gv-on .gv-empty b {
  display: block; color: var(--dv2-text-primary); font-size: 14.5px;
  font-weight: 660; margin-bottom: 4px;
}

/* ── The welcome screen ─────────────────────────────────────────────────
   The rotating car/bike/van photography was the section's best surface and
   it was reachable only while the household had zero vehicles. Concept C
   keeps it as the welcome and gives it the type picker, so the carousel
   finally chooses something. */
body.gv-on .gv-welcome { padding: 10px 0 0; }
body.gv-on .gv-welcome-art {
  width: 100%; height: 200px; border-radius: 20px; overflow: hidden;
  position: relative; background: linear-gradient(160deg, #2E3A5C, #6E6890);
  box-shadow: 0 6px 22px rgba(20,20,60,0.14);
}
body.gv-on .gv-welcome-art img {
  width: 100%; height: 200px; object-fit: cover; display: block;
  transition: opacity .28s ease;
}
@media (prefers-reduced-motion: reduce) {
  body.gv-on .gv-welcome-art img { transition: none; }
}
body.gv-on .gv-welcome h2 {
  font-size: 23px; font-weight: 760; letter-spacing: -0.03em;
  margin: 20px 2px 8px; color: var(--dv2-text-primary);
}
body.gv-on .gv-welcome p {
  font-size: 14.5px; line-height: 1.55; color: var(--dv2-text-sub);
  margin: 0 2px 20px;
}

/* ── Assignee chips + task rows reuse the shared engine's classes ───────
   Garage tasks ARE Home tasks — same table, same row. These only bring the
   legacy row onto the tile standard. */
/* ── THE TASK CARD ──────────────────────────────────────────────────────
   The Home task card's structure, in this namespace: a top row of controls
   around the name, over a labelled two-column grid for the two dates, with
   the history behind a counted chip.

   What it replaces: one flex row carrying a bell, a tick, the name, the two
   dates as prose, an assignee chip, a clock and a bin. At 390px that left the
   name about half the width, so "Check tyre tread & pressure" wrapped to two
   lines and "2 Oct 2027 · Yearly Last done 27 Aug 2026 · Alex Rivera" wrapped
   to three more — and the two dates ran together into one sentence because
   both were inline spans. The grid gives each date a column and a label. */
/* b210 — `.gv-task` is a MODIFIER on the shared `.row.tc-row` now, not the
   row itself. Its old block/padding belonged to the tile this used to be and
   would fight the row's own geometry, so it goes; what stays below is tokens.
   The `.gv-task-top` / `-label` / `-meta` / `.gv-task-del` rules that used to
   follow were inert — the elements they named stopped being drawn at b210 —
   and are deleted at T12 rather than left to be read as live. `.gv-check` was
   named alongside them and is NOT inert; see its own note below. */
/* The NAME takes the room, and the repeat chip takes what is left. Without
   `flex: 1` the span sits at min-content inside a flex parent that allows it
   to shrink, and "Check tyre tread & pressure" broke at every space. */
body.gv-on .gv-rep {
  flex: none; font-size: 10px; font-weight: 720; letter-spacing: .02em;
  padding: 2px 7px; border-radius: 100px;
  color: var(--dv2-text-sub); border: 1px solid var(--gv-tile-bd);
}
body.gv-on .gv-urgent {
  flex: none; width: 16px; height: 16px; border-radius: 50%;
  display: grid; place-items: center; font-size: 11px; font-weight: 800;
  color: #B3312A; background: rgba(179,49,42,0.12);
}
html[data-theme="dark"] body.gv-on .gv-urgent { color: #FF8A80; background: rgba(255,138,128,0.15); }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.gv-on .gv-urgent { color: #FF8A80; background: rgba(255,138,128,0.15); }
}

/* THE DELETE MOVED DOWN HERE. The top row is 296px wide on a 390px phone once
   the sheet and card padding are taken out, and a bell, a tick, an assignee
   chip and a bin left the NAME 137px — so "Check tyre tread & pressure" broke
   at every space. Home's top row carries no delete for the same reason. The
   meta row has the width to spare, and the control stays one tap away rather
   than moving into the edit sheet. */
body.gv-on .gv-k {
  grid-row: 1; font-size: 8.5px; line-height: 1.15; font-weight: 750;
  letter-spacing: .1em; text-transform: uppercase; color: var(--dv2-text-muted);
}
body.gv-on .gv-v {
  grid-row: 2; font-size: 12.5px; line-height: 1.3; font-weight: 650;
  color: var(--dv2-text-primary); font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body.gv-on .gv-v.q { color: var(--dv2-text-sub); font-weight: 550; }
body.gv-on .gv-v.due { color: #A25C00; }
html[data-theme="dark"] body.gv-on .gv-v.due { color: #E5A44A; }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.gv-on .gv-v.due { color: #E5A44A; }
}
body.gv-on .gv-hist {
  grid-row: 1 / span 2; align-self: center; flex: none;
  display: inline-flex; align-items: center; gap: 4px;
  padding: 5px 9px; border-radius: 9999px; cursor: pointer;
  border: 1px solid var(--gv-tile-bd); background: var(--gv-tile-bg);
  color: var(--dv2-link, #5b4fe0);
  font: inherit; font-size: 11.5px; font-weight: 750;
}
body.gv-on .gv-hist .gv-ic { width: 13px; height: 13px; }
/* NOT dead, despite what the b210 note above claimed: `.gv-check` is still
   drawn by the template picker and the suggested-routine checklist (app.js
   30744, 31681). Only its use on the TASK ROW went away, when prvTaskRow took
   over and brought `.box` with it. */
body.gv-on .gv-check {
  width: 22px; height: 22px; border-radius: 7px; flex: none; cursor: pointer;
  border: 1.8px solid var(--gv-tile-bd); background: transparent;
  display: grid; place-items: center; color: #fff; padding: 0;
}
body.gv-on .gv-check.on { background: #16704A; border-color: #16704A; }
html[data-theme="dark"] body.gv-on .gv-check.on { background: #2F8F5F; border-color: #2F8F5F; }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.gv-on .gv-check.on { background: #2F8F5F; border-color: #2F8F5F; }
}
html[data-theme="dark"] @media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) }
body.gv-on .gv-ava {
  width: 24px; height: 24px; border-radius: 50%; flex: none;
  display: grid; place-items: center; font-size: 10px; font-weight: 750;
  color: #fff; background: var(--dv2-link, #5b4fe0);
}
body.gv-on .gv-tools { display: flex; align-items: center; gap: 4px; flex: none; }
body.gv-on .gv-tools button {
  background: none; border: 0; padding: 5px; cursor: pointer;
  color: var(--dv2-text-muted); display: flex; font-family: inherit;
}

/* Two-up navigation tiles (Looking after it / Papers). Stacked rather than a
   row: at 390px the icon beside the label squeezed "Looking after it" onto two
   ragged lines. */
body.gv-on .gv-nav2 { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
body.gv-on .gv-nav2 .gv-tile {
  margin: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
}
body.gv-on .gv-nav2 .gv-ic { color: var(--dv2-text-sub); }

/* ── COLLAPSIBLE GROUPS ─────────────────────────────────────────────────
   The same interaction the rest of the app has used since the beginning:
   a header you tap, a chevron that rotates, rows that hide. Six years of MOT
   certificates open by default buried everything under them — a history is
   reference, and reference is closed until asked for.

   Mirrored in the `gv-` namespace rather than reusing `.section` itself,
   because `.section` brings a completion ring, an n/m counter and a reorder
   grip, none of which mean anything for a run of MOT certificates. Same
   grammar, not the same component. */
body.gv-on .gv-group { margin: 0 0 4px; }
/* The bar holds the toggle and the add side by side, so tapping "+" cannot
   also collapse the group it is adding to. */
body.gv-on .gv-group-bar {
  display: flex; align-items: center; gap: 8px; margin: 18px 2px 8px;
}
body.gv-on .gv-group-head {
  display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0;
  background: none; border: 0; cursor: pointer; font-family: inherit;
  padding: 0;
  font-size: 11px; font-weight: 700; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--dv2-text-sub);
  text-align: left;
}
body.gv-on .gv-group-head .gv-group-name { flex: 1; min-width: 0; }
body.gv-on .gv-group-count {
  flex: none; font-variant-numeric: tabular-nums; letter-spacing: 0;
  color: var(--dv2-text-muted); font-weight: 650;
}
body.gv-on .gv-group-chev {
  width: 16px; height: 16px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
  transition: transform .18s ease;
}
body.gv-on .gv-group.collapsed .gv-group-chev { transform: rotate(-90deg); }
body.gv-on .gv-group.collapsed .gv-group-rows { display: none; }
@media (prefers-reduced-motion: reduce) {
  body.gv-on .gv-group-chev { transition: none; }
}
/* The add button sits beside the chevron and must not toggle the group. */
body.gv-on .gv-group-add {
  flex: none; background: none; border: 0; padding: 2px; cursor: pointer;
  color: var(--dv2-text-sub); display: flex; font-family: inherit;
}
body.gv-on .gv-group-add:active { transform: scale(.92); }

/* ── Spec grid ──────────────────────────────────────────────────────────*/
body.gv-on .gv-spec { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
body.gv-on .gv-spec > div {
  background: var(--gv-tile-bg); border: 1px solid var(--gv-tile-bd);
  border-radius: 13px; padding: 10px 12px;
}
body.gv-on .gv-spec .k {
  font-size: 10px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--dv2-text-sub);
}
body.gv-on .gv-spec .v {
  font-size: 14px; font-weight: 640; color: var(--dv2-text-primary); margin-top: 2px;
}

/* ── THE UPLOAD IS SHOWN WHILE IT HAPPENS ───────────────────────────────
   Scanning from a status row gave no feedback at all: the "Reading document…"
   line was written into the Papers screen, which is not the screen you were
   on. A document takes several seconds to read, and several seconds of
   nothing is indistinguishable from a dead button — the exact complaint the
   last release was raised for. Three states, over whatever screen you started
   from: sending, reading, done. */
/* IN THE MIDDLE, OVER A SCRIM, WHILE IT HAPPENS. b180 put this in a card at
   the bottom of the screen and it was still reported as "no obvious feedback"
   — a 340px card below the fold of whatever you were reading is easy to miss,
   and there is nothing useful to do during the read anyway. Centred and over a
   dimmed ground it cannot be missed, and it makes the wait legible instead of
   ambiguous. The finished state clears itself. */
body.gv-on .gv-upload-scrim {
  position: fixed; inset: 0; z-index: 300;
  background: rgba(20,20,40,.42);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  display: grid; place-items: center;
  padding: calc(env(safe-area-inset-top) + 20px) 20px calc(env(safe-area-inset-bottom) + 20px);
}
body.gv-on .gv-upload {
  position: relative; width: min(320px, 100%);
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 26px 22px 30px; border-radius: 22px;
  background: var(--gv-sheet-bg);
  -webkit-backdrop-filter: blur(22px) saturate(180%);
  backdrop-filter: blur(22px) saturate(180%);
  border: 1px solid var(--gv-tile-bd);
  box-shadow: 0 18px 46px rgba(20,20,40,.3);
  color: var(--dv2-text-primary); text-align: center;
}
body.gv-on .gv-upload-t { font-size: 16px; font-weight: 700; letter-spacing: -.02em; }
body.gv-on .gv-upload-s { font-size: 13px; color: var(--dv2-text-sub); line-height: 1.4; }
body.gv-on .gv-upload .gv-spin { width: 28px; height: 28px; border-width: 3px; color: var(--dv2-link, #5b4fe0); }
body.gv-on .gv-upload .gv-ic { width: 30px; height: 30px; color: #16704A; }
/* The three states, named on screen so the wait is legible. */
body.gv-on .gv-upload-steps {
  display: flex; gap: 6px; margin-top: 4px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--dv2-text-muted);
}
body.gv-on .gv-upload-steps span { opacity: .35; }
body.gv-on .gv-upload-steps span.on { opacity: 1; color: var(--dv2-link, #5b4fe0); }
body.gv-on .gv-upload-steps span.done { opacity: 1; color: #16704A; }
html[data-theme="dark"] body.gv-on .gv-upload-steps span.done { color: #5DD48F; }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.gv-on .gv-upload-steps span.done { color: #5DD48F; }
}
html[data-theme="dark"] body.gv-on .gv-upload .gv-ic { color: #5DD48F; }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.gv-on .gv-upload .gv-ic { color: #5DD48F; }
}
body.gv-on .gv-upload.bad .gv-ic { color: #B3312A; }
html[data-theme="dark"] body.gv-on .gv-upload.bad .gv-ic { color: #FF8A80; }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.gv-on .gv-upload.bad .gv-ic { color: #FF8A80; }
}
/* A progress bar, because "uploading" on a phone is a real wait. */
body.gv-on .gv-upload-bar {
  position: absolute; left: 22px; right: 22px; bottom: 14px; height: 4px;
  border-radius: 100px; background: var(--gv-tile-bd); overflow: hidden;
}
body.gv-on .gv-upload-bar i {
  display: block; height: 100%; width: 0%; border-radius: 100px;
  background: var(--dv2-link, #5b4fe0); transition: width .25s ease;
}
@media (prefers-reduced-motion: reduce) {
  body.gv-on .gv-upload-bar i { transition: none; }
}

/* ── Spinner ────────────────────────────────────────────────────────────*/
body.gv-on .gv-spin {
  width: 16px; height: 16px; border-radius: 50%; flex: none;
  border: 2px solid currentColor; border-top-color: transparent;
  animation: gv-spin .7s linear infinite;
}
@keyframes gv-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  body.gv-on .gv-spin { animation-duration: 2.4s; }
}
body.gv-on .gv-loading {
  display: flex; align-items: center; gap: 9px; justify-content: center;
  padding: 20px; color: var(--dv2-text-sub); font-size: 13px;
}

/* ── Legacy Pets-era classes still present in the Garage markup ─────────
   Nothing renders them any more, but style.css still targets them and a
   stale cached script could. Neutralised rather than fought. */
body.gv-on .petcard,
body.gv-on .pets-more,
body.gv-on .pets-empty { background: transparent; box-shadow: none; }


/* b210 — the list's own bar. gvSheetHead() takes ONE action and that is Add;
   giving the whole routine to someone belongs beside the list it acts on, not
   competing with Add in the header. */
body.gv-on .gv-listbar { display: flex; margin-top: 12px; }
body.gv-on .gv-listbar .gv-btn { width: 100%; justify-content: center; }

/* ══ b220 · ONE CAR PER PAGE ══════════════════════════════════════════════
   The Garage is the vehicle now, not a list of them. What this adds is the
   pager (dots + swipe, mirroring Pets), the spacing the page needs once the
   two half-width tiles became full rows, and the single-button hero top. */

/* Dots. Only drawn for a second vehicle — one car needs no pager. */
body.gv-on .gv-dots {
  display: flex; justify-content: center; align-items: center; gap: 2px;
  padding: 8px 0 2px;
}
/* b221.1 — SECTION_CHROME R6. The widening pill was MINE, invented in b220 and
   briefly written into the contract as canon until the benchmark caught it.
   Pets and Health both change colour and keep the mark 7px; Garage now does too,
   with the 28px target b214.1 argued for. */
body.gv-on .gv-dots button {
  width: 28px; height: 28px; padding: 0; border: 0; border-radius: 50%;
  background: none; cursor: pointer; display: grid; place-items: center;
  -webkit-tap-highlight-color: transparent;
}
body.gv-on .gv-dots button::before {
  content: ''; width: 7px; height: 7px; border-radius: 50%;
  background: var(--dv2-dot-empty-bd); transition: background .15s ease;
}
body.gv-on .gv-dots button.on::before { background: var(--dv2-text-primary); }
@media (prefers-reduced-motion: reduce) {
  body.gv-on .gv-dots button { transition: none; }
}

/* The Vault folder row. Full width, in the household's order. */
body.gv-on .gv-navrow { margin-top: 9px; width: 100%; }

/* Expand / collapse the whole car. Ten sections is too many to open one at a
   time when you are hunting for something — Health has the same pair. */
/* b221 — SECTION_CHROME R3. Two right-aligned accent text links, the Pets
   control, replacing two half-width filled boxes. It is a SECONDARY control and
   was sitting directly under the road-legal card carrying more weight than it. */
body.gv-on .gv-allfold {
  display: flex; justify-content: flex-end; padding: 8px 2px 2px; margin: 0;
}
body.gv-on .gv-allfold button {
  border: 0; background: none; cursor: pointer; padding: 6px 4px; border-radius: 0;
  color: var(--dv2-link, #5b4fe0); font-size: 13.5px; font-weight: 600;
  font-family: inherit;
}

/* b220.2 — THE ACTIONS BELONG ON THE TITLE'S LINE. Put in the fold BODY they
   dropped onto a row of their own under the heading, which read as three
   orphaned circles. The head is a row now: the title (a button), the actions,
   and the chevron (a second button on the same section). */
/* b221 — SECTION_CHROME R4. The chip: 30x30, r9, tinted with Garage's own hue
   (220, the number RENEWAL_SECTIONS already gives it), light and dark values
   written out rather than one dimmed — the b212 lesson. */
body.gv-on .gv-st-ic {
  width: 30px; height: 30px; border-radius: 9px; flex: none;
  display: grid; place-items: center;
  background: hsl(220 70% 93%); color: hsl(220 48% 34%);
}
html[data-theme="dark"] body.gv-on .gv-st-ic {
  background: hsl(220 38% 19%); color: hsl(220 72% 80%);
}
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.gv-on .gv-st-ic {
    background: hsl(220 38% 19%); color: hsl(220 72% 80%);
  }
}

body.gv-on .gv-st-headrow { display: flex; align-items: center; gap: 6px; }
body.gv-on .gv-st-headrow .gv-st-head { flex: 1; min-width: 0; padding-right: 4px; }
body.gv-on .gv-st-chevbtn {
  flex: none; width: 34px; height: 34px; display: grid; place-items: center;
  margin-right: 9px; background: none; border: 0; cursor: pointer;
  color: var(--dv2-text-muted);
}
body.gv-on .gv-st-acts { display: flex; gap: 6px; flex: none; }
body.gv-on .gv-st-acts button {
  width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center;
  background: var(--dv2-g-08-bg); border: 1px solid var(--dv2-g-08-bd);
  color: var(--dv2-text-primary); cursor: pointer;
}
body.gv-on .gv-st-acts button:active { transform: scale(.94); }

/* An odd last cell fills the row. Five details left "MILEAGE" as a half-width
   box with a hole beside it; the count is fixed per vehicle, so the gap is
   permanent rather than a transient state. */
body.gv-on .gv-spec > div:last-child:nth-child(odd) { grid-column: 1 / -1; }

/* b221 — SECTION_CHROME R2. Geometry copied from .pv-cardmenu rather than
   approximated: absolute top-left, 34x34, round, dark translucent with a blur
   so it reads on any photograph. It was 44x44 in the pencil's flex row. */
body.gv-on .gv-burger {
  /* b221.2 — ON THE PHOTOGRAPH, not above it. `.gv-hero` carries
     `padding-top: env(safe-area-inset-top)`, and `top` on an absolutely
     positioned child measures from the PADDING BOX — so a bare `top: 10px`,
     copied from Pets whose hero has no such strip, parked this in the white
     band above the image. `.gv-hero-top` beside it already knew: it offsets by
     the inset. 10px INTO the picture, which is what R2 was always describing. */
  position: absolute; top: calc(env(safe-area-inset-top) + 10px); left: 10px; z-index: 3;
  width: 34px; height: 34px; border: 0; border-radius: 50%;
  display: grid; align-content: center; justify-items: center; gap: 3px;
  cursor: pointer; background: rgba(0, 0, 0, .45); color: #fff;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
body.gv-on .gv-burger span {
  display: block; width: 15px; height: 1.8px; border-radius: 2px;
  background: currentColor;
}
@media (prefers-reduced-motion: reduce) {
  body.gv-on .gv-allfold button:active,
  body.gv-on .gv-st-acts button:active { transform: none; }
}

/* Details and Notes fold like the five records above them, so they need the
   same gap between cards that #vehicle-compliance gives its children. */
body.gv-on .gv-fold { margin-top: 9px; }
/* The notes editor sits inside a fold now; it supplied its own top margin for
   the old always-open block. */
body.gv-on .gv-st-body #vehicle-notes { margin-top: 0; }

/* The hero lost its back button — the page's own topbar is the way out — so
   the remaining control must not be spread to the left by space-between. */
body.gv-on .gv-hero-top-1 { justify-content: flex-end; gap: 8px; }

/* b220.1 — THE HERO'S BLEED MUST MATCH ITS CONTAINER, and it did not.
   `.gv-hero` bleeds -14px because the profile SHEET it used to live in padded
   14px. The car page's `main` pads 16px, so on the page the photo stopped 2px
   short of each edge and, more visibly, pushed #car-hub's scrollWidth 14px past
   its clientWidth — a horizontal scroll on a screen that should have none.
   Scoped to #car-hub so any other use of .gv-hero keeps the old number. */
body.gv-on #car-hub .gv-hero { margin-left: -16px; margin-right: -16px; }
