/* Every component: header, banner, sky field, strip, gauges, verdict, tab bar,
   Control view, Log view, and the boot-status screens. Layout positions these
   pieces; layout.css never sets colours or type. */

/* --- Header --- */

.app-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--s-4) var(--gutter);
  gap: var(--s-3);
}

.app-title {
  font-size: var(--t-head);
  font-weight: var(--w-bold);
}

.header-name-block { display: flex; flex-direction: column; align-items: flex-start; }

.header-right {
  display: flex;
  align-items: center;
  gap: var(--s-2);
}

.mode-pill,
.override-chip {
  display: inline-flex;
  align-items: center;
  height: 1.75rem;
  padding: 0 var(--s-3);
  border-radius: var(--r-pill);
  font-size: var(--t-label);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  min-height: 0;
}

.mode-pill {
  min-height: var(--tap);
  min-width: var(--tap);
}
.header-panel-button { min-height: var(--tap); padding: 0 var(--s-3); border: var(--stroke) solid var(--ink); border-radius: var(--r-pill); background: transparent; color: var(--ink); font: inherit; font-weight: var(--w-medium); cursor: pointer; }

.mode-pill--auto {
  background: var(--ink);
  color: var(--ink-inverse);
}

.mode-pill--observe {
  background: transparent;
  color: var(--ink);
  border: var(--stroke) solid var(--ink);
}

.mode-pill--off {
  background: transparent;
  color: var(--ink);
  border: var(--stroke) dashed var(--line-strong);
}

.override-chip {
  background: transparent;
  color: var(--ink);
  border: var(--stroke) solid var(--ink);
}

/* --- Alert banner --- */

.alert-banner {
  display: flex;
  gap: var(--s-3);
  align-items: flex-start;
  padding: var(--s-4) var(--gutter);
  background: var(--loss-soft);
  color: var(--ink);
}

.alert-banner-icon {
  color: var(--loss);
  width: 1.5rem;
  height: 1.5rem;
}
.alert-banner-icon .icon {
  width: 100%;
  height: 100%;
}

.alert-banner-action {
  color: var(--ink-2);
  margin-top: var(--s-1);
}

.mock-banner {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--gutter);
  background: var(--warn-soft);
  color: var(--ink);
  font-size: var(--t-small);
  font-weight: var(--w-medium);
}
.mock-banner-icon { display: inline-flex; color: var(--ink); }

/* --- Sky field / arc.js --- */

.arc {
  position: relative;
  width: 100%;
  max-width: var(--arc-max-width);
  aspect-ratio: 390 / 384;
  margin: 0 auto;
}

.arc-svg {
  width: 100%;
  height: 100%;
}

.arc-foot-text { fill: var(--on-sky-2); font-size: var(--svg-label); font-weight: var(--w-medium); font-variant-numeric: tabular-nums; }
.arc-figure-text { fill: var(--ink); font-size: var(--svg-figure); font-weight: var(--w-bold); letter-spacing: var(--track-display); font-variant-numeric: tabular-nums; }
.arc-phrase-text { fill: var(--on-sky-2); font-size: var(--svg-phrase); font-weight: var(--w-medium); font-variant-numeric: tabular-nums; }
.arc-node-value { fill: var(--ink); font-size: var(--svg-value); font-weight: var(--w-bold); font-variant-numeric: tabular-nums; }
.arc-node-state { fill: var(--on-sky-2); font-size: var(--svg-state); font-variant-numeric: tabular-nums; }

.arc-freshness {
  margin-top: var(--s-1);
  font-size: var(--t-label);
  font-weight: var(--w-medium);
  color: var(--on-sky-2);
  max-width: 100%;
  text-align: left;
  line-height: var(--lh-tight);
  text-transform: none;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s-2);
}
/* Beats the phone rule that hides the header's Log/Controls buttons (layout.css). */
.arc-freshness .preview-back {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: 0 var(--s-3);
  border-color: var(--line-strong);
  background: var(--ink);
  color: var(--ink-inverse);
  font-size: var(--t-label);
  white-space: nowrap;
}
.arc-freshness-icon { display: inline-block; width: var(--s-3); height: var(--s-3); vertical-align: text-bottom; }
.arc-freshness-icon .icon { width: 100%; height: 100%; }

.arc.is-scrubbing .arc-figure-text, .arc.is-scrubbing .arc-phrase-text { opacity: 0.55; }

/* --- Time strip / strip.js --- */

.strip {
  background: var(--surface);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  height: var(--strip-height);
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.strip-scroll {
  overflow-x: auto;
  overflow-y: hidden;
  height: calc(var(--strip-chart-height) + var(--lanes-height) + var(--strip-readout-h));
  flex: none;
  position: relative;
  -webkit-overflow-scrolling: touch;
  padding-top: 0;
  scrollbar-width: none;
}
.strip-scroll::-webkit-scrollbar { display: none; }

.strip-track {
  position: relative;
  /* Height is set inline by strip.js to match its drawn content exactly. */
}

.strip-svg {
  display: block;
}
.strip-axis { position: absolute; left: 0; bottom: 0; display: block; height: var(--strip-axis-h); overflow: hidden; z-index: 2; pointer-events: none; }
.strip-axis-label { fill: var(--ink-2); font-size: var(--t-label); font-variant-numeric: tabular-nums; }
.strip-axis-label--day { fill: var(--ink); font-weight: var(--w-bold); }
.strip-axis-tick { stroke: var(--ink-2); stroke-width: 1; }
.strip-axis-tick--day { stroke: var(--ink); stroke-width: 1.5; }
.strip-midnight-line { stroke: var(--ink-2); stroke-width: 1; stroke-dasharray: var(--dash-idle); opacity: 0.22; pointer-events: none; }

.strip-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.strip-zone-label,
.strip-empty-placeholder {
  position: absolute;
  color: var(--ink-2);
}
.strip-zone-label {
  transition: opacity var(--d-ui) var(--ease-out);
}
/* The hover pill can sit over a day name or price warning: fade the label
   rather than let the pill half-cover its text. */
.strip.is-hovering .strip-zone-label {
  opacity: 0;
}

/* Price chart: buy above the centre line, sell below. Tones come from
   priceTone(): green = good for the house, grey = ordinary, red = costs it. */
.strip-price-zero { stroke: var(--line-strong); stroke-width: 1; }
.strip-price-hit { fill: transparent; cursor: pointer; }
.strip-price-hit:focus-visible { outline: none; stroke: var(--focus); stroke-width: var(--stroke); }
.strip-price-bar { pointer-events: none; }
.strip-price-now { fill: var(--ink); fill-opacity: 0.08; pointer-events: none; }
.strip-price-now.is-stale { fill-opacity: 0; stroke: var(--ink-2); stroke-width: var(--stroke); stroke-dasharray: var(--dash-idle); }
.strip-price-bar--good-2 { fill: var(--green); }
.strip-price-bar--good-1 { fill: color-mix(in srgb, var(--green) var(--price-tint), transparent); }
.strip-price-bar--neutral { fill: color-mix(in srgb, var(--ink-2) var(--price-tint-neutral), transparent); }
.strip-price-bar--bad-1 { fill: color-mix(in srgb, var(--loss) var(--price-tint), transparent); }
.strip-price-bar--bad-2 { fill: var(--loss); }
.strip-price-label {
  fill: var(--ink-2);
  font-size: var(--t-label);
  font-weight: var(--w-bold);
  font-variant-numeric: tabular-nums;
  paint-order: stroke; stroke: var(--surface); stroke-width: var(--label-halo); stroke-linejoin: round;
}
.strip-price-label--good-2, .strip-price-label--good-1 { fill: var(--green-text); }
.strip-price-label--bad-2, .strip-price-label--bad-1 { fill: var(--loss); }
.strip-price-side { fill: var(--ink-2); font-weight: var(--w-medium); letter-spacing: var(--track-label); }
.strip-price-inside { font-size: var(--t-label); font-weight: var(--w-bold); font-variant-numeric: tabular-nums; pointer-events: none; fill: var(--ink); }
.strip-price-inside--good-2, .strip-price-inside--bad-2 { fill: var(--ink-inverse); }
.strip-line-label { fill: var(--ink-2); font-size: var(--svg-state); font-weight: var(--w-medium); paint-order: stroke; stroke: var(--surface); stroke-width: var(--label-halo); stroke-linejoin: round; }
.strip-line-label--solar { fill: var(--ink-2); }

.strip-now-line {
  position: absolute;
  top: calc(var(--strip-pill-top) + var(--strip-pill-h));
  width: var(--stroke-heavy);
  background: var(--ink);
  transform: translateX(-50%);
  pointer-events: none;
}
.strip-playhead { position: absolute; left: 30%; top: 0; bottom: 0; z-index: 2; pointer-events: none; }
.strip-playhead .strip-now-pill { pointer-events: auto; }
.strip-now-marker { position: absolute; top: calc(var(--strip-pill-top) + var(--strip-pill-h)); width: var(--stroke-heavy); height: 100%; background: var(--ink); transform: translateX(-50%); font-size: var(--t-label); color: var(--ink-2); }
/* While a selection moves the NOW pill away, "now" sits in the pill's empty row
   above the line, never beside it where it would cover the first price. */
.strip-now-marker::before { content: attr(data-label); position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); padding-bottom: var(--s-1); white-space: nowrap; }
.strip--fixed .strip-scroll { overflow-x: hidden; }

.strip-now-pill {
  position: absolute;
  top: var(--strip-pill-top);
  height: var(--strip-pill-h);
  transform: translateX(-50%);
  width: max-content;
  white-space: nowrap;
  background: var(--ink);
  color: var(--ink-inverse);
  font-size: var(--t-label);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  padding: var(--s-1) var(--s-3);
  border-radius: var(--r-pill);
  pointer-events: auto;
  cursor: pointer;
  border: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
.strip-now-word { text-transform: uppercase; letter-spacing: var(--track-label); }
.strip-now-price.is-positive { color: var(--ink-inverse); }
.strip-now-price.is-negative { color: var(--ink-inverse); }
.strip-now-price { text-transform: none; letter-spacing: normal; }

.strip-readout {
  /* The selection readout grows upward over the CAR lane, leaving the axis
     ticks visible at all times. */
  min-height: var(--strip-axis-h);
  max-height: calc(3 * var(--t-small) * var(--lh-body));
  overflow: hidden;
  padding: 0 var(--gutter);
  font-size: var(--t-small);
  line-height: var(--lh-body);
  color: var(--ink-2);
  position: absolute;
  z-index: 3;
  left: 0;
  right: 0;
  bottom: 0;
  background: transparent;
  top: auto;
  display: none;
}
/* A live selection's readout uses full ink over the axis. */
.strip-readout.is-selected {
  display: block;
  color: var(--ink);
  background: var(--surface);
  bottom: var(--strip-axis-h);
}

.strip-lanes {
  position: absolute;
  left: 0;
  bottom: var(--strip-axis-h);
  width: 100%;
  height: var(--lanes-height);
  overflow: hidden;
}
.strip-lanes-svg {
  display: block;
  overflow: visible;
}
.strip-lane-baseline {
  stroke: var(--line-strong);
  stroke-width: 1;
}
.strip-lane-fill {
  opacity: var(--lane-wash-opacity);
}
.strip-lane-line {
  fill: none;
  stroke-width: var(--stroke);
}
.strip-lane-hatch-line {
  stroke-width: var(--stroke);
}
.strip-lane-assumed {
  opacity: var(--lane-hatch-opacity);
}
.strip-lane-assumed-line {
  fill: none;
  stroke-width: var(--stroke);
  stroke-dasharray: var(--dash-idle);
  stroke-dashoffset: var(--dash-idle-gap);
}
.strip-lane-now,
.strip-lane-hover {
  stroke: var(--ink);
  stroke-width: var(--stroke-heavy);
}
.strip-lane-label {
  fill: var(--ink-2);
  font-size: var(--t-label);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
}
/* The HOME outlook band starts under the lane label; the same halo as the
   chart's line labels keeps the text clean over it. */
.strip-lane-label,
.strip-lane-value,
.strip-lane-forecast-label {
  paint-order: stroke;
  stroke: var(--surface);
  stroke-width: var(--label-halo);
  stroke-linejoin: round;
}
.strip-lane-forecast-label {
  fill: var(--ink);
  font-size: var(--t-label);
  font-weight: var(--w-medium);
  font-variant-numeric: tabular-nums;
}
.strip-charge-window {
  fill: var(--ink);
  opacity: var(--charge-window-opacity);
}
.strip-charge-window.is-draggable { cursor: ew-resize; touch-action: none; }
.strip-charge-hit { fill: transparent; cursor: ew-resize; touch-action: none; }
.strip-charge-grip {
  position: absolute;
  z-index: 3;
  width: var(--tap);
  height: var(--tap);
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
  cursor: ew-resize;
  touch-action: none;
}
.strip-charge-grip[hidden] { display: none; }
.strip-charge-grip-mark {
  width: var(--s-3);
  height: var(--s-5);
  border-radius: var(--r-pill);
  background: var(--ink-2);
  border: var(--stroke) solid var(--ink);
  box-shadow: 0 0 0 var(--stroke) var(--surface);
}
.strip-charge-grip:hover .strip-charge-grip-mark,
.strip.is-plug-dragging .strip-charge-grip-mark { background: var(--ink); }
.strip-charge-grip:focus-visible { outline: var(--stroke) solid var(--focus); outline-offset: 0; border-radius: var(--r-pill); }
.strip.is-plug-dragging, .strip.is-plug-dragging .strip-charge-hit { cursor: grabbing; }
.strip-charge-error { position: absolute; z-index: 4; bottom: var(--strip-axis-h); left: var(--s-4); margin: 0; padding: var(--s-2); background: var(--surface); color: var(--loss); }
.strip-charge-edge {
  stroke: var(--ink-2);
  stroke-width: 1;
  stroke-dasharray: var(--dash-idle);
}
.strip-charge-line {
  fill: none;
  stroke-width: var(--stroke-heavy);
  stroke-linecap: round;
  stroke-linejoin: round;
}
.strip-charge-label {
  fill: var(--ink);
  font-size: var(--t-label);
  font-weight: var(--w-medium);
  paint-order: stroke;
  stroke: var(--surface);
  stroke-width: var(--label-halo);
  stroke-linejoin: round;
}
.strip-lane-value {
  fill: var(--ink);
  font-size: var(--t-label);
  font-weight: var(--w-bold);
  font-variant-numeric: tabular-nums;
  letter-spacing: normal;
}

/* Battery outlook: a quiet, honest "this is a guess" on the HOME lane, right
   of NOW. Same charge colour (soc-low/mid/high) as the lane's own live line.
   Paint order: the hatched fill-under-line area (.strip-outlook-band, a
   pattern of diagonal strokes at --outlook-hatch-opacity — never a solid
   fill, so it never reads as a real reading), then the low–high range as a
   stronger solid wash on top (.strip-outlook-wash, no hatch), then the
   dotted line, then the peak dot. */
.strip-outlook-band {
  opacity: var(--outlook-hatch-opacity);
}
.strip-outlook-wash {
  opacity: var(--outlook-wash-opacity);
}
.strip-outlook-line {
  fill: none;
  stroke-width: var(--stroke-heavy);
  /* Reuses the "dotted = not settled yet" dash token: a modelled guess is not
     a live or idle reading, and dotted is the vocabulary closest to that. */
  stroke-dasharray: var(--dash-planned);
}
.strip-outlook-peak-dot {
  stroke: var(--surface);
  stroke-width: 1;
}

/* Desktop hover ghost cursor: a dashed idle-style line plus a small outlined
   (never solid, so it never reads as the live NOW pill) data pill that
   follows the pointer. Supplementary to the touch/drag readout, so it is
   aria-hidden; its content comes from the same describeTime() the readout
   uses. */
.strip-hover {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}
.strip-hover-line {
  position: absolute;
  top: calc(var(--strip-pill-top) + var(--strip-pill-h));
  bottom: 0;
  width: 0;
  border-left: var(--stroke) dashed var(--line-strong);
  transform: translateX(-50%);
  opacity: 0;
  transition: opacity var(--d-ui) var(--ease-out);
}
.strip-hover.is-active .strip-hover-line {
  opacity: 1;
}
.strip-hover-pill {
  position: absolute;
  top: var(--strip-pill-top);
  width: max-content;
  white-space: nowrap;
  background: var(--surface);
  color: var(--ink);
  border: var(--stroke) dashed var(--line-strong);
  border-radius: var(--r-small);
  padding: var(--s-1) var(--s-2);
  box-shadow: var(--shadow);
  /* Opaque and shown/hidden instantly (no transition): a mid-fade partial
     opacity would let the chart lines behind it show through. */
  opacity: 0;
}
.strip-hover.is-active .strip-hover-pill {
  opacity: 1;
}
.strip-hover-time {
  font-size: var(--t-label);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-2);
}
.strip-hover-row {
  font-size: var(--t-small);
  font-variant-numeric: tabular-nums;
  line-height: var(--lh-tight);
}
@media (prefers-reduced-motion: reduce) {
  .strip-hover-line,
  .strip-zone-label {
    transition: none;
  }
}
@media (hover: none), (pointer: coarse) {
  .strip-hover { display: none; }
}

/* --- Device gauges / gauges.js --- */

.gauges {
  padding: var(--s-5) var(--gutter);
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}

/* --- Weekly forecast, below the energy story --- */
.weather-host { border-top: 1px solid var(--line); }
.weather-forecast { padding: var(--s-5) var(--gutter) var(--s-6); }
.weather-heading { margin: 0 0 var(--s-3); color: var(--ink-2); }
.weather-placeholder { margin: 0; }
.weather-days { display: grid; grid-template-columns: repeat(7, minmax(calc(var(--s-6) + var(--s-6)), 1fr)); gap: var(--s-2); overflow-x: auto; }
.weather-day { min-width: calc(var(--s-6) + var(--s-6)); min-height: var(--tap); padding: var(--s-2) var(--s-1); display: flex; flex-direction: column; align-items: center; gap: var(--s-1); color: var(--ink); background: transparent; border: 0; border-radius: var(--r-small); font: inherit; cursor: pointer; }
.weather-day:hover { background: var(--surface); }
.weather-day:focus-visible { outline: var(--stroke) solid var(--focus); outline-offset: var(--s-1); }
.weather-day-name { white-space: nowrap; }
.weather-mark { height: 1.5rem; display: inline-flex; align-items: center; justify-content: center; }
.weather-mark { position: relative; }
.weather-mark .icon { position: absolute; width: 1.35rem; height: 1.35rem; }
.weather-sun { width: 1rem; height: 1rem; border: var(--stroke) solid var(--sun); border-radius: 50%; background: linear-gradient(to top, var(--sun) calc(var(--sun-fill) * 100%), transparent 0); }
.weather-temperatures { display: flex; flex-direction: column; align-items: center; gap: 0; font-variant-numeric: tabular-nums; line-height: 1.1; }
.weather-rain, .weather-solar-estimate { font-variant-numeric: tabular-nums; white-space: nowrap; }
/* The battery outlook's one line for tomorrow: full width below the narrow
   day columns (one of those would wrap the sentence into several lines),
   quiet like the detail line below it. */
.weather-outlook-note { margin: var(--s-2) 0 0; }
.weather-detail { min-height: 1.25rem; margin: var(--s-3) 0 0; }
.weather-forecast.is-stale { opacity: 0.65; }

/* At the wide breakpoint the tapped day's summary floats instead of using
   the reserved .weather-detail line above (layout.css hides that line there;
   weather.js shows this instead) — same floating mechanism and visual style
   as the car detail panel below (.gauge-detail): position: fixed, anchored
   from the card's own getBoundingClientRect() so it overlays the page
   instead of pushing it. */
.weather-float {
  position: fixed;
  overflow-y: auto;
  overscroll-behavior: contain;
  z-index: var(--z-float-panel);
  background: var(--ground);
  color: var(--ink-2);
  border-radius: var(--r-panel);
  box-shadow: var(--shadow);
  padding: var(--s-5);
  margin: 0;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-0.375rem);
  transition: opacity var(--d-ui) var(--ease-out), transform var(--d-ui) var(--ease-out), visibility 0s linear var(--d-ui);
}
.weather-float.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition: opacity var(--d-ui) var(--ease-out), transform var(--d-ui) var(--ease-out);
}

.gauge-row {
  display: grid;
  grid-template-columns: 5rem 1fr auto var(--gauge-chevron);
  grid-template-rows: auto auto;
  align-items: center;
  column-gap: var(--s-3);
  row-gap: var(--s-1);
}

.gauge-label {
  grid-row: 1 / 3;
}

.gauge-track-wrap {
  display: block;
  position: relative;
}

.gauge-track {
  /* Explicit now that the track sits inside .gauge-track-wrap rather than as
     a direct grid item: a grid item's display is "blockified" for free, but
     this span needs it spelled out to size at all once it isn't one. */
  display: block;
  height: var(--gauge-track-h);
  border-radius: var(--r-pill);
  background: var(--track);
  overflow: hidden;
  position: relative;
}
.gauge-track--planned {
  background: transparent;
  border: var(--stroke) dotted var(--line-strong);
}
.gauge-track--offline {
  background: transparent;
  border: var(--stroke) dashed var(--line-strong);
}

.gauge-fill {
  display: block;
  height: 100%;
  background: var(--blue);
  border-radius: var(--r-pill);
  transition: width var(--d-ui) var(--ease-out);
}
.gauge-fill--hatched {
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, var(--blue) 0 3px, transparent 3px 6px);
  opacity: 0.7;
}

/* The car's charge target: a thin line at the target level, spanning the
   full track height. */
.gauge-target {
  position: absolute;
  top: 0;
  bottom: 0;
  transform: translateX(-50%);
  pointer-events: none;
}
.gauge-target-line {
  display: block;
  width: var(--stroke-heavy);
  height: 100%;
  background: var(--ink);
  border-radius: var(--r-pill);
}

.gauge-value {
  min-width: 4ch;
  text-align: right;
}

/* State and the quiet ETA line share one flex group so a wide card can flow
   them onto a single line; a narrow one keeps them stacked. Whether the
   "done ~" segment fits is decided in JS (gauges.js `fitEtaDone`), which
   measures the real, laid-out text widths rather than guessing a fixed
   breakpoint — the state text and the time-to-target vary in length, so a
   plain container query here would sometimes hide "done ~" with room to
   spare, or keep it and let the line overflow. */
.gauge-status {
  grid-column: 2 / -1;
  grid-row: 2;
  display: flex;
  flex-direction: column;
  row-gap: var(--s-1);
  min-width: 0;
}
.gauge-state {
  min-width: 0;
  overflow-wrap: anywhere;
}
.gauge-eta {
  min-width: 0;
  overflow-wrap: anywhere;
}
.gauge-eta-time,
.gauge-eta-done {
  white-space: nowrap;
}
.gauge-eta-done::before {
  content: " · ";
}
@media (min-width: 45rem) {
  /* Wide: one line, "state · ETA · done ~" — flex-wrap is only a last-resort
     safety net; `fitEtaDone` keeps content within one line by dropping
     "done ~" first. */
  .gauge-status {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: var(--s-1);
    row-gap: 0;
  }
  /* A leading space in generated content collapses away (it opens the ETA
     flex item's own line box), so the gap before "·" comes from column-gap
     above instead; only the trailing space here is real, kept content. */
  .gauge-eta::before {
    content: "· ";
  }
}

.gauge-row--button {
  width: 100%;
  min-height: var(--tap);
  padding: 0;
  background: transparent;
  border: 0;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.gauge-row--button:focus-visible { outline: var(--stroke) solid var(--focus); outline-offset: var(--s-1); }
.gauge-chevron { grid-column: 4; grid-row: 1; display: inline-flex; transform: rotate(90deg); transition: transform var(--d-ui) var(--ease-out); }
.gauge-chevron .icon { width: var(--gauge-chevron); height: var(--gauge-chevron); }
.gauge-row--button.is-open .gauge-chevron { transform: rotate(270deg); }
.gauge-state--fault { color: var(--loss); }

/* The car detail opens as a floating panel anchored below the whole gauges
   card (never below just its own row), matching the card's own width and
   styling, so it overlays whatever sits under the card instead of pushing it
   down. `position: fixed` is deliberate: none of its ancestors set a
   transform/filter/contain, so it stays viewport-relative and gauges.js
   repositions it on scroll/resize/content-size change. It stays in the DOM
   (never `[hidden]`) while transitioning, or the display:none jump would
   make the transition invisible; gauges.js sets `hidden` back on once the
   close transition ends. */
.gauge-detail {
  position: fixed;
  z-index: var(--z-float-panel);
  background: var(--ground);
  border-radius: var(--r-panel);
  box-shadow: var(--shadow);
  padding: var(--s-5);
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) var(--track);
  overscroll-behavior: contain;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-0.375rem);
  transition: opacity var(--d-ui) var(--ease-out), transform var(--d-ui) var(--ease-out), visibility 0s linear var(--d-ui);
}
.gauge-detail.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition: opacity var(--d-ui) var(--ease-out), transform var(--d-ui) var(--ease-out);
}
.gauge-detail-hint { margin-bottom: var(--s-3); }
.gauge-detail-rows .kv-row { border-top: var(--stroke) solid var(--line); }
.gauge-detail-rows .kv-row:first-child { border-top: 0; }
.gauge-detail-note { margin-top: var(--s-3); }
.gauge-detail--last-known .kv-row > :last-child { color: var(--ink-2); }
@media (prefers-reduced-motion: reduce) {
  .gauge-chevron { transition: none; }
  .gauge-detail { transition: none; }
  .weather-float { transition: none; }
}

/* --- Verdict --- */

.verdict {
  padding: var(--s-2) var(--gutter) 0;
}
.verdict-title {
  margin-bottom: var(--s-2);
}
.verdict-icon { display: inline-flex; color: var(--ink); margin-right: var(--s-2); vertical-align: middle; }
.verdict-icon .icon { width: 1.25rem; height: 1.25rem; }
.verdict-reason {
  color: var(--ink-2);
}

/* --- Tab bar --- */

.tab-bar {
  display: flex;
  background: var(--surface);
  border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom, 0);
}

.tab-btn {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-1);
  padding: var(--s-3) 0 var(--s-2);
  min-height: var(--tap);
  color: var(--ink-2);
  position: relative;
}

.tab-btn.is-active {
  color: var(--ink);
  font-weight: var(--w-bold);
}

.tab-btn.is-active::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 1.75rem;
  height: 3px;
  background: var(--ink);
  border-radius: var(--r-pill) var(--r-pill) 0 0;
}

.tab-label {
  font-size: var(--t-small);
}

/* --- Control view --- */

.control-view {
  padding: var(--s-5) var(--gutter);
}

.control-section {
  padding: 0;
}
.control-section:first-child {
  padding-top: 0;
}

.control-section--collapsible {
  border-top: var(--stroke) solid var(--line);
  padding: var(--s-3) 0;
}
.control-section--collapsible:first-child {
  border-top: none;
  padding-top: 0;
}

.control-section-header-row {
  display: flex;
  align-items: center;
  gap: var(--s-2);
}

.control-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex: 1;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  text-align: left;
  gap: var(--s-3);
  min-height: var(--tap);
  color: inherit;
}

.control-section-header:focus-visible {
  outline: var(--stroke) solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--r-small);
}

.control-section-header h2 {
  margin: 0;
  flex: 1;
}

.control-section-chevron {
  flex: none;
  color: var(--ink-2);
  transition: transform var(--d-ui) var(--ease-out);
}
.control-section-header[aria-expanded="true"] .control-section-chevron {
  transform: rotate(180deg);
}

.help-trigger {
  flex: none;
  width: var(--tap);
  height: var(--tap);
  min-width: var(--tap);
  min-height: var(--tap);
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--ink);
  border: none;
  font-weight: var(--w-bold);
  font-size: var(--t-body);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: background var(--d-ui) ease, color var(--d-ui) ease;
}

.help-trigger:hover {
  background: var(--line);
  color: var(--ink);
}

.help-trigger:focus-visible {
  outline: var(--stroke) solid var(--focus);
  outline-offset: 2px;
}

.help-trigger[aria-expanded="true"] {
  background: var(--ink);
  color: var(--ink-inverse);
}

.control-section-content {
  padding: var(--s-3) 0 0 0;
  overflow: hidden;
}

.control-section-content[hidden] {
  display: none;
}

.help-popover {
  position: fixed;
  z-index: var(--z-popover);
  background: var(--ink);
  color: var(--ink-inverse);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-small);
  font-size: var(--t-small);
  line-height: var(--lh-body);
  max-width: min(18rem, calc(100vw - var(--s-5)));
  box-shadow: var(--shadow);
  overflow-wrap: anywhere;
  pointer-events: auto;
}
.help-popover[hidden] {
  display: none;
}

/* Compact layout: label + help button left, input + unit right, messages full width.
   On mobile (390px) narrow input column; desktop wider. Enum and locked fields stay
   stacked (label, options/value, messages). */
.setting-label-row {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-height: var(--tap);
}
.setting-label-row .setting-label {
  flex: 1;
  min-width: 0;
  overflow-wrap: break-word;
  word-break: break-word;
}
.setting-option-label {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  flex-wrap: wrap;
}

@media (max-width: 44.99rem) {
  .setting-field:not(.setting-field--enum):not(.setting-field--locked) {
    display: grid;
    grid-template-columns: 1fr minmax(4rem, 5.5rem);
    grid-template-rows: auto auto auto;
    gap: var(--s-1) var(--s-2);
    align-items: start;
  }
  .setting-field:not(.setting-field--enum):not(.setting-field--locked) .setting-label-row {
    grid-column: 1;
    grid-row: 1;
    margin: 0;
  }
  .setting-field:not(.setting-field--enum):not(.setting-field--locked) .setting-input-row {
    grid-column: 2;
    grid-row: 1;
    margin: 0;
    width: 100%;
  }
  .setting-field:not(.setting-field--enum):not(.setting-field--locked) .setting-msg {
    grid-column: 1 / -1;
    grid-row: 2;
    margin-top: 0;
  }
}

@media (min-width: 45rem) {
  .setting-field:not(.setting-field--enum):not(.setting-field--locked) {
    display: grid;
    grid-template-columns: minmax(9rem, 1fr) minmax(7rem, 180px);
    grid-template-rows: auto auto auto;
    gap: var(--s-1) var(--s-3);
    align-items: start;
  }
  .setting-field:not(.setting-field--enum):not(.setting-field--locked) .setting-label-row {
    grid-column: 1;
    grid-row: 1;
    margin: 0;
  }
  .setting-field:not(.setting-field--enum):not(.setting-field--locked) .setting-input-row {
    grid-column: 2;
    grid-row: 1;
    margin: 0;
    width: 100%;
  }
  .setting-field:not(.setting-field--enum):not(.setting-field--locked) .setting-msg {
    grid-column: 1 / -1;
    grid-row: 2;
    margin-top: 0;
  }
}

.control-section h2 {
  margin: 0;
}

.control-explain {
  margin-top: var(--s-3);
}

.segmented-control-row {
  display: flex;
  align-items: center;
  gap: var(--s-2);
}

.segmented-control {
  display: flex;
  flex-wrap: nowrap;
  background: var(--surface-2);
  border-radius: var(--r-pill);
  padding: var(--s-1);
  gap: var(--s-1);
  flex: 1;
  min-width: 0;
}

.segmented-btn {
  flex: 1;
  text-align: center;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-pill);
  min-height: var(--tap);
  font-size: var(--t-label);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  white-space: nowrap;
}

.segmented-btn.is-selected {
  background: var(--ink);
  color: var(--ink-inverse);
}

.button-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  margin-top: var(--s-3);
}
.control-section .button-row:first-child {
  margin-top: 0;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  padding: 0 var(--s-5);
  border-radius: var(--r-pill);
  font-weight: var(--w-medium);
  font-size: var(--t-body);
  transition: opacity var(--d-ui) ease;
}

.btn-primary {
  background: var(--ink);
  color: var(--ink-inverse);
}

.btn-secondary {
  background: transparent;
  color: var(--ink);
  border: var(--stroke) solid var(--ink);
}

.btn-destructive {
  background: transparent;
  color: var(--loss);
  border: var(--stroke) solid var(--loss);
}

.btn:hover:not(:disabled) {
  opacity: 0.85;
}
.btn:active:not(:disabled) {
  opacity: 0.7;
}

.duration-picker {
  margin-top: var(--s-3);
}

.kv-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  column-gap: var(--s-4);
  padding: var(--s-2) 0;
}
/* Only the car detail needs the no-wrap label: Control's longer labels wrap as before. */
.gauge-detail-rows .kv-row > :first-child { flex: none; }
.kv-row > :last-child { min-width: 0; text-align: right; }
.kv-value { display: grid; justify-items: end; gap: var(--s-1); }

.control-error,
.node-detail-error {
  margin-top: var(--s-4);
}

.control-refresh-slot {
  border-top: var(--stroke) solid var(--line);
  padding: var(--s-3) 0;
  margin-top: var(--s-3);
}

.account-password-form { display: grid; gap: var(--s-2); }

/* --- Settings form (Control) ---
   Drawn from the server schema. Fields stack in one column at every width; the
   input is capped so numbers do not sprawl across a tablet. Compact for Control. */
.settings-panel { display: grid; gap: var(--s-3); }
.settings-intro-row { display: flex; align-items: center; gap: var(--s-2); margin-bottom: var(--s-1); }
.settings-form,
.settings-fields,
.setting-group { display: grid; gap: var(--s-3); min-width: 0; }
.settings-fields,
.setting-field { border: 0; padding: 0; margin: 0; min-width: 0; }
.setting-group { gap: var(--s-3); }
/* A hairline and a section of space above each group, less below its heading, so
   the heading reads as belonging to the fields under it. */
.setting-group + .setting-group { padding-top: var(--s-3); border-top: var(--stroke) solid var(--line); }
.setting-field { display: grid; gap: var(--s-1); }
.setting-label { padding: 0; } /* a fieldset legend otherwise indents by the browser default */
.setting-help { max-width: 65ch; }
.setting-input-row { display: flex; align-items: center; gap: var(--s-2); margin-top: var(--s-1); }
.setting-input { width: 100%; max-width: 10rem; font-variant-numeric: tabular-nums; }
.setting-input[aria-invalid="true"] { border-color: var(--loss); }
.setting-select { width: 100%; max-width: 12rem; margin-top: var(--s-1); }
/* Flush with the panel's clipped left edge: draw the ring inside the border. */
.setting-select:focus-visible { outline-offset: -2px; }
.setting-msg { min-height: 1.25em; }
.setting-protected { margin-top: var(--s-1); }
.setting-options { display: grid; gap: var(--s-2); margin-top: var(--s-2); }
.setting-option {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  min-height: var(--tap);
  padding: var(--s-3) var(--s-4);
  border: var(--stroke) solid var(--line-strong);
  border-radius: var(--r-small);
  cursor: pointer;
}
.setting-option.is-selected { border-color: var(--ink); background: var(--surface-2); }
/* Unavailable: a dashed outline, the disabled radio and the stated reason carry it;
   the text keeps full contrast so the description stays readable. */
.setting-option.is-unavailable { cursor: default; border-style: dashed; }
.setting-option-text { display: grid; gap: var(--s-1); min-width: 0; }
.setting-option-reason { color: var(--ink); font-weight: var(--w-medium); }
.setting-radio {
  flex: none;
  width: 1.25rem;
  height: 1.25rem;
  min-height: 0;
  padding: 0;
  margin-top: 0.15rem;
}
.settings-advanced-toggle { justify-self: start; }
.settings-advanced { display: grid; gap: var(--s-5); }
.settings-notice {
  display: grid;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-small);
  background: var(--warn-soft);
  color: var(--ink);
}
.settings-review { display: grid; gap: var(--s-3); padding: var(--s-4); border-radius: var(--r-small); background: var(--surface-2); }
.settings-review-list { display: grid; gap: var(--s-1); padding-left: var(--s-5); }
.settings-review .button-row { margin-top: 0; }
.settings-actions { margin-top: 0; }
.setting-count { margin-top: var(--s-2); }
.log-notice { margin-bottom: var(--s-3); }

/* --- Log view --- */

.log-view {
  padding: var(--s-5) var(--gutter);
}

.log-filters {
  display: flex;
  gap: var(--s-2);
  margin-bottom: var(--s-4);
}

.chip {
  padding: var(--s-2) var(--s-4);
  border-radius: var(--r-pill);
  border: var(--stroke) solid var(--line-strong);
  color: var(--ink-2);
  font-size: var(--t-small);
  font-weight: var(--w-medium);
  min-height: var(--tap);
}

.chip.is-selected {
  background: var(--ink);
  color: var(--ink-inverse);
  border-color: var(--ink);
}

.log-day {
  margin-bottom: var(--s-4);
}

.log-day-heading {
  margin-bottom: var(--s-2);
}

.log-row {
  display: grid;
  grid-template-columns: 1.5rem auto minmax(0, 1fr);
  align-items: start;
  gap: var(--s-3);
  padding: var(--s-2) 0;
}

.log-row-icon {
  width: 1.25rem;
  height: 1.25rem;
  color: var(--ink-2);
}
.log-row-icon .icon {
  width: 100%;
  height: 100%;
}

.log-row-time {
  white-space: nowrap;
}

.log-row-message {
  min-width: 0;
  overflow-wrap: anywhere;
}

.log-row--alert .log-row-icon,
.log-row--alert .log-row-message {
  color: var(--loss);
}

.log-row--warn {
  background: var(--warn-soft);
  border-radius: var(--r-small);
  padding: var(--s-2);
  margin: var(--s-1) calc(-1 * var(--s-2));
}

.log-row--info .log-row-icon {
  color: var(--ink-2);
}

.log-empty {
  padding: var(--s-5) 0;
  max-width: 32rem;
}

.log-load-more {
  margin-top: var(--s-3);
}

/* --- Boot status: loading / unreachable --- */

.boot-status {
  padding: var(--s-7) var(--gutter);
  max-width: 28rem;
  margin: 0 auto;
  text-align: left;
}
.offline-sky { width: 100%; max-width: var(--arc-max-width); margin: calc(-1 * var(--s-5)) auto var(--s-4); }
.boot-status-unreachable .btn { margin-top: var(--s-4); }

.boot-status-inner > div > p {
  margin-top: var(--s-3);
}
.boot-status-inner > div > p:first-child {
  margin-top: 0;
}

/* --- Sign-in (login.html) and the Control view's Account section --- */

.login-page {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--ground);
}

.login-body {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s-6) var(--gutter);
  background: var(--ground);
}

.login-panel {
  background: var(--surface);
  border-radius: var(--r-panel);
  box-shadow: var(--shadow);
  padding: var(--s-6);
  width: 100%;
  max-width: 24rem;
}

.login-panel h1,
.login-panel h2 {
  margin-bottom: var(--s-4);
}

.login-form,
.account-password-form {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}

.text-input,
.password-input {
  width: 100%;
}

.login-message {
  margin-top: 0;
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
.form-alert-icon { display: inline-flex; flex: 0 0 auto; }

.login-note {
  margin-top: var(--s-3);
}
