@charset "utf-8";
/* Sitatunga Transfers
   ------------------------------------------------------------------
   Sitatunga Brand Guide v2 ("company profile style"), applied 1 October
   2026 — https://claude.ai/artifact/PxN33Gtzimet5qmo2QrgTg
     forest  #2F4A3A   header band, headings, buttons — carries the brand
     gold    #B8955A   rules under headings, eyebrow labels
     sand    #D9C39A   small text on forest
     linen   #F7F3EA   panels, table headers
     ink     #2B2B26   body text
     orange  #EB8928   the logo and the pattern line ONLY (≤ 5% of a screen)
   Type: Caladea Bold for headings, Carlito for everything else.

   The guide is written for print. Three things are adjusted for a phone read
   one-handed in Highveld sun, each checked by computed contrast, not by eye:
   gold is 2.8:1 on white, too faint for small text, so labels that spell
   use --gold-text (#8A6A2F, 5.0:1) and gold itself only draws lines; status
   colours stay their own family so a status never reads as decoration; and
   there is a dark theme, which the guide does not cover, built from its
   forest-deep and sand.

   ORANGE, 1 October 2026: the guide keeps orange for the logo and the
   pattern line. The office asked for it as the app's highlight as well —
   the primary button on each screen (Add booking, Accept this trip), the
   count bubbles on the calendar, the selected tab. It fills; it never
   spells: text on orange is dark ink (6.9:1), and orange is never text on a
   light ground (2.6:1). Everything else stays as the guide says.
   ------------------------------------------------------------------ */

:root {
  /* brand */
  --forest:       #2F4A3A;
  --forest-deep:  #243A2D;
  --gold:         #B8955A;
  --gold-text:    #8A6A2F;
  --sand:         #D9C39A;
  --linen:        #F7F3EA;
  --orange:       #EB8928;
  --ink:          #2B2B26;
  --serif:        Caladea, Cambria, Georgia, serif;
  --sans:         Carlito, Calibri, "Segoe UI", system-ui, sans-serif;
  /* The chevron-and-dot line, one 100×60 repeat. Transparent: only the
     orange shows, so it only ever sits on forest. */
  --line: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='60' viewBox='0 0 100 60'%3E%3Cg fill='none' stroke='%23EB8928' stroke-width='5.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cg transform='translate(-100,0)'%3E%3Cpath d='M-7.4 5 L25 52 L57.4 5'/%3E%3Cpath d='M6.4 6 L25 33 L43.6 6'/%3E%3Cpath d='M40.5 55 L75 5 L109.5 55'/%3E%3Cpath d='M54.3 54 L75 24 L95.7 54'/%3E%3Ccircle cx='25' cy='13' r='4.4' stroke='none' fill='%23EB8928'/%3E%3Ccircle cx='75' cy='46.5' r='4.4' stroke='none' fill='%23EB8928'/%3E%3C/g%3E%3Cg transform='translate(0,0)'%3E%3Cpath d='M-7.4 5 L25 52 L57.4 5'/%3E%3Cpath d='M6.4 6 L25 33 L43.6 6'/%3E%3Cpath d='M40.5 55 L75 5 L109.5 55'/%3E%3Cpath d='M54.3 54 L75 24 L95.7 54'/%3E%3Ccircle cx='25' cy='13' r='4.4' stroke='none' fill='%23EB8928'/%3E%3Ccircle cx='75' cy='46.5' r='4.4' stroke='none' fill='%23EB8928'/%3E%3C/g%3E%3Cg transform='translate(100,0)'%3E%3Cpath d='M-7.4 5 L25 52 L57.4 5'/%3E%3Cpath d='M6.4 6 L25 33 L43.6 6'/%3E%3Cpath d='M40.5 55 L75 5 L109.5 55'/%3E%3Cpath d='M54.3 54 L75 24 L95.7 54'/%3E%3Ccircle cx='25' cy='13' r='4.4' stroke='none' fill='%23EB8928'/%3E%3Ccircle cx='75' cy='46.5' r='4.4' stroke='none' fill='%23EB8928'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");

  /* light theme surfaces */
  --bg:           #F7F3EA;
  --surface:      #FFFFFF;
  --surface-2:    #F7F3EA;
  --border:       #E5DCC9;
  --text:         #2B2B26;
  --text-dim:     #6F6A5E;
  --header-bg:    var(--forest);
  --header-text:  #FFFFFF;
  --accent-text:  var(--forest);
  --heading:      var(--forest);
  --btn-bg:       var(--forest);
  --btn-text:     #FFFFFF;
  --btn2-bg:      var(--forest-deep);
  --hi:           var(--orange);    /* the highlight — fills only */
  --hi-ink:       #241403;          /* 6.9:1 on orange */

  /* A trip's status, solid: the card's left edge and its badge.
     Redone 1 October 2026: the office could not tell "No driver yet" (warm
     grey) from "Completed" (charcoal) — and by the numbers Completed was also
     within 3.8 ΔE of Accepted's forest. Checked with the data-viz palette
     validator (OKLab ΔE, all pairs): No driver yet is now a slate BLUE (it
     is the one that needs doing), Completed a light grey that steps back
     (it is finished). Light: every pair ≥ 15.3 ΔE normal vision and ≥ 13.4
     under colour blindness. Badge text: white on blue 6.0:1, dark ink on the
     grey 5.2:1. The grey is 3.2:1 on white — it always travels with a
     "Completed" label. */
  --st-unassigned: #3D63A0;  --st-unassigned-ink: #FFFFFF;
  --st-sent:       #A85A0C;  --st-sent-ink:       #FFFFFF;
  --st-live:       #2F4A3A;  --st-live-ink:       #FFFFFF;
  --st-done:       #948F84;  --st-done-ink:       #16201A;
  --st-cancel:     #8A3B2E;  --st-cancel-ink:     #FFFFFF;
  --focus:        #9C7A43;          /* 4.0:1 on white; gold itself is 2.8 */

  /* semantic — kept clear of the brand hues so status never reads as decoration */
  --good:         #1F6B3F;
  --good-bg:      #DDEFE3;
  --warn:         #8E5412;
  --warn-bg:      #FBEBD3;
  --stop:         #8A3B2E;
  --stop-bg:      #F6ECE8;
  --info-bg:      #E4ECE5;

  --radius:       4px;             /* the guide's buttons */
  --radius-lg:    6px;             /* the guide's cards and panels */
  --shadow:       0 1px 2px rgba(36,58,45,.06), 0 4px 14px rgba(36,58,45,.06);
  --shadow-lift:  0 2px 6px rgba(36,58,45,.12), 0 12px 28px rgba(36,58,45,.12);
  --tap:          48px;           /* nothing interactive is smaller than this */
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:          #16201A;
    --surface:     #1E2A22;
    --surface-2:   #24382B;
    --border:      #34463A;
    --text:        #F1EDE2;
    --text-dim:    #A9B3A6;
    --header-bg:   var(--forest-deep);
    --header-text: #FFFFFF;
    --accent-text: var(--sand);
    --heading:     var(--sand);
    --gold-text:   var(--sand);
    --btn-bg:      #3E614C;
    --btn2-bg:     var(--forest);
    --focus:       var(--sand);
    /* Dark: the same idea, validated on the dark surface (normal vision ≥ 15.6
       ΔE; Sent↔Accepted 6.5 under protan, which is why blocks say "No driver
       yet" in words and the key names every colour). Completed is a dim grey
       with light ink (5.8:1) — it recedes; its label carries it. */
    --st-unassigned: #9AA6F0;  --st-unassigned-ink: #16201A;
    --st-sent:       #E3A95F;  --st-sent-ink:       #16201A;
    --st-live:       #6FB08A;  --st-live-ink:       #16201A;
    --st-done:       #5F5B53;  --st-done-ink:       #F1EDE2;
    --st-cancel:     #E8917D;  --st-cancel-ink:     #16201A;
    --good:        #7CCB98;
    --good-bg:     #1C3326;
    --warn:        #E3A95F;
    --warn-bg:     #3A2C16;
    --stop:        #E8917D;
    --stop-bg:     #3B2019;
    --info-bg:     #24382B;
    --shadow:      0 1px 2px rgba(0,0,0,.4), 0 4px 14px rgba(0,0,0,.34);
    --shadow-lift: 0 2px 6px rgba(0,0,0,.5), 0 12px 28px rgba(0,0,0,.4);
  }
}

/* A theme chosen with the header button also sets the native controls
   (date pickers, selects, scrollbars) to match, whatever the phone says. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

:root[data-theme="dark"] {
  --bg:          #16201A;
  --surface:     #1E2A22;
  --surface-2:   #24382B;
  --border:      #34463A;
  --text:        #F1EDE2;
  --text-dim:    #A9B3A6;
  --header-bg:   var(--forest-deep);
  --header-text: #FFFFFF;
  --accent-text: var(--sand);
  --heading:     var(--sand);
  --gold-text:   var(--sand);
  --btn-bg:      #3E614C;
  --btn2-bg:     var(--forest);
  --focus:       var(--sand);
  --st-unassigned: #9AA6F0;  --st-unassigned-ink: #16201A;
  --st-sent:     #E3A95F;  --st-sent-ink:     #16201A;
  --st-live:     #6FB08A;  --st-live-ink:     #16201A;
  --st-done:     #5F5B53;  --st-done-ink:     #F1EDE2;
  --st-cancel:   #E8917D;  --st-cancel-ink:   #16201A;
  --good:        #7CCB98;
  --good-bg:     #1C3326;
  --warn:        #E3A95F;
  --warn-bg:     #3A2C16;
  --stop:        #E8917D;
  --stop-bg:     #3B2019;
  --info-bg:     #24382B;
  --shadow:      0 1px 2px rgba(0,0,0,.4), 0 4px 14px rgba(0,0,0,.34);
  --shadow-lift: 0 2px 6px rgba(0,0,0,.5), 0 12px 28px rgba(0,0,0,.4);
}

/* ---- base ---------------------------------------------------------- */

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

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 17px;               /* Carlito runs small; 17 reads like 16 elsewhere, and stays above iOS's 16px zoom line */               /* iOS zooms any input below this on focus */
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
  font-variant-numeric: tabular-nums;
  overscroll-behavior-y: none;
}

h1, h2, h3 {
  margin: 0; font-family: var(--serif); font-weight: 700; line-height: 1.2;
  color: var(--heading); text-wrap: balance;
}
h1 { font-size: 1.45rem; }
h2 { font-size: 1.2rem; }
h3 { font-size: 1.02rem; }
p { margin: 0; }

a { color: var(--accent-text); }

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ---- app frame ------------------------------------------------------ */

#app { display: flex; flex-direction: column; min-height: 100%; }

.shell {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  max-width: 640px;
  margin-inline: auto;
  width: 100%;
  background: var(--bg);
}

/* The header scrolls away with the page (office, 2 October 2026 — it used to
   stay pinned). What stays pinned is the calendar's week card (.calstick). */
.topbar {
  position: relative;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 16px 32px;
  padding-top: calc(10px + env(safe-area-inset-top, 0px));
  background: var(--header-bg);
  color: var(--header-text);
  box-shadow: var(--shadow);
}
/* The guide's pattern line: edge to edge along the bottom of the forest band,
   with a strip of green left below it. Drawn here rather than as an image in
   the markup, so every screen gets it and none can forget it. 14px is the
   guide's small size (its floor on screen is 12). */
.topbar::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 9px;
  height: 14px;
  background: var(--line) repeat-x left center / auto 14px;
  pointer-events: none;
}
.topbar__logo { height: 38px; width: 38px; display: block; flex: none; }
.topbar__title { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.topbar__title b { font-family: var(--serif); font-size: 1.2rem; font-weight: 700; color: #fff; }
.topbar__title span {
  font-size: .66rem; text-transform: uppercase; letter-spacing: .2em; color: var(--sand);
}
.topbar__spacer { margin-left: auto; }

.topbar__who {
  display: flex; align-items: center; gap: 8px;
  background: rgba(255,255,255,.12);
  border: 1px solid rgba(255,255,255,.2);
  color: inherit;
  border-radius: 999px;
  padding: 5px 6px 5px 12px;
  font: inherit; font-size: .8rem;
  cursor: pointer;
  min-height: 38px;
}
.topbar__who img { width: 26px; height: 26px; border-radius: 50%; display: block; }
/* Light / dark switch (ui.js toggleTheme). A sun in dark mode, a moon in
   light: the icon shows where a tap takes you. */
.topbar__theme, .topbar__bell {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 40px; height: 40px; padding: 0; margin-right: 8px;
  background: rgba(255,255,255,.12);
  border: 1px solid rgba(255,255,255,.2);
  border-radius: 50%;
  color: #fff; cursor: pointer;
}
.topbar__theme svg, .topbar__bell svg { width: 20px; height: 20px; display: block; }
/* The bell (app.js): dim with a stroke through it when this device gets no
   notifications, the gold accent when it does. */
.topbar__bell { color: rgba(255,255,255,.7); }
.topbar__bell.is-on { color: var(--hi); border-color: var(--hi); }

main {
  flex: 1;
  padding: 14px 16px calc(24px + env(safe-area-inset-bottom, 0px));
  display: flex; flex-direction: column; gap: 14px;
}

/* ---- tabs ----------------------------------------------------------- */

.tabs {
  display: flex; gap: 2px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 0 4px;
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs button {
  flex: 1 0 auto;
  min-height: var(--tap);
  padding: 10px 12px 8px;
  border: 0; border-bottom: 3px solid transparent; border-radius: 0;
  background: transparent;
  color: var(--text-dim);
  font: inherit; font-size: .92rem; font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
}
.tabs button[aria-selected="true"] {
  color: var(--accent-text);
  border-bottom-color: var(--hi);
}

.tabs button[hidden] { display: none; }

/* The three screens inside the owner's Fleet tab (app.js FLEET_SUBS): one
   segmented row under the tabs, the same look as the calendar's Day / List. */
.subtabs {
  display: flex;
  border: 1.5px solid var(--border); border-radius: var(--radius);
  overflow: hidden;
}
.subtabs button {
  flex: 1; min-height: 42px; padding: 6px 10px;
  border: 0; background: var(--surface); color: var(--text);
  font: inherit; font-size: .9rem; font-weight: 700; cursor: pointer;
}
.subtabs button + button { border-left: 1.5px solid var(--border); }
.subtabs button[aria-current="page"] { background: var(--btn-bg); color: #fff; }

/* ---- surfaces ------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 16px;
  box-shadow: var(--shadow);
}
.card--flush { padding: 0; overflow: hidden; }
.stack { display: flex; flex-direction: column; gap: 12px; }
.stack--tight { gap: 8px; }
.row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.muted { color: var(--text-dim); }
.small { font-size: .82rem; }
.eyebrow {
  font-size: .72rem; font-weight: 700; letter-spacing: .16em;
  text-transform: uppercase; color: var(--gold-text);
}
/* A card that opens with its label gets the guide's gold rule under it. */
.card > .eyebrow:first-child {
  padding-bottom: 7px;
  border-bottom: 1.5px solid var(--gold);
}

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

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: var(--tap);
  padding: 12px 18px;
  border: 1.5px solid transparent;
  border-radius: var(--radius);
  font: inherit; font-size: .95rem; font-weight: 700;
  cursor: pointer;
  text-decoration: none;
  width: 100%;
  transition: transform .06s ease;
}
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: .45; cursor: not-allowed; transform: none; }

.btn--primary { background: var(--hi);      color: var(--hi-ink); }
.btn--olive   { background: var(--btn2-bg); color: var(--btn-text); }
.btn--ghost   { background: transparent; color: var(--text); border-color: var(--border); }
.btn--danger  { background: transparent; color: var(--stop); border-color: var(--stop-bg); }
.btn--sm      { min-height: 38px; padding: 8px 14px; font-size: .82rem; width: auto; white-space: nowrap; }

/* The step button a driver taps through a run. Deliberately the largest
   thing on the screen: it is pressed at night, in the rain, in a hurry. */
.btn--step {
  min-height: 62px;
  font-size: 1.05rem;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lift);
}

/* ---- pills ---------------------------------------------------------- */

.pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 9px;
  border-radius: var(--radius);
  font-size: .7rem; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase;
  white-space: nowrap;
}
.pill--unassigned { background: var(--st-unassigned); color: var(--st-unassigned-ink); }
.pill--assigned   { background: var(--st-sent);       color: var(--st-sent-ink); }
.pill--accepted   { background: var(--st-live);       color: var(--st-live-ink); }
.pill--moving     { background: var(--st-live);       color: var(--st-live-ink); }
.pill--completed  { background: var(--st-done);       color: var(--st-done-ink); }
.pill--cancelled  { background: var(--st-cancel);     color: var(--st-cancel-ink); }

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

.field { display: flex; flex-direction: column; gap: 6px; }
.field > label {
  font-size: .72rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--text-dim);
}
.field .req { color: var(--gold-text); }
.field input, .field select, .field textarea {
  width: 100%;
  min-height: var(--tap);
  padding: 11px 12px;
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font: inherit;
}
.field textarea { min-height: 88px; resize: vertical; line-height: 1.45; }
.field input:focus, .field select:focus, .field textarea:focus {
  border-color: var(--accent-text); outline: 3px solid color-mix(in srgb, var(--gold) 45%, transparent); outline-offset: 0;
}
.field--bad input, .field--bad select, .field--bad textarea { border-color: var(--stop); }
.field__note { font-size: .76rem; color: var(--text-dim); }
.field__err  { font-size: .76rem; color: var(--stop); font-weight: 700; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 380px) { .grid2 { grid-template-columns: 1fr; } }

/* ---- trip cards ----------------------------------------------------- */

.trip {
  display: flex; flex-direction: column; gap: 7px;
  width: 100%;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 6px solid var(--st-unassigned);
  border-radius: var(--radius-lg);
  padding: 13px 15px 13px 14px;
  box-shadow: var(--shadow);
  font: inherit; color: inherit;
  cursor: pointer;
}
/* The status owns the edge — the same colour as the badge beside the time. */
.trip[data-status="assigned"]  { border-left-color: var(--st-sent); }
.trip[data-status="accepted"],
.trip[data-status="enroute"],
.trip[data-status="atpickup"],
.trip[data-status="onboard"]   { border-left-color: var(--st-live); }
.trip[data-status="completed"] { border-left-color: var(--st-done); }
.trip[data-status="cancelled"] { border-left-color: var(--st-cancel); opacity: .72; }
.trip .pill { font-size: .74rem; padding: 5px 10px; }
.trip__when { font-family: var(--serif); font-weight: 700; font-size: 1.2rem; color: var(--heading); }
.trip__route {
  display: flex; flex-direction: column; gap: 1px;
  font-size: .92rem; color: var(--text);
  overflow-wrap: anywhere;
}
/* From and to on their own lines. Side by side, a long airport name wraps and
   strands the arrow in the middle of the card. */
.trip__route .arrow { color: var(--gold-text); font-weight: 700; }
.trip__guest { font-size: .92rem; font-weight: 700; color: var(--text); overflow-wrap: anywhere; }
/* Reference and who is billed: the two things the office looks a booking up
   by. A tinted strip so they read as the paperwork line, not the route. */
.trip__billing {
  display: flex; flex-wrap: wrap; gap: 4px 16px;
  padding: 6px 9px;
  background: var(--surface-2);
  border-radius: var(--radius);
  font-size: .84rem; color: var(--text);
  overflow-wrap: anywhere;
}
.trip__billing b {
  font-size: .68rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--gold-text); margin-right: 4px;
}
.trip__foot { display: flex; justify-content: space-between; align-items: center; gap: 10px; font-size: .84rem; color: var(--text-dim); }
.trip__foot:empty { display: none; }
.trip__nodriver { font-style: italic; }

/* ---- key/value ------------------------------------------------------ */

.kv { display: flex; justify-content: space-between; gap: 14px; padding: 9px 0; border-bottom: 1px solid var(--border); }
.kv:last-child { border-bottom: 0; }
.kv dt { color: var(--text-dim); font-size: .85rem; flex: none; }
.kv dd { margin: 0; font-weight: 700; text-align: right; overflow-wrap: anywhere; }

/* ---- calendar ------------------------------------------------------- */

.cal__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.cal__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; margin-top: 10px; }
.cal__dow {
  text-align: center; font-size: .65rem; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--text-dim); padding-bottom: 4px;
}
.cal__day {
  position: relative;
  aspect-ratio: 1;
  min-height: 42px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  border: 0; border-radius: var(--radius);
  background: transparent; color: var(--text);
  font: inherit; font-size: .88rem;
  cursor: pointer;
}
.cal__day[data-out="true"] { color: var(--text-dim); opacity: .45; }
.cal__day[aria-pressed="true"] { background: var(--forest); color: #fff; font-weight: 700; }
.cal__day[data-today="true"]:not([aria-pressed="true"]) { box-shadow: inset 0 0 0 2px var(--gold); }

/* One week across seven columns. Taller than a month cell and no longer
   square: there is room for the weekday, the date and how many bookings the
   day holds, which is the question being asked of it. */
.cal__grid--week { gap: 4px; }
.cal__grid--week .cal__day {
  aspect-ratio: auto;
  min-height: 74px;
  padding: 8px 2px;
  justify-content: flex-start; gap: 2px;
  background: var(--surface-2);
}
.cal__grid--week .cal__dow { padding: 0; }
.cal__grid--week .cal__date { font-size: 1.12rem; font-weight: 700; line-height: 1.1; }
.cal__grid--week .cal__count {
  min-width: 19px; height: 19px; margin-top: 1px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 999px;
  font-size: .72rem; font-weight: 700;
  background: var(--hi); color: var(--hi-ink);
}
.cal__grid--week .cal__count:empty { background: none; }
.cal__grid--week .cal__day[aria-pressed="true"] { background: var(--forest); }
.cal__grid--week .cal__day[aria-pressed="true"] .cal__dow { color: var(--sand); }
.cal__grid--week .cal__day[data-today="true"]:not([aria-pressed="true"]) {
  box-shadow: inset 0 0 0 2px var(--gold);
}

/* Narrow phones: three characters of weekday and a two-digit date have to
   fit in a seventh of the screen, so the labels give way before the dates. */
@media (max-width: 360px) {
  .cal__grid--week { gap: 2px; }
  .cal__grid--week .cal__day { min-height: 68px; padding: 7px 0; }
  .cal__grid--week .cal__dow { font-size: .58rem; letter-spacing: .02em; }
  .cal__grid--week .cal__date { font-size: 1rem; }
}

/* ---- signature ------------------------------------------------------ */

.sig {
  position: relative;
  border: 2px dashed var(--border);
  border-radius: var(--radius-lg);
  background: #FFFFFF;      /* a signature is ink on paper in either theme */
  overflow: hidden;
}
.sig canvas { display: block; width: 100%; height: 190px; touch-action: none; }
.sig__hint {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: #9A9384; font-size: .85rem; pointer-events: none;
}
.sig__baseline {
  position: absolute; left: 24px; right: 24px; bottom: 44px;
  border-bottom: 1px solid #E2DCD0; pointer-events: none;
}
.sig-shot { background: #fff; border: 1px solid var(--border); border-radius: var(--radius); padding: 8px; }
.sig-shot img { display: block; width: 100%; }

/* ---- photo capture -------------------------------------------------- */

.shot {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  min-height: 108px;
  border: 2px dashed var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
  color: var(--text-dim);
  font: inherit; font-size: .8rem; font-weight: 700;
  cursor: pointer; width: 100%; padding: 10px;
}
.shot--has { border-style: solid; padding: 0; overflow: hidden; background: var(--surface); }
.shot--has img { display: block; width: 100%; height: 140px; object-fit: cover; }

/* ---- misc ----------------------------------------------------------- */

.empty { text-align: center; padding: 40px 18px; color: var(--text-dim); }
.empty__mark { width: 52px; height: 52px; opacity: .42; margin: 0 auto 10px; display: block; }

.toast {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  z-index: 60;
  max-width: calc(100% - 32px);
  background: var(--ink); color: var(--linen);
  padding: 11px 18px; border-radius: var(--radius-lg);
  font-size: .85rem; font-weight: 700;
  box-shadow: var(--shadow-lift);
}
:root[data-theme="dark"] .toast { background: var(--surface-2); border: 1px solid var(--border); }

.banner {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 11px 13px; border-radius: var(--radius);
  font-size: .84rem;
}
.banner--warn { background: var(--warn-bg); color: var(--warn); }
.banner--stop { background: var(--stop-bg); color: var(--stop); }
.banner--info { background: var(--info-bg); color: var(--accent-text); }

.backbar { display: flex; align-items: center; gap: 10px; }
.backbtn {
  flex: none;
  width: var(--tap); height: var(--tap);
  display: grid; place-items: center;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text);
  font-size: 1.1rem; cursor: pointer;
}

.spinner {
  width: 32px; height: 32px; border-radius: 50%;
  border: 3px solid var(--border); border-top-color: var(--accent-text);
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.centre {
  flex: 1; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 14px;
  text-align: center; padding: 40px 24px;
}
.centre__logo { width: 96px; height: auto; }
.centre p { max-width: 34ch; color: var(--text-dim); font-size: .9rem; }

/* Signed-out gate */
.gate { background: var(--forest); color: #fff; min-height: 100dvh; position: relative; }
.gate h1 { color: #fff; font-size: 1.75rem; }
.gate .centre p { color: #DDE3DA; }
.gate .btn--google {
  background: #fff; color: var(--ink);
  border-color: transparent; max-width: 320px;
}
.gate .btn--ghost { color: #fff; border-color: rgba(217,195,154,.55); max-width: 320px; }
/* The pattern line at the guide's standard 24px, with green left below it. */
.gate::after {
  content: "";
  position: absolute; left: 0; right: 0;
  bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  height: 24px;
  background: var(--line) repeat-x left center / auto 24px;
  pointer-events: none;
}

/* ---- sticky action bar ---------------------------------------------- */
/* The driver's one button. A trip screen is longer than a phone, and the
   thing they opened it to do must never be below the fold. */

.actionbar {
  position: sticky;
  bottom: 0;
  z-index: 20;
  margin: 4px -16px calc(-24px - env(safe-area-inset-bottom, 0px));
  padding: 14px 16px calc(14px + env(safe-area-inset-bottom, 0px));
  background: linear-gradient(to top,
    var(--bg) 0%, var(--bg) 62%, color-mix(in srgb, var(--bg) 0%, transparent) 100%);
}
.actionbar .btn + .btn { margin-top: 8px; }

/* ---- chips ----------------------------------------------------------- */
/* A row of one-tap choices: which car, how full. Faster than a dropdown on
   a phone, and the whole set is visible at once. */

.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  min-height: 42px;
  padding: 9px 16px;
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font: inherit; font-size: .92rem; font-weight: 700;
  cursor: pointer;
}
.chip[aria-pressed="true"] {
  background: var(--btn-bg);
  border-color: var(--btn-bg);
  color: #fff;
}

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

/* One car's month on the report. A rule between them rather than a card each:
   four of these stacked as cards would bury the drivers above them. */
.veh__line + .veh__line {
  border-top: 1px solid var(--border);
  padding-top: 8px;
  margin-top: 2px;
}

.veh__photo {
  display: block; width: 100%; padding: 0; border: 0;
  background: var(--surface-2);
  cursor: pointer;
  aspect-ratio: 16 / 9;
  overflow: hidden;
}
.veh__photo img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* No photo yet: a slim prompt rather than a reserved 16:9 hole. Five cars
   without photos should not be five screens of empty grey. */
.veh__photo--empty {
  aspect-ratio: auto;
  min-height: 0;
  display: flex; align-items: center; justify-content: center; gap: 7px;
  padding: 9px;
  color: var(--text-dim);
  font: inherit; font-size: .76rem; font-weight: 700;
  border-bottom: 1px dashed var(--border);
}
.veh__photo--empty::before { content: "＋"; font-size: .95rem; line-height: 1; }

.linklike {
  border: 0; background: none; padding: 0;
  color: var(--accent-text);
  font: inherit; font-weight: 700;
  text-decoration: underline;
  cursor: pointer;
}

/* A field in error state, when the thing that is missing is a photo rather
   than an input. */
.field--bad .shot { border-color: var(--stop); }

/* ---- on/off switch ---------------------------------------------------- */
/* A person on or off the roster. Deliberately larger than a checkbox: it is
   pressed with a thumb, and switching the wrong driver off is a phone call. */

.switch-toggle { position: relative; flex: none; cursor: pointer; }
.switch-toggle input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  margin: 0; opacity: 0; cursor: pointer;
}
.switch-track {
  display: block; width: 50px; height: 30px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1.5px solid var(--border);
  transition: background .15s ease, border-color .15s ease;
}
.switch-track::after {
  content: ""; display: block;
  width: 22px; height: 22px; margin: 3px;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(0,0,0,.25);
  transition: transform .15s ease;
}
.switch-toggle input:checked + .switch-track {
  background: var(--btn-bg); border-color: var(--btn-bg);
}
.switch-toggle input:checked + .switch-track::after { transform: translateX(20px); }
.switch-toggle input:focus-visible + .switch-track { outline: 2px solid var(--focus); outline-offset: 2px; }
.switch-toggle input[disabled] + .switch-track { opacity: .45; }

/* ---- paperwork panel -------------------------------------------------- */
/* Folded away by default: on the Fleet screen five cars' documents would
   otherwise push the cars themselves off the screen. */

.docs { border-top: 1px solid var(--border); margin-top: 2px; }
.docs > summary {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  min-height: 44px; padding: 10px 0;
  cursor: pointer; list-style: none;
  font-size: .82rem; font-weight: 700; color: var(--text-dim);
}
.docs > summary::-webkit-details-marker { display: none; }
.docs > summary > span:first-child::before {
  content: "▸"; display: inline-block; margin-right: 7px;
  transition: transform .15s ease;
}
.docs[open] > summary > span:first-child::before { transform: rotate(90deg); }
.docs__body { padding-bottom: 12px; }
.docs__row { padding: 7px 0; border-bottom: 1px solid var(--border); }
.docs__row:last-of-type { border-bottom: 0; }
.docs__when { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 2px; }
.docs__actions { display: flex; align-items: center; gap: 14px; flex: none; }
.docs__del { color: var(--stop); }
.docs__add {
  display: flex; flex-direction: column; gap: 10px;
  margin-top: 6px; padding-top: 12px;
  border-top: 1px dashed var(--border);
}

/* ---- address suggestions ---------------------------------------------- */

.place { position: relative; }
.places {
  position: absolute; z-index: 30; top: 100%; left: 0; right: 0;
  margin: 4px 0 0; padding: 4px; list-style: none;
  max-height: 46vh; overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lift);
}
.places li {
  display: flex; flex-direction: column; gap: 2px;
  min-height: 46px; padding: 9px 11px;
  border-radius: 6px; cursor: pointer;
}
.places li strong { font-size: .9rem; font-weight: 700; }
.places li span { font-size: .76rem; color: var(--text-dim); }
.places li:hover,
.places li[aria-selected="true"] { background: var(--surface-2); }

.place__map {
  display: block; width: 100%; height: auto;
  margin-top: 2px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
}

/* ---- a tick box on a form --------------------------------------------- */
/* The whole row is the target, not the 20px box: this is filled in on a
   phone, often one-handed. */

.check {
  display: flex; align-items: center; gap: 11px;
  min-height: var(--tap);
  cursor: pointer;
  font-size: .9rem; font-weight: 700;
}
.check input[type="checkbox"] {
  width: 22px; height: 22px; flex: none; margin: 0;
  accent-color: var(--forest);
}
.check input[disabled] { opacity: .45; }
.check input[disabled] + span { color: var(--text-dim); }

/* ---- whose trip it is -------------------------------------------------- */
/* A colour per driver, set on the chip as --who / --who-ink. Until 1 October
   2026 it also took over the card's left edge; the office asked for the status
   there instead, so the driver's colour is now only ever the chip with their
   name in it. The colours themselves are in data.js (DRIVER_COLOURS). */

.who {
  display: inline-block;
  padding: 4px 11px;
  border-radius: 999px;
  background: var(--who, var(--surface-2));
  color: var(--who-ink, var(--text));
  font-size: .84rem; font-weight: 700;
  white-space: nowrap;
}

.swatches { display: flex; flex-wrap: wrap; gap: 8px; }
.swatch {
  width: 34px; height: 34px;
  border: 2px solid transparent;
  border-radius: 50%;
  background: var(--sw);
  cursor: pointer;
  padding: 0;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.12);
}
.swatch[aria-pressed="true"] {
  border-color: var(--text);
  box-shadow: inset 0 0 0 2px var(--surface);
}

/* ---- send from the list ----------------------------------------------- */
/* A trip with nobody on it carries its own driver picker. The bar is tucked
   under the card and shares its left edge marker, so it reads as part of the
   trip rather than as a loose control between two of them. */

.assign { display: flex; flex-direction: column; }
.assign .trip { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.assign__bar {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 12px 9px 14px;
  border: 1px solid var(--border);
  border-top: 0;
  border-left: 6px solid var(--st-unassigned);   /* lines up with the card above it */
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  background: var(--surface-2);
}
.assign__bar select {
  flex: 1; min-width: 0;
  min-height: 40px; padding: 8px 10px;
  border: 1.5px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text);
  font: inherit; font-size: .84rem;
}
.assign__bar .btn { flex: none; }

/* ---- import review ---------------------------------------------------- */

.imp { cursor: pointer; }
.imp--warn { border-left: 3px solid var(--warn); }
.imp input[type="checkbox"] { width: 22px; height: 22px; flex: none; accent-color: var(--forest); }

/* ---- a confirmation, before it is sent -------------------------------- */

/* Specific enough to beat `.field textarea`, which sets a much shorter
   minimum. A confirmation is read whole before it is sent, so it gets the
   height, and the line breaks are the layout — they must not be reflowed
   away, only wrapped when a line is too long for the phone. */
.field textarea.confirm__text {
  min-height: 58vh;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .78rem; line-height: 1.55;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* ---- the monthly report ------------------------------------------------ */

.report__cards { display: flex; flex-direction: column; gap: 10px; }
.report__sum { border-color: var(--gold); background: var(--surface-2); }
.report__sum .kv dd { font-size: 1.05rem; }

/* The paper version, which is not on screen at all until it is printed. */
.report__print { display: none; }

.report__table--screen { width: 100%; border-collapse: collapse; font-size: .82rem; }
.report__table--screen th, .report__table--screen td {
  padding: 6px 4px; border-bottom: 1px solid var(--border); text-align: left;
}
.report__table--screen th { font-size: .68rem; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-dim); }
.report__table--screen .num { text-align: right; }

/* ---- a running balance ------------------------------------------------- */

.bal__out { color: var(--text-dim); }
.bal__off { opacity: .5; }
.bal__line { border-top: 1.5px solid var(--border); border-bottom: 0; padding-top: 11px; margin-top: 2px; }
.bal__line dt { font-weight: 700; color: var(--text); }
.bal__line dd { font-size: 1.15rem; }
.bal__good { color: var(--good); }
.bal__bad  { color: var(--stop); }
.exp__add { border-top: 1px dashed var(--border); padding-top: 10px; }

/* ---- charts ------------------------------------------------------------ */
/* One measure per chart and one hue: the title says what is plotted, so the
   colour carries no identity and never has to be told apart from another
   series. Brand v2 moves the bars off orange (the guide keeps orange for the
   logo and the pattern line) and onto forest: 9.7:1 on white. The bar being
   read is deep gold, 5.0:1 on white and a different hue from forest, so it
   stands apart without a second colour to explain. Dark: sand, 8.7:1, with
   linen for the bar being read. */

.chart { margin: 0; }
.chart figcaption {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  margin-bottom: 6px;
}
.chart figcaption b { font-family: var(--serif); font-size: 1rem; color: var(--heading); }
.chart__note { font-size: .78rem; color: var(--text-dim); }
.chart svg { display: block; width: 100%; height: auto; overflow: visible; }

.chart__bar { fill: #2F4A3A; transition: fill .12s ease; }
.chart__bar--on { fill: #8A6A2F; }
.chart__hit { fill: transparent; cursor: pointer; }
.chart__grid { stroke: var(--border); stroke-width: 1; }
.chart__tick, .chart__x { fill: var(--text-dim); font-size: 8px; font-family: inherit; }
.chart__x--year { font-weight: 700; }
.chart__peak { fill: var(--text); font-size: 9px; font-weight: 700; font-family: inherit; }
.chart__read {
  margin-top: 4px; font-size: .82rem; font-weight: 700; color: var(--text-dim);
  min-height: 1.2em;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .chart__bar { fill: #D9C39A; }
  :root:not([data-theme="light"]) .chart__bar--on { fill: #F7F3EA; }
}
:root[data-theme="dark"] .chart__bar { fill: #D9C39A; }
:root[data-theme="dark"] .chart__bar--on { fill: #F7F3EA; }

/* ---- demo switcher (demo page only) ------------------------------------ */

.demobar {
  /* In the page flow, under the app — fixed, it would sit on the driver's
     action bar and the toasts. */
  width: fit-content; margin: 0 auto calc(16px + env(safe-area-inset-bottom, 0px));
  display: flex; align-items: center; gap: 6px;
  padding: 5px 6px 5px 12px;
  background: var(--ink); color: var(--linen);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lift);
  font-size: .78rem; font-weight: 700;
}
.demobar span { letter-spacing: .16em; text-transform: uppercase; color: var(--sand); margin-right: 4px; }
.demobar button {
  min-height: 34px; padding: 6px 10px;
  border: 1px solid rgba(217,195,154,.4); border-radius: var(--radius);
  background: transparent; color: var(--linen);
  font: inherit; cursor: pointer;
}
.demobar button[aria-pressed="true"] { background: var(--forest); border-color: var(--forest); }

/* ---- printing ---------------------------------------------------------- */
/* What comes out of "Save as PDF". Everything that belongs to the app — tabs,
   buttons, the month picker, the screen cards — is taken away, and what is
   left is the table an accountant asked for. */

@media print {
  @page { size: A4; margin: 16mm 14mm; }

  :root {
    /* Print in ink on paper, whatever the phone's theme was. */
    --bg: #fff; --surface: #fff; --surface-2: #fff;
    --text: #2B2B26; --text-dim: #6F6A5E; --border: #E5DCC9;
    --heading: #2F4A3A;
  }

  body { background: #fff; color: #2B2B26; font-size: 10.5pt; font-family: var(--sans); }

  .topbar, .tabs, .actionbar, .demobar, #toast-host,
  .noprint, .btn, .backbar, .report__cards, .banner { display: none !important; }

  .shell, main { max-width: none; padding: 0; margin: 0; box-shadow: none; gap: 0; }
  .card { border: 0; box-shadow: none; padding: 0; }

  .report__print { display: block; }

  /* Brand bands print in colour: without this, browsers drop backgrounds and
     the white title prints white on white. */
  .report__head, .report__footband, .report__table th {
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  .report__head { background: var(--forest); color: #fff; margin-bottom: 8mm; }
  .report__band {
    display: flex; justify-content: space-between; align-items: center; gap: 8mm;
    padding: 9mm 8mm 4mm;
  }
  .report__eye {
    font-size: 8pt; letter-spacing: .2em; text-transform: uppercase; color: var(--sand);
  }
  .report__head h1 {
    font-family: var(--serif); font-size: 24pt; color: #fff; margin: 1.5mm 0 1mm;
  }
  .report__head p:not(.report__eye) { font-size: 10.5pt; color: #DDE3DA; }
  .report__badge { width: 22mm; height: 22mm; flex: none; }
  /* The pattern line along the bottom of the band, green left below it. */
  .report__line {
    height: 5mm; margin-bottom: 4mm;
    background: var(--line) repeat-x left center / auto 5mm;
  }
  .report__footband {
    display: flex; justify-content: space-between; gap: 6mm;
    margin-top: 8mm; padding: 4mm 8mm;
    background: var(--forest); color: #fff; font-size: 9pt;
  }
  .report__footband b { color: var(--sand); }

  .report__table { width: 100%; border-collapse: collapse; font-size: 10.5pt; }
  .report__table th, .report__table td {
    padding: 2.4mm 2mm;
    border-bottom: 0.5pt solid #bbb;
    text-align: left;
    vertical-align: baseline;
  }
  .report__table th {
    font-size: 8.5pt; text-transform: uppercase; letter-spacing: .06em;
    background: var(--linen); color: var(--forest);
    border-bottom: 1pt solid var(--gold);
  }
  .report__table .num { text-align: right; font-variant-numeric: tabular-nums; }

  /* A statement's columns, sized for what goes in them: the date never wraps,
     the money columns are as wide as the widest figure and no wider, and
     everything left over goes to the guest and the route. */
  .report__table--trips .col-date { width: 20mm; white-space: nowrap; }
  .report__table--trips .col-km   { width: 12mm; }
  .report__table--trips .col-cash { width: 24mm; }
  .report__bottom { margin-top: 8mm; width: 62%; }
  .report__bottom td { border-bottom: 0; padding: 1.6mm 2mm; }
  .report__table tfoot td {
    font-weight: 700;
    border-top: 1pt solid var(--gold);
    border-bottom: 0;
    padding-top: 3mm;
  }
  /* A driver's row is never split across two pages. */
  .report__table tr { break-inside: avoid; }
  .report__table thead { display: table-header-group; }

  /* The second line in a cell: the time under the date, the route under the
     guest, what an extra was under its amount. Quieter than the figure it
     belongs to, so a column of totals still reads as a column. */
  .report__sub {
    display: block;
    font-size: 8.5pt;
    color: #444;
    font-weight: 400;
    margin-top: 0.6mm;
  }
  .report__table .num .report__sub { font-variant-numeric: normal; }

  .report__flag {
    margin-top: 4mm;
    font-size: 9pt;
    padding: 2mm 3mm;
    border-left: 2pt solid var(--gold);
  }

  .report__foot {
    margin-top: 8mm; padding-top: 3mm;
    border-top: 0.5pt solid #bbb;
    font-size: 8.5pt; color: #444;
  }
  .report__foot p + p { margin-top: 1.5mm; }

  a { color: #000; text-decoration: none; }
}

[hidden] { display: none !important; }

/* ---- calendar filter ------------------------------------------------ */
/* One driver, one ref, one "Bill to". A lighter card than the week above it:
   it is a control, not content. */
.calfilter { padding: 12px 14px; box-shadow: none; }
.calfilter .field > label { font-size: .68rem; }
.calfilter select { min-height: 42px; padding: 8px 10px; }

/* ---- calendar: List / Day switch and the day timeline ------------------ */
/* Added 1 October 2026 (cal-day.js). The switch sits under the week with the
   Today button. The timeline draws one hour as 60px; a trip is a block from
   its pickup for the length of the drive, tinted in its status colour with
   the solid status colour down its edge — the same reading as a trip card.
   The tint is mixed into the surface, so text stays --text on it in both
   themes; where color-mix is missing it falls back to the plain surface-2. */

.seg { display: inline-flex; border: 1.5px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.seg button {
  min-height: 38px; padding: 6px 16px;
  border: 0; background: var(--surface); color: var(--text);
  font: inherit; font-size: .88rem; font-weight: 700; cursor: pointer;
}
.seg button + button { border-left: 1.5px solid var(--border); }
.seg button[aria-pressed="true"] { background: var(--btn-bg); color: #fff; }

.tl {
  position: relative;
  margin: 6px 0 0;
  padding-left: 52px;             /* the hour labels */
}
.tl__hour {
  position: absolute; left: 0; right: 0; height: 0;
  border-top: 1px solid var(--border);
}
.tl__hour span {
  position: absolute; left: 0; top: -.62em; width: 46px;
  padding-right: 6px; text-align: right;
  background: var(--bg);
  font-size: .76rem; font-variant-numeric: tabular-nums; color: var(--text-dim);
}
.tl__lane { position: absolute; top: 0; bottom: 0; left: 52px; right: 0; cursor: copy; }

.tl__ev {
  --st: var(--st-unassigned);
  position: absolute;
  display: flex; flex-direction: column; gap: 1px;
  overflow: hidden;
  padding: 3px 6px 3px 8px;
  text-align: left;
  border: 0; border-left: 4px solid var(--st);
  border-radius: var(--radius);
  background: var(--surface-2);
  background: color-mix(in srgb, var(--st) 20%, var(--surface));
  color: var(--text);
  font: inherit; font-size: .8rem; line-height: 1.2;
  cursor: pointer;
}
.tl__ev[data-status="assigned"]  { --st: var(--st-sent); }
.tl__ev[data-status="accepted"],
.tl__ev[data-status="enroute"],
.tl__ev[data-status="atpickup"],
.tl__ev[data-status="onboard"]   { --st: var(--st-live); }
.tl__ev[data-status="completed"] { --st: var(--st-done); }
.tl__ev[data-status="cancelled"] { --st: var(--st-cancel); }
.tl__ev:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; z-index: 2; }
.tl__line { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl__line b { font-weight: 700; color: var(--heading); }
.tl__route { color: var(--text); }
.tl__who { color: var(--text-dim); }
.tl__dot {
  display: inline-block; width: 9px; height: 9px; margin-right: 5px;
  border-radius: 50%; vertical-align: 0;
}
/* Trips with no pickup time can't be placed, so they're listed above. */
.tl__untimed { display: flex; flex-direction: column; gap: 6px; }
.tl__ev--flat { position: static; width: 100%; }

/* Now, on today only. Red because every phone calendar draws it red. */
.tl__now {
  position: absolute; left: -6px; right: 0; height: 0; z-index: 3;
  border-top: 2px solid var(--stop);
  pointer-events: none;
}
.tl__now::before {
  content: ""; position: absolute; left: -4px; top: -6px;
  width: 10px; height: 10px; border-radius: 50%; background: var(--stop);
}

/* The calendar's filter, folded behind one line with an arrow (owner-calendar.js).
   The browser's own marker is hidden and replaced by a chevron that turns
   down when open, so it looks the same on iPhone, Android and desktop. */
.calfold > summary {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  min-height: 44px; padding: 6px 2px;
  list-style: none;
  font-weight: 700; font-size: .95rem; color: var(--accent-text); cursor: pointer;
}
.calfold > summary::-webkit-details-marker { display: none; }
.calfold__arrow {
  width: 9px; height: 9px; flex: none;
  border-right: 2.5px solid currentColor; border-bottom: 2.5px solid currentColor;
  transform: rotate(-45deg);
  transition: transform .15s;
}
.calfold[open] .calfold__arrow { transform: rotate(45deg); }
.calfold__on {
  font-weight: 400; font-size: .84rem; color: var(--text);
  background: var(--surface-2); border-radius: 999px; padding: 3px 10px;
}
.calfold[open] > summary { margin-bottom: 8px; }

/* The app's own question box (ui.js ask / tell / askText), in place of the
   browser's confirm(), which a sandboxed frame silently answers "no". */
.ask { position: fixed; inset: 0; z-index: 80; display: flex; align-items: center; justify-content: center; padding: 16px; }
.ask__shade { position: absolute; inset: 0; background: rgba(16, 21, 15, .5); }
.ask__box {
  position: relative; width: 100%; max-width: 440px; max-height: 85vh; overflow-y: auto;
  padding: 18px 16px 16px;
  background: var(--surface); color: var(--text);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lift);
}
.ask__text { margin: 0 0 14px; white-space: pre-line; line-height: 1.45; overflow-wrap: anywhere; }
.ask__field {
  width: 100%; min-height: 44px; margin: 0 0 14px; padding: 8px 10px;
  border: 1.5px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text); font: inherit;
}
.ask__buttons { display: flex; gap: 8px; justify-content: flex-end; }
.ask__buttons .btn { flex: 1; }

/* Overnight on-duty blocks (duty.js) cross midnight. The edge cut by
   midnight goes square, as a phone calendar draws an event that carries on
   into the next day; the other day's date sits beside the pickup time. */
.tl__ev[data-from-before="true"] { border-top-left-radius: 0; border-top-right-radius: 0; }
.tl__ev[data-into-next="true"]   { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.tl__date { font-weight: 700; color: var(--heading); }   /* gold-text was 3.7:1 on the tint */
.tl__night { color: var(--text-dim); font-size: .74rem; }
/* An overnight block is hours tall, so its first line (time, date, guest)
   wraps instead of cutting off — four side by side are a quarter of the
   width each. The route and driver lines stay one line each. */
.tl__ev[data-night="true"] .tl__line:first-child { white-space: normal; overflow-wrap: anywhere; }

/* ---- Day view quick look -------------------------------------------- */
/* Tapping a block on the timeline opens this sheet from the bottom: the trip
   card, the driver bar (send / change) and Open booking. A sheet rather than
   a page so the day stays where it was underneath (owner-calendar.js). */
.sheet { position: fixed; inset: 0; z-index: 50; display: flex; flex-direction: column; justify-content: flex-end; }
.sheet__shade { position: absolute; inset: 0; background: rgba(16, 21, 15, .45); }
.sheet__panel {
  position: relative;
  width: 100%; max-width: 560px; margin: 0 auto;
  max-height: 85vh; overflow-y: auto;
  padding: 16px 16px calc(16px + env(safe-area-inset-bottom, 0px));
  background: var(--bg);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: var(--shadow-lift);
}
.sheet__panel .row .btn { flex: 1; }
html.sheet-open { overflow: hidden; }

/* ---- Day view colour key (cal-day.js colourKey) --------------------- */
.tl__key {
  display: flex; flex-wrap: wrap; gap: 6px 14px;
  font-size: .78rem; color: var(--text-dim);
}
.tl__key > span { display: inline-flex; align-items: center; gap: 6px; }
.tl__sw {
  width: 16px; height: 14px; flex: none;
  border-left: 4px solid var(--st);
  border-radius: 2px;
  background: var(--surface-2);
  background: color-mix(in srgb, var(--st) 20%, var(--surface));
}
/* A dot in three driver colours: says "a colour per driver", not one driver. */
.tl__dot--key {
  margin-right: 0;
  background: conic-gradient(#2670CC 0 33%, #C2338A 0 66%, #1BAF7A 0);
}
.tl__keynow { width: 16px; height: 0; border-top: 2px solid var(--stop); }

/* The car picker sits at the front of a trip's bar (assign.js). */
.assign__bar { flex-wrap: wrap; }
.assign__bar select.assign__car { flex: 0 1 auto; width: auto; max-width: 46%; }
.tl__car { font-weight: 700; color: var(--text); }
.sheet__panel:focus { outline: none; }

/* The pickup time in the Day view's quick look (owner-calendar.js). */
.sheet__time { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sheet__timelabel { font-size: .84rem; font-weight: 700; color: var(--text-dim); }
.sheet__time select {
  min-height: 42px; padding: 6px 10px;
  border: 1.5px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text);
  font: inherit; font-size: 1rem; font-variant-numeric: tabular-nums;
}
.sheet__time .btn { margin-left: auto; }

/* The driver/car bar under a card takes the card's status colour down its
   edge, so the two read as one piece. It used to be fixed to the No driver
   yet grey; once that became a strong blue (1 October 2026) a Sent card had a
   blue foot. */
.assign:has(> .trip[data-status="assigned"]) > .assign__bar { border-left-color: var(--st-sent); }
.assign:has(> .trip[data-status="accepted"]) > .assign__bar,
.assign:has(> .trip[data-status="enroute"]) > .assign__bar,
.assign:has(> .trip[data-status="atpickup"]) > .assign__bar,
.assign:has(> .trip[data-status="onboard"]) > .assign__bar { border-left-color: var(--st-live); }
.assign:has(> .trip[data-status="completed"]) > .assign__bar { border-left-color: var(--st-done); }

/* Swiping between days in the Day view (cal-day.js wireSwipe): the new day
   slides a short way in from the side it came from. Short and small, so it
   reads as "moved a day" without slowing anyone down; off for anyone who has
   asked their phone for less motion. */
/* touch-action pan-y: up/down still scrolls (and pinch still zooms), but a
   sideways swipe is the app's — without it Chrome on a phone read a swipe
   right as the browser's own Back and left the app (found in testing,
   2 October 2026). overscroll-behavior-x stops the same from the page edge. */
.tl-swipe {
  display: flex; flex-direction: column; gap: 14px;
  touch-action: pan-y pinch-zoom;
  overscroll-behavior-x: none;
}
.tl-swipe--next { animation: tl-in-next .18s ease-out; }
.tl-swipe--prev { animation: tl-in-prev .18s ease-out; }
@keyframes tl-in-next { from { transform: translateX(28px); opacity: .4; } to { transform: none; opacity: 1; } }
@keyframes tl-in-prev { from { transform: translateX(-28px); opacity: .4; } to { transform: none; opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .tl-swipe--next, .tl-swipe--prev { animation: none; } }



/* ---- pinned, compact week card (owner-calendar.js, 2 October 2026) ----- */
/* With the header no longer pinned, an installed iPhone app would show the
   page sliding under the clock and battery. This strip, the height of that
   area (0 in a browser tab), keeps it the header's green at all times. */
body::before {
  content: ""; position: fixed; left: 0; right: 0; top: 0; z-index: 40;
  height: env(safe-area-inset-top, 0px);
  background: var(--header-bg);
  pointer-events: none;
}
/* The week, the day and its controls stay at the top in Day and List. The
   page colour behind it runs into the side gutters so nothing scrolling
   underneath shows at its edges. */
.calstick {
  position: sticky;
  top: env(safe-area-inset-top, 0px);
  z-index: 20;
  margin: -6px -16px 0;
  padding: 6px 16px 8px;
  background: var(--bg);
  touch-action: pan-y pinch-zoom;           /* sideways = change week (wireSwipe) */
  overscroll-behavior-x: none;
}
.calweek { padding: 8px 10px 10px; }
.calweek .cal__head .btn--sm { min-height: 34px; padding: 4px 14px; }
.calweek .cal__head h2 { font-size: 1.02rem; }
.calweek .cal__grid--week { margin-top: 6px; gap: 3px; }
.calweek .cal__grid--week .cal__day { min-height: 52px; padding: 5px 1px; gap: 1px; }
.calweek .cal__grid--week .cal__dow { font-size: .6rem; }
.calweek .cal__grid--week .cal__date { font-size: .98rem; }
.calweek .cal__grid--week .cal__count { min-width: 17px; height: 17px; font-size: .66rem; }
.calweek__day {
  display: flex; align-items: center; justify-content: space-between; gap: 6px 8px; flex-wrap: wrap;
  margin-top: 8px; padding-top: 8px;
  border-top: 1px solid var(--border);
}
.calweek__day h2 { font-size: 1.12rem; }
.calweek__ctl { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.calweek__ctl .btn--sm { min-height: 34px; padding: 4px 10px; }
.calweek__ctl .seg button { min-height: 34px; padding: 4px 11px; font-size: .84rem; }
/* Filter line and Import, under the pinned card, scrolling with the page. */
.calbar2 { display: flex; align-items: flex-start; gap: 8px; }
.calbar2 > .calfold { flex: 1 1 auto; min-width: 0; }
.calbar2 > .btn { flex: none; margin-top: 3px; }

/* Search in the pinned week card (owner-calendar.js, 2 October 2026). The
   magnifier and "+" are square icon buttons so the day line still fits a
   phone; the search bar opens under the day line and stays pinned with it. */
.calweek__icon { min-width: 36px; padding-left: 0 !important; padding-right: 0 !important; }
.calweek__add { font-size: 1.25rem; line-height: 1; }
.calweek__icon[aria-pressed="true"] { background: var(--btn-bg); border-color: var(--btn-bg); color: #fff; }
.calsearch { display: flex; gap: 6px; margin-top: 8px; }
.calsearch input {
  flex: 1; min-width: 0; min-height: 40px; padding: 8px 10px;
  border: 1.5px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text); font: inherit; font-size: 1rem;  /* 16px: no iPhone zoom-in */
}
.calsearch input:focus { outline: 2px solid var(--focus); outline-offset: 1px; }
.calresults { gap: 12px; }
.tripsearch { margin-top: 0; }

/* ---- full-screen search (owner-calendar.js, 2 October 2026) ------------ */
/* Over everything, sticky week included: the box at the top, the results
   scrolling under it, the whole height of the phone for them. */
.srch {
  position: fixed; inset: 0; z-index: 60;
  display: flex; flex-direction: column;
  background: var(--bg);
}
.srch__bar {
  display: flex; gap: 8px; align-items: center;
  max-width: 640px; width: 100%; margin: 0 auto;
  padding: calc(10px + env(safe-area-inset-top, 0px)) 16px 10px;
  border-bottom: 1px solid var(--border);
}
.srch__bar input {
  flex: 1; min-width: 0; min-height: 44px; padding: 8px 12px;
  border: 1.5px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text); font: inherit; font-size: 1rem;   /* 16px: no iPhone zoom */
}
.srch__bar input:focus { outline: 2px solid var(--focus); outline-offset: 1px; }
.srch__results {
  flex: 1; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 10px;
  max-width: 640px; width: 100%; margin: 0 auto;
  padding: 12px 16px calc(16px + env(safe-area-inset-bottom, 0px));
}
.srch__results .stack { gap: 6px; }

/* The compact trip row (parts.js tripRow). */
.trow {
  --st: var(--st-unassigned);
  display: flex; flex-direction: column; gap: 2px;
  width: 100%; text-align: left;
  padding: 7px 10px 7px 10px;
  background: var(--surface);
  border: 1px solid var(--border); border-left: 5px solid var(--st);
  border-radius: var(--radius);
  font: inherit; font-size: .86rem; line-height: 1.3; color: var(--text);
  cursor: pointer;
}
.trow[data-status="assigned"]  { --st: var(--st-sent); }
.trow[data-status="accepted"], .trow[data-status="enroute"],
.trow[data-status="atpickup"], .trow[data-status="onboard"] { --st: var(--st-live); }
.trow[data-status="completed"] { --st: var(--st-done); }
.trow[data-status="cancelled"] { --st: var(--st-cancel); opacity: .75; }
.trow__top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.trow__top b { font-family: var(--serif); color: var(--heading); font-size: .95rem; }
.trow .pill { font-size: .62rem; padding: 2px 7px; }
.trow__line { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.trow__dim { color: var(--text-dim); font-size: .8rem; }
.srch__opt { min-height: 40px; max-width: 640px; width: 100%; margin: 0 auto; padding: 8px 16px; border-bottom: 1px solid var(--border); font-size: .9rem; }

/* ======================================================================
   Desktop — the office on a computer (2 October 2026)
   Owners only (.shell--office, set in app.js): drivers use phones and keep
   the phone layout at any width. Nothing here applies below 1024px, and on
   a phone the calendar's two wrappers (.calwrap, .calside, .calmain) are
   display: contents, so the phone layout is exactly as it was.
   ====================================================================== */

.calwrap, .calside, .calmain { display: contents; }
.kbdhint { display: none; }

@media (min-width: 1024px) {
  .shell--office { max-width: 1320px; }
  .shell--office main { padding: 20px 28px 32px; gap: 18px; }
  .shell--office .tabs { padding: 0 8px; }
  .shell--office .tabs button { flex: 0 0 auto; padding: 12px 20px 10px; }

  /* Calendar: week, filter and Import in a left column that stays put; the
     day (timeline or list) takes the rest of the width. */
  .shell--office .calwrap {
    display: grid; grid-template-columns: 360px minmax(0, 1fr);
    gap: 24px; align-items: start;
  }
  .shell--office .calside {
    display: flex; flex-direction: column; gap: 12px;
    position: sticky; top: 16px;
    max-height: calc(100vh - 32px); overflow-y: auto;
  }
  .shell--office .calmain { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
  .shell--office .calstick {
    position: static; margin: 0; padding: 0; background: none;
  }
  .shell--office .calweek .cal__grid--week .cal__day { min-height: 58px; }
  .shell--office .calweek .cal__day:hover:not([aria-pressed="true"]) { background: var(--border); }
  .shell--office .kbdhint {
    display: block; font-size: .78rem; color: var(--text-dim); line-height: 1.7;
  }
  .kbdhint kbd {
    display: inline-block; min-width: 1.6em; padding: 0 5px; text-align: center;
    border: 1px solid var(--border); border-bottom-width: 2px; border-radius: 4px;
    background: var(--surface); font: inherit; font-size: .74rem; color: var(--text);
  }
  /* More room on a wide timeline: a taller hour is not needed, but blocks
     side by side get real width, and lines no longer cut short as often. */
  .shell--office .tl__ev { font-size: .84rem; }

  /* Lists of cards become columns of cards. */
  .shell--office .deskgrid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
    gap: 12px; align-items: start;
  }
  .shell--office .calmain .deskgrid { grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); }

  /* Reports: the drivers' cards side by side. */
  .shell--office .report__cards {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 12px;
  }

  /* Expenses: standing costs and the month's own side by side; the month
     picker and the balance stay full width above them. */
  .shell--office #view[data-route="expenses"] {
    display: grid; grid-template-columns: 1fr 1fr; gap: 18px; align-items: start;
  }
  .shell--office #view[data-route="expenses"] > :not(section:not(.bal)) { grid-column: 1 / -1; }

  /* Forms and a single trip read best at a readable width, centred. */
  .shell--office #view:is([data-route="trip"], [data-route="new"], [data-route="import"],
    [data-route="confirm"], [data-route="complete"]) {
    width: 100%; max-width: 760px; margin-inline: auto;
  }
  /* The Trips tab: the search box and Add button needn't span 1,300px;
     nor does the month picker on Reports and Expenses. */
  .shell--office .tripsearch { max-width: 560px; }
  .shell--office #view[data-route="trips"] > .btn--primary,
  .shell--office #view[data-route="fleet"] > .btn--primary,
  .shell--office #view[data-route="people"] > .btn--primary { align-self: flex-start; padding-inline: 28px; }
  .shell--office #view:is([data-route="reports"], [data-route="expenses"]) > .field { max-width: 520px; }

  /* The quick look opens in the middle of the screen, not from the bottom. */
  .shell--office .sheet { justify-content: center; }
  .shell--office .sheet__panel { border-radius: var(--radius-lg); max-height: 80vh; }

  /* Search: wider results, two columns of rows. */
  .shell--office .srch__bar, .shell--office .srch__opt, .shell--office .srch__results { max-width: 1000px; }
  .shell--office .srch__results .stack { display: grid; grid-template-columns: repeat(auto-fill, minmax(420px, 1fr)); gap: 8px; }
  .shell--office .srch__results .stack > .eyebrow { grid-column: 1 / -1; }

  /* A pointer can hover: say what's clickable. */
  .shell--office .trip:hover, .shell--office .trow:hover, .shell--office .tl__ev:hover {
    box-shadow: var(--shadow-lift);
  }
}

/* ---- Notifications tab (owner-notify.js, 2 October 2026) ------------------ */

.nlist { display: flex; flex-direction: column; gap: 12px; }
.ntype.is-off { opacity: .75; }
.nwho { list-style: none; margin: 0; padding: 0; }
.nwho__row {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding: 8px 0; border-bottom: 1px solid var(--border);
}
.nwho__row:last-child { border-bottom: 0; }
.nwho__row.is-off > span:last-child { color: var(--warn); font-weight: 700; }
.chip--sm { min-height: 32px; padding: 4px 10px; font-size: .8rem; font-weight: 600; font-family: ui-monospace, Menlo, Consolas, monospace; }
/* The preview looks like a phone's notification, not like the app. */
.npreview {
  display: flex; flex-direction: column; gap: 2px;
  padding: 10px 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 14px;
  font-size: .86rem; line-height: 1.35;
}
.npreview__app { font-size: .7rem; letter-spacing: .06em; text-transform: uppercase; color: var(--text-dim); }
.npreview__body { color: var(--text); }
.linkbtn {
  align-self: flex-start; background: none; border: 0; padding: 4px 0;
  color: var(--accent-text); text-decoration: underline; cursor: pointer; font: inherit;
}
.nhelp summary { cursor: pointer; font-weight: 700; }
.nhelp ul { margin: 10px 0 6px; padding-left: 18px; }
.nhelp code { font-size: .82rem; }
/* Unsaved changes: pinned to the bottom of the screen until saved or undone. */
.savebar {
  position: sticky; bottom: calc(10px + env(safe-area-inset-bottom, 0px)); z-index: 5;
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px;
  background: var(--surface);
  border: 1.5px solid var(--hi);
  border-radius: var(--radius-lg);
  box-shadow: 0 6px 20px rgba(0,0,0,.18);
}
.savebar > span { flex: 1; font-weight: 700; }
.savebar[hidden] { display: none; }

@media (min-width: 1024px) {
  .shell--office #view[data-route="notify"] { width: 100%; max-width: 1100px; margin-inline: auto; }
  .shell--office #view[data-route="notify"] .nlist { display: grid; grid-template-columns: 1fr 1fr; align-items: start; }
}
.nedit > summary { cursor: pointer; font-weight: 700; font-size: .86rem; color: var(--accent-text); padding: 4px 0; }
.nedit > .stack { margin-top: 8px; }
