/* Palette: warm greys, a teal for action and a purple for the tire-correction
   layer. Every pair below was checked against WCAG before being written down —
   the previous teal put white button text at 2.6:1, well under the 4.5 needed.

   Two petrol steps on purpose. --teal is dark enough to carry white text; --teal-mark
   is the brighter step used for chart lines and focus rings, where the job is
   hue identity against a surface (3:1) rather than legible text. The bright one
   fails text contrast and the dark one reads as grey on a chart, so neither can
   do both jobs. */
:root {
  /* warm neutrals */
  --bg: #f6f2ee;
  --card: #fffdfb;
  --line: #e5ded6;
  --line-soft: #efe9e2;
  --text: #33302d;
  --heading: #2a2724;
  --muted: #6b625b;

  /* petrol — actions, links, primary data. Taken from the CarLog brand teal
     so the interface and the mark are the same colour, not near-misses. */
  --teal: #0E5A70;
  --teal-dark: #0b4a5c;
  --teal-mark: #1585a8;
  --teal-wash: #e8f3f6;
  --teal-line: #c2dfe8;

  /* purple — reserved for the tire-correction annotation layer. A warm accent
     was tried here and rejected: terracotta sits at dE 3.8 from the danger red,
     so a tire chip and a Delete button would have read as the same colour. */
  --purple: #6d4aa6;
  --purple-wash: #f1ecfa;
  --purple-line: #ddd2f0;

  /* status */
  --danger: #a63f34;
  --danger-wash: #fbeeec;
  --danger-line: #eccdc8;
  --amber: #8a5a12;
  --amber-wash: #fbf2e2;
  --amber-line: #ecdcb8;

  /* legacy aliases, kept so existing rules keep working */
  --cream: var(--bg);
  --success-bg: var(--teal-wash);
  --success-line: var(--teal-line);
  --success-text: var(--teal);

  --radius: 10px;

  /* Two layers rather than one: a tight contact shadow that seats the card on
     the page, and a wider soft one for the light. A single blur reads as a
     drop shadow; the pair reads as an object. */
  --shadow: 0 1px 1px rgba(58, 44, 30, 0.05), 0 1px 3px rgba(58, 44, 30, 0.04);
  --shadow-lift:
    0 1px 1px rgba(58, 44, 30, 0.05),
    0 2px 4px rgba(58, 44, 30, 0.04),
    0 8px 20px rgba(58, 44, 30, 0.06);
  --shadow-pop:
    0 1px 1px rgba(58, 44, 30, 0.06),
    0 4px 10px rgba(58, 44, 30, 0.07),
    0 16px 34px rgba(58, 44, 30, 0.09);

  --teal-deep: #0a4757;

  /* Paper grain. Generated by the browser from ~200 bytes of SVG rather than
     fetched: no request, nothing to cache, and it survives offline like the
     rest of the shell. Rasterised once as a 120px tile and repeated, so the
     cost is one small texture upload however tall the page gets. */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23g)'/%3E%3C/svg%3E");

  --ease: cubic-bezier(0.2, 0.7, 0.3, 1);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--cream);
  color: var(--text);
  font-family: "Segoe UI", Roboto, -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;
  font-size: 17px;
  line-height: 1.45;
}

/* ---------- app chrome ---------- */

.topbar {
  background: var(--teal);
  color: #fff;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  position: sticky;
  top: 0;
  z-index: 10;
}

.topbar .brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: #fff;
  text-decoration: none;
}

.brand-mark { display: block; width: 34px; height: 34px; flex: none; }

.brand-word {
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: 0.06em;
}

/* the mark above the title on the sign-in screen */
.signin-mark { width: 72px; height: 72px; margin: 0 auto 6px; display: block; }

.topbar .spacer { flex: 1; }

main {
  max-width: 720px;
  margin: 0 auto;
  padding: 18px 16px 64px;
}

/* ---------- typography ---------- */

h1 { font-size: 1.9rem; font-weight: 600; color: var(--heading); margin: 8px 0 4px; }
h2 { font-size: 1.35rem; font-weight: 600; color: var(--heading); margin: 26px 0 10px; }
h3 { font-size: 1.1rem; font-weight: 600; color: var(--heading); margin: 18px 0 8px; }
.rule { border: 0; border-top: 1px solid var(--line); margin: 12px 0 18px; }
a { color: var(--teal-dark); }
.muted { color: var(--muted); }
.small { font-size: 0.85rem; }
.center { text-align: center; }

/* ---------- list cards ---------- */

.list {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow-lift);
}

.list-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 18px;
  border-bottom: 1px solid var(--line);
  text-decoration: none;
  color: var(--text);
  background: none;
  border-left: 0;
  border-right: 0;
  border-top: 0;
  width: 100%;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.list-item:last-child { border-bottom: 0; }
.list-item:hover { background: var(--line-soft); }
.list-item > span:first-child { min-width: 0; }
.list-item .sub { display: block; font-size: 0.82rem; color: var(--muted); }
.list-item .chev { color: var(--muted); flex: none; }

.empty {
  background: var(--card);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  padding: 24px 18px;
  text-align: center;
  color: var(--muted);
}

/* On a vehicle page with nothing logged the empty state stands in for the hero
   in exactly the same slot, so it needs the hero's own top margin — without it
   the box sits flush against the buttons. Scoped to the button row rather than
   put on .empty itself: the empty states that sit under a heading already have
   the heading's margin, and would otherwise get both. */
.btn-row + .empty { margin-top: 20px; }

/* ---------- confirmation card ---------- */

.confirm {
  position: relative;
  background: var(--success-bg);
  border: 1px solid var(--success-line);
  border-radius: var(--radius);
  padding: 22px 24px 26px;
  margin-bottom: 26px;
}

.confirm h2 { margin-top: 0; font-size: 2rem; font-weight: 500; }
.confirm .headline { font-size: 1.7rem; color: var(--heading); margin: 0 0 18px; }
.confirm h3 { font-size: 1.45rem; font-weight: 500; margin: 0 0 8px; }
.confirm dl { margin: 0 0 18px; font-size: 0.98rem; }
.confirm dl div { display: flex; gap: 6px; }
.confirm dt { color: var(--success-text); font-weight: 700; }
.confirm dd { margin: 0; }

.confirm .dismiss {
  position: absolute;
  top: 10px;
  right: 14px;
  background: none;
  border: 0;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--muted);
  cursor: pointer;
}

/* ---------- forms ---------- */

.field { margin-bottom: 16px; }

.field-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 6px;
}

label { font-weight: 700; color: var(--heading); }
.hint { color: var(--muted); font-size: 0.85rem; text-align: right; }

input[type="text"], input[type="number"], input[type="date"],
input[type="time"], select, textarea {
  width: 100%;
  padding: 13px 14px;
  font: inherit;
  color: var(--text);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--teal-mark);
  outline-offset: -1px;
}

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 380px) { .grid-2 { grid-template-columns: 1fr; gap: 0; } }

.checks { display: flex; gap: 32px; margin: 18px 0 22px; flex-wrap: wrap; }
.check { display: flex; align-items: center; gap: 10px; font-weight: 700; color: var(--heading); }
.check input { width: 20px; height: 20px; accent-color: var(--teal); }

/* slider with the value bubble from the reference screen; --pct drives both
   the filled track and the bubble's position, and is set from JS on input. */
.slider-wrap { margin: 26px 0 20px; --pct: 50; }
.bubble-row { position: relative; height: 52px; }

.bubble {
  position: absolute;
  bottom: 10px;
  left: calc(17px + (100% - 34px) * var(--pct) / 100);
  transform: translateX(-50%);
  white-space: nowrap;
  border: 1px solid var(--line);
  background: var(--card);
  border-radius: 4px;
  padding: 8px 20px;
  font-size: 1.1rem;
}

.bubble::after {
  content: "";
  position: absolute;
  bottom: -9px;
  left: 50%;
  transform: translateX(-50%);
  border: 9px solid transparent;
  border-top-color: var(--card);
  border-bottom: 0;
}

.slider-label { font-weight: 700; color: var(--heading); margin: 8px 0 4px; }

input[type="range"] {
  width: 100%;
  height: 34px;
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
}

input[type="range"]::-webkit-slider-runnable-track {
  height: 12px;
  border-radius: 6px;
  background: linear-gradient(
    to right,
    var(--teal) 0 calc(var(--pct) * 1%),
    var(--line) calc(var(--pct) * 1%) 100%
  );
}

input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 34px;
  height: 34px;
  margin-top: -11px;
  border-radius: 50%;
  background: #fff;
  border: 1px solid var(--line);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}

input[type="range"]::-moz-range-track { height: 12px; border-radius: 6px; background: var(--line); }
input[type="range"]::-moz-range-progress { height: 12px; border-radius: 6px; background: var(--teal); }
input[type="range"]::-moz-range-thumb {
  width: 32px;
  height: 32px;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: #fff;
}

/* ---------- buttons ---------- */

.btn {
  display: inline-block;
  padding: 13px 20px;
  font: inherit;
  font-weight: 600;
  border-radius: var(--radius);
  border: 1px solid var(--teal);
  background: var(--teal);
  color: #fff;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
}

.btn:hover { background: var(--teal-dark); border-color: var(--teal-dark); }
.btn.secondary { background: var(--card); color: var(--teal-dark); }
.btn.secondary:hover { background: var(--teal-wash); }
.btn.danger { background: var(--card); color: var(--danger); border-color: var(--danger-line); }
.btn.block { display: block; width: 100%; }
.btn-row { display: flex; gap: 12px; margin-top: 22px; flex-wrap: wrap; }
.btn-row .btn { flex: 1 1 160px; }

/* ---------- stats ---------- */


.hero {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 24px 20px;
  text-align: center;
  box-shadow: var(--shadow-lift);
}

.hero .value { font-size: 3rem; color: var(--heading); line-height: 1; }
.hero .label { color: var(--muted); margin-top: 6px; }

/* ---------- economy trend chart ---------- */

.chart { width: 100%; height: auto; display: block; }
.chart-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px; box-shadow: var(--shadow); }

/* ---------- log rows ---------- */

.tag {
  display: inline-block;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 2px 7px;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--muted);
  margin-left: 6px;
  vertical-align: middle;
}

.log-figure { text-align: right; flex: none; }
.log-figure .mpg { display: block; font-size: 1.15rem; color: var(--heading); }
.log-figure .cost { display: block; font-size: 0.82rem; color: var(--muted); }

.banner {
  background: var(--danger-wash);
  border: 1px solid var(--danger-line);
  color: var(--danger);
  padding: 12px 14px;
  border-radius: var(--radius);
  margin-bottom: 16px;
}

.banner.offline { background: var(--amber-wash); border-color: var(--amber-line); color: var(--amber); }

/* sign-in screen */
.signin { max-width: 420px; margin: 8vh auto 0; text-align: center; }
.signin h1 { font-size: 2.2rem; }
.signin .btn-row { margin-top: 26px; }

/* as-calculated figure shown beside its tire-corrected counterpart */
.as-calc {
  display: block;
  font-size: 0.72rem;
  font-weight: 400;
  color: var(--muted);
  letter-spacing: 0;
  text-transform: none;
}

.hero .as-calc { font-size: 0.85rem; margin-top: 2px; }
.log-figure .as-calc { font-size: 0.68rem; }

.corr-note {
  display: inline-block;
  font-size: 0.72rem;
  color: var(--purple);
  border: 1px solid var(--purple-line);
  background: var(--purple-wash);
  border-radius: 999px;
  padding: 1px 8px;
  margin-left: 6px;
  vertical-align: middle;
}

.chart-legend {
  display: flex;
  gap: 16px;
  justify-content: center;
  font-size: 0.78rem;
  color: var(--muted);
  margin-top: 6px;
}
.chart-legend span { display: inline-flex; align-items: center; gap: 6px; }
.chart-legend i { width: 16px; height: 0; border-top: 2.5px solid var(--teal-mark); display: inline-block; }
.chart-legend i.dashed { border-top-style: dashed; border-color: var(--muted); }

/* GPS capture on the fuel-up form */
.link-btn {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 0.85rem;
  color: var(--teal-dark);
  text-decoration: underline;
  cursor: pointer;
}
.link-btn:disabled { color: var(--muted); cursor: not-allowed; text-decoration: none; }

.gps-readout { text-align: left; margin-top: 6px; min-height: 1.2em; }
.gps-error { color: var(--danger); }

/* a value the form shows but does not accept */
.label-text { font-weight: 700; color: var(--heading); }

.readonly-value {
  display: block;
  padding: 13px 14px;
  font: inherit;
  color: var(--muted);
  background: var(--line-soft);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
}

/* ---------- subject line under a heading ---------- */

.subject {
  color: var(--muted);
  font-size: 0.9rem;
  margin: 0 0 14px;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.chip {
  border: 1px solid var(--purple-line);
  background: var(--purple-wash);
  border-radius: 999px;
  padding: 2px 10px;
  font-size: 0.78rem;
  color: var(--purple);
  white-space: nowrap;
}

/* ---------- hero figure: exactly one per screen ---------- */

.hero { margin-top: 20px; }
.hero .value { font-size: 3.4rem; font-weight: 600; letter-spacing: -0.02em; }
.hero .label { color: var(--heading); font-size: 1rem; margin-top: 2px; }
.hero-sub { color: var(--muted); font-size: 0.85rem; margin-top: 6px; }

/* ---------- KPI row ---------- */

.kpi-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-top: 14px;
}

/* Four tiles do not fit across a phone at a legible size, so they wrap to two
   rows there and open out to a single row once there is width for it. */
.kpi-row.four { grid-template-columns: repeat(2, 1fr); }
@media (min-width: 620px) { .kpi-row.four { grid-template-columns: repeat(4, 1fr); } }

/* When a tile was last true. Sits where the delta would, so a tile carries one
   line of context or the other, never two. */
.kpi-when { font-size: 0.72rem; color: var(--muted); margin-top: 3px; }

/* ---------- change, beside a figure ----------
   The sign is on the number and the direction of "good" is in the colour, so
   the number alone is readable if the colour is not. Green for better, red for
   worse, decided by the caller — in litres per 100 km the good direction is
   down, and a rule that assumed "up is good" would colour every improvement as
   a loss. */

.delta { display: block; font-weight: 600; font-variant-numeric: tabular-nums; }
.delta.up { color: var(--teal); }
.delta.down { color: var(--danger); }
.delta.flat { color: var(--muted); font-weight: 400; }
.delta-context { font-weight: 400; color: var(--muted); }
.kpi .delta { font-size: 0.72rem; margin-top: 3px; }
/* On the hero the change is part of the sub-line, not its own block. The
   context stays: a bare "+0.5" sitting next to "over 1,451 mi" reads as if it
   might belong to the distance. */
.hero .delta { display: inline; }
.hero .delta::after { content: ' · '; color: var(--muted); font-weight: 400; }

/* ---------- the totals strip ----------
   One card, three cells, hairlines between. Flatter than a KPI tile on purpose:
   these are context for the figures above rather than figures competing with
   them. */

.totals {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin-top: 14px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}

.totals > div {
  padding: 12px 10px;
  text-align: center;
  border-left: 1px solid var(--line);
}

.totals > div:first-child { border-left: 0; }
.totals-value {
  display: block;
  font-size: 1.15rem;
  font-weight: 600;
  color: var(--heading);
  font-variant-numeric: tabular-nums;
}
.totals-label { display: block; font-size: 0.75rem; color: var(--muted); margin-top: 2px; }

/* ---------- the reporting window ----------
   A segmented control: three options, one tap, and the current one legible as
   the current one without relying on colour alone — it is heavier and sits on
   a filled ground. */

.period-picker {
  display: inline-flex;
  margin-top: 18px;
  padding: 3px;
  gap: 3px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  max-width: 100%;
  overflow-x: auto;
}

.period-picker button {
  flex: 0 0 auto;
  padding: 7px 14px;
  font: inherit;
  font-size: 0.88rem;
  color: var(--muted);
  background: none;
  border: 0;
  border-radius: 7px;
  cursor: pointer;
  white-space: nowrap;
}

.period-picker button:hover { color: var(--heading); background: var(--teal-wash); }
.period-picker button.on {
  color: var(--card);
  background: var(--teal);
  font-weight: 600;
}

/* ---------- the log's own controls ---------- */

.log-controls { margin-bottom: 10px; }
.log-controls input[type="search"] {
  width: 100%;
  padding: 10px 14px;
  font: inherit;
  font-size: 0.92rem;
  color: var(--text);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.log-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 8px;
}

.log-footer p { margin: 0; }

.kpi {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 12px 10px;
  text-align: center;
  box-shadow: var(--shadow);
}

.kpi-value { font-size: 1.5rem; font-weight: 600; color: var(--heading); line-height: 1.15; }
.kpi-unit { font-size: 0.72rem; font-weight: 400; color: var(--muted); margin-left: 3px; }
/* Sentence case, no trailing colon. */
.kpi-label { font-size: 0.78rem; color: var(--muted); margin-top: 4px; }

/* ---------- the long tail of figures, as a table ---------- */

.stat-table {
  margin: 0;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow);
}

.stat-table > div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 11px 16px;
  border-bottom: 1px solid var(--line);
}

.stat-table > div:last-child { border-bottom: 0; }
.stat-table dt { color: var(--muted); font-size: 0.88rem; }
/* Columns of numbers align; the hero and KPI values stay proportional. */
.stat-table dd {
  margin: 0;
  color: var(--heading);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* ---------- fuel-up form ---------- */

/* The three fields typed at every fill-up get a larger target. */
.field.primary input { font-size: 1.35rem; padding: 15px 14px; }
.field-note { margin: -6px 0 18px; text-align: left; }

details.more {
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  margin: 20px 0;
  padding: 4px 0;
}

details.more > summary {
  cursor: pointer;
  padding: 12px 2px;
  font-weight: 600;
  color: var(--teal-dark);
  list-style-position: inside;
}

details.more > summary::marker { color: var(--muted); }
details.more[open] > summary { border-bottom: 1px solid var(--line); margin-bottom: 16px; }

.gps-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  margin: 0 0 18px;
}

.gps-row .gps-readout { font-size: 0.85rem; color: var(--muted); }

/* a log row carries its own map link, so the row is a container rather than
   a single anchor (a nested <a> is invalid and would not work) */
.list-item.log-item {
  display: block;
  padding: 0;
}

.log-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 18px 14px;
  color: var(--text);
  text-decoration: none;
}

.log-link:hover { background: var(--line-soft); }

.log-map {
  display: block;
  padding: 0 18px 13px;
  font-size: 0.78rem;
  color: var(--teal-dark);
  font-variant-numeric: tabular-nums;
}

.chart-legend i.faint { border-top-color: var(--line); }

/* ---------- report ---------- */

.delta {
  display: block;
  font-size: 0.78rem;
  color: var(--muted);
  margin-top: 2px;
}

/* Better/worse is carried by the arrow and the word, never by colour alone —
   red against green sits at dE 5.2 for a deuteranope. */
.delta.better, .delta.worse { color: var(--muted); }
.delta.better { font-weight: 600; }
.delta-word { white-space: nowrap; }

.panel-grid { display: grid; gap: 12px; grid-template-columns: 1fr; }
@media (min-width: 560px) { .panel-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
.panel-grid > * { min-width: 0; }

.panel {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 14px 8px;
  box-shadow: var(--shadow);
}

.panel-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.panel-label { font-size: 0.82rem; color: var(--muted); }
.panel-value { font-size: 1.5rem; font-weight: 600; color: var(--heading); }
.panel-chart { margin-top: 8px; }


/* ---------- vehicle identity: a colour and a silhouette ---------- */

/* A rounded square, matching the buttons, cards and pickers rather than being
   the one circle among them. The radius scales with the badge so a 28px one in
   a list and a 40px one in a heading have the same corner, not the same number
   of pixels — a fixed radius reads as a circle at small sizes and a box at
   large ones.
   
   The account avatar stays a circle on purpose: it is a person, and that is
   the one convention worth keeping distinct from the objects around it. */
.vbadge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--vb-size, 40px);
  height: var(--vb-size, 40px);
  flex: 0 0 auto;
  border-radius: calc(var(--vb-size, 40px) * 0.29);
  background: var(--vb-fill, var(--teal));
  color: var(--vb-ink, #fff);
}

/* The list row already lays out label and chevron; the badge joins that row.
   The badge is excluded from the growing child by name — it is a span and not
   the chevron, so a plainer selector claims it and stretches the disc into an
   ellipse, and it wins on specificity over .vbadge's own flex. */
.list-item.has-badge { display: flex; align-items: center; gap: 12px; }
.list-item.has-badge > span:not(.chev):not(.vbadge) { flex: 1 1 auto; min-width: 0; }

h1.titled { display: flex; align-items: center; gap: 12px; }
.title-badge { display: inline-flex; }

/* Two rows of buttons: silhouettes, then colours. */
.picker { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }

.pick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 42px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--card);
  color: var(--muted);
  cursor: pointer;
}

/* Selection is a ring and a colour change, never colour alone — and the ring
   sits outside the border so a chosen swatch does not appear to shrink. */
.pick.on {
  border-color: var(--teal);
  color: var(--teal);
  box-shadow: 0 0 0 2px var(--teal-wash), inset 0 0 0 1px var(--teal);
}

.pick.swatch {
  width: 42px;
  background: var(--sw);
  border-color: rgba(0, 0, 0, 0.14);
}

.pick.swatch.on {
  box-shadow: 0 0 0 2px var(--card), 0 0 0 4px var(--sw);
}

/* The full picker, dressed as one more swatch. The native control is the whole
   target rather than a button beside it, so the tap area is the swatch itself;
   it is made invisible rather than hidden, because a display:none input cannot
   be opened by clicking its label on some mobile browsers. */
.pick.swatch.custom { position: relative; overflow: hidden; cursor: pointer; }

.pick.swatch.custom::after {
  content: "";
  position: absolute;
  inset: 0;
  background: conic-gradient(#e0453b, #d8a318, #7fae1f, #1a9c8a, #2f6fd0, #7d4ec4, #c2479a, #e0453b);
  opacity: 0.92;
  border-radius: inherit;
}

/* The chosen colour shows through the middle, so the control doubles as the
   readout of what is currently set. */
.pick.swatch.custom::before {
  content: "";
  position: absolute;
  inset: 9px;
  border-radius: 50%;
  background: var(--sw);
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.85);
  z-index: 1;
}

.pick.swatch.custom input[type="color"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  opacity: 0;
  cursor: pointer;
  z-index: 2;
}

/* Visible to a screen reader, not on screen. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- account menu ---------- */

.account { position: relative; margin-left: 10px; }

.avatar-button {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  border-radius: 50%;
  cursor: pointer;
  line-height: 0;
}

/* The ring is on the button rather than the image, so it is the same whether
   the picture loaded or the initials are showing through. */
.avatar-button:hover .avatar,
.avatar-button[aria-expanded="true"] .avatar { box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.85); }
.avatar-button:focus-visible { outline: none; }
.avatar-button:focus-visible .avatar { box-shadow: 0 0 0 3px #fff; }

.avatar {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--teal-dark);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.5);
  transition: box-shadow 0.12s ease;
}

/* The initials sit underneath the picture rather than replacing it on error,
   so the avatar is never briefly empty and a blocked image just reveals them. */
.avatar-initials {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: #fff;
  line-height: 1;
}

.avatar img {
  position: relative;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 50;
  min-width: 210px;
  padding: 6px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 10px 28px rgba(40, 30, 20, 0.18);
}

.menu-head {
  margin: 0;
  padding: 8px 10px 10px;
  border-bottom: 1px solid var(--line);
  color: var(--heading);
  font-weight: 600;
  line-height: 1.3;
}

.menu-head .sub {
  display: block;
  font-weight: 400;
  font-size: 0.8rem;
  color: var(--muted);
  /* An address longer than the menu should not widen it off-screen. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.menu-item {
  display: block;
  width: 100%;
  padding: 10px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--text);
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.menu-item:first-of-type { margin-top: 4px; }
.menu-item:hover, .menu-item:focus-visible { background: var(--teal-wash); color: var(--teal-dark); }
.menu-item.danger { color: var(--danger); }
.menu-item.danger:hover, .menu-item.danger:focus-visible { background: var(--danger-wash); }

/* The header used to carry a second link that flipped between Settings and
   Dashboard. Both destinations are already there — Settings in the account
   menu, the dashboard on the wordmark — so it was a third route to two places
   that reads as clutter once you notice it. */

/* ---------- desktop ----------
   The phone layout is the base and stays untouched below these widths. What
   changes above them is only how much horizontal room the page is allowed to
   use — a single 720px ribbon down the middle of a 1440px screen reads as a
   phone screenshot rather than as a page. Nothing here changes what is on a
   screen, only where it sits. */

/* Cards for the vehicle list. On a phone this is one column and looks exactly
   like the stacked list it replaces; the seams between rows become gaps. */
@media (min-width: 720px) {
  .list.cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 12px;
    background: none;
    border: 0;
    box-shadow: none;
    overflow: visible;
  }

  .list.cards > .list-item {
    min-width: 0;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--card);
    box-shadow: var(--shadow-lift);
  }

  /* A row is a whole card now, so the chevron pointing off into empty space
     is noise; the card itself is the affordance. */
  .list.cards > .list-item .chev { display: none; }
}

@media (min-width: 1024px) {
  main { max-width: 1080px; padding: 24px 24px 80px; }

  /* Buttons stop stretching. flex: 1 1 160px is right on a phone, where a
     full-width target is the point; across 1080px it makes a row of slabs. */
  .btn-row .btn { flex: 0 1 auto; min-width: 190px; }

  /* Forms do not get wider just because the window did: a text field the
     width of a desk is harder to scan, not easier. */
  form { max-width: 640px; }

  /* Two independent blocks side by side, each free to be as tall as it is.
     The top margin is here rather than in the base rules because below this
     width the h2 inside the first column still supplies it; see the next rule,
     which is what takes it away. */
  .split {
    display: grid;
    grid-template-columns: minmax(0, 4fr) minmax(0, 6fr);
    gap: 28px;
    align-items: start;
    margin-top: 26px;
  }

  /* The first heading inside each column lines up with the other's. Zeroing
     that margin also removes the only thing holding the split away from
     whatever precedes it — the hero, the KPI row, or the empty state — which
     is the space restored above. */
  .split > section > h2:first-child { margin-top: 0; }

  /* A grid item's default min-width is auto, which means "never narrower than
     my content". A log row full of unbreakable figures then refuses to shrink,
     the track grows past its fraction, and the whole grid overflows the page
     to the right — taking the second column off the edge of the screen. The
     tracks above are minmax(0, …) and these children are min-width: 0 for the
     same reason; both halves are needed. */
  .split > * { min-width: 0; }

  /* The hero is a headline, not a banner — it should not span the page. */
  .hero { padding: 26px 20px; }
  .hero .value { font-size: 3.8rem; }
}

/* Above this the column stops growing; more width becomes margin rather than
   longer lines, which are the thing that actually hurts readability. */
@media (min-width: 1400px) {
  main { max-width: 1180px; }
}

/* A photo fills the badge, sitting over the silhouette rather than replacing
   it — if the image never arrives the badge is still a badge. */
.vbadge img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}

.vbadge.has-photo { position: relative; overflow: hidden; }

/* ---------- vehicle photo ---------- */

.photo-row { display: flex; align-items: center; gap: 14px; margin-top: 10px; }

.photo-preview {
  width: 96px;
  height: 72px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg);
  overflow: hidden;
}

.photo-preview img { width: 100%; height: 100%; object-fit: cover; }
.photo-empty { color: var(--muted); font-size: 0.78rem; }
.photo-actions { display: flex; flex-wrap: wrap; gap: 8px; }

/* The file input is inside its label, so the label is the button. Hiding the
   input with `hidden` rather than display:none on a wrapper keeps the click
   forwarding that some mobile browsers otherwise drop. */
.small-btn { flex: 0 0 auto; padding: 8px 14px; font-size: 0.9rem; cursor: pointer; }
.hint.error { color: var(--danger); }

/* ---------- archived vehicles ---------- */

.banner.archived {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--purple-wash);
  border-color: var(--purple-line);
  color: var(--text);
}

.past-vehicles {
  margin-top: 26px;
  padding: 0 2px;
}

/* Collapsed by default: these are the vehicles you are *not* filling up, so
   they should be findable without being in the way. */
.past-vehicles > summary {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  font-size: 1.35rem;
  font-weight: 600;
  color: var(--heading);
  list-style: none;
}

.past-vehicles > summary::-webkit-details-marker { display: none; }

.past-vehicles > summary::before {
  content: "›";
  display: inline-block;
  color: var(--muted);
  transition: transform 0.15s ease;
}

.past-vehicles[open] > summary::before { transform: rotate(90deg); }
.past-vehicles > summary .count {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--muted);
  background: var(--line);
  border-radius: 999px;
  padding: 1px 9px;
}

.past-vehicles .hint { margin: 6px 0 10px; }

@media (prefers-reduced-motion: reduce) {
  .past-vehicles > summary::before { transition: none; }
}

/* ---------- surface ----------
   The app is a log of paper receipts, so the ground is paper rather than a
   flat fill. Everything here is decoration in the strict sense: it sits in its
   own fixed layer, takes no pointer events, and never affects layout, so it
   cannot push anything around or trigger a reflow. */

body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image: var(--grain);
  background-size: 120px 120px;
  /* Low enough to read as tooth on paper rather than as noise. Any higher and
     it starts eating the small text it sits under. */
  opacity: 0.03;
}

/* The grain is behind everything, not over it: over the top it would sit on
   photographs and chart lines too, which is a different and worse effect.
   Only main needs lifting — the top bar is already sticky at z-index 10, and
   restating position here would quietly turn it back into a relative element
   that scrolls away. */
main { position: relative; z-index: 1; }

/* The bar gets depth from a gradient and a light top edge rather than sitting
   as one flat block of teal — the single largest area of colour in the app. */
.topbar {
  background: linear-gradient(178deg, var(--teal) 0%, var(--teal-deep) 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.09), 0 1px 0 rgba(58, 44, 30, 0.06);
}

/* ---------- motion ----------
   Only transform and opacity, so every one of these runs on the compositor and
   none of them can cause layout. Durations are short enough to feel like
   response rather than animation. */

.list-item, .btn, .kpi, .panel, .chart-card, .pick {
  transition: transform 0.14s var(--ease), box-shadow 0.14s var(--ease), background-color 0.14s var(--ease);
}

.list-item:hover { background: var(--line-soft); }
.list.cards > .list-item:hover { transform: translateY(-2px); box-shadow: var(--shadow-pop); }
.btn:hover { transform: translateY(-1px); }
.btn:active, .list.cards > .list-item:active { transform: translateY(0); }
.pick:hover { transform: translateY(-1px); }

/* Figures line up in columns all over this app — averages beside averages,
   costs beside costs — and proportional digits make those columns ragged. */
.kpi-value, .hero .value, .panel-value, .stat-table dd, .log-figure, .mpg, .cost,
.readonly-value, table { font-variant-numeric: tabular-nums; }

h1, h2, h3 { text-wrap: balance; }
.kpi-label, .panel-label { letter-spacing: 0.01em; }

@media (prefers-reduced-motion: reduce) {
  .list-item, .btn, .kpi, .panel, .chart-card, .pick { transition: none; }
  .list.cards > .list-item:hover, .btn:hover, .pick:hover { transform: none; }
}

/* ---------- the confirmation, as a receipt ----------
   This is the one moment the app celebrates: the fill-up is logged. The mark
   in the top bar is a receipt, so the card that reports a fill-up tears off
   like one. It is the only place this device appears — a torn edge on every
   card would be wallpaper rather than a signature. */

.confirm {
  border-bottom: 0;
  border-radius: var(--radius) var(--radius) 0 0;
  /* Room for the teeth, so text never sits in the torn strip. */
  padding-bottom: 30px;
  animation: receipt-in 0.32s var(--ease) both;
}

/* Scallops bitten out of the bottom edge, the way a ticket tears off a
   perforation. One repeating radial-gradient: a single paint, no extra
   elements, and it tiles to any card width. Crisper than a zigzag at this
   size, where triangles this small just read as a fuzzy band. */
.confirm::after {
  content: "";
  position: absolute;
  left: -1px;
  right: -1px;
  bottom: -10px;
  height: 11px;
  background:
    radial-gradient(circle at 50% 100%, transparent 5.5px, var(--success-bg) 6px) 0 0 / 15px 11px repeat-x;
  border-left: 1px solid var(--success-line);
  border-right: 1px solid var(--success-line);
}

@keyframes receipt-in {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .confirm { animation: none; }
}

/* The import preview: one line per file, so what is about to happen to each
   vehicle is readable before anything is written. */
.import-plan { margin: 14px 0 0; padding: 0; list-style: none; display: grid; gap: 10px; }

.import-plan li {
  padding: 12px 14px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.import-plan .sub { display: block; margin-top: 4px; color: var(--muted); font-size: 0.85rem; line-height: 1.5; }
