@charset "UTF-8";
/* ==========================================================================
   LAXToGo design system  ·  v0.4.0
   Limoform visual language: calm, light, Apple-system typography, generous
   space. One token set drives customer booking, admin console and the
   chauffeur portal — no per-page colours, radii or spacing anywhere.
   ========================================================================== */

/* ------------------------------------------------------------------ tokens */

:root {
  /* --- surfaces ------------------------------------------------------- */
  --bg:            #ffffff;   /* page */
  --surface:       #ffffff;   /* cards */
  --surface-soft:  #fbfbfd;   /* soft sections, inset panels */
  --surface-sunk:  #f5f5f7;   /* wells, disabled fields */
  --surface-dark:  #1d1d1f;   /* dark chrome: sidebar, footer */

  /* --- text ----------------------------------------------------------- */
  --text:          #1d1d1f;   /* 15.8:1 on white */
  --text-muted:    #6e6e73;   /*  5.3:1 on white  */
  --text-subtle:   #86868b;   /*  3.9:1 — large text and glyphs only */
  --text-on-dark:  #f5f5f7;
  --text-on-dark-muted: #a1a1a6;

  /* --- lines ---------------------------------------------------------- */
  --line:          #e8e8ed;
  --line-strong:   #d2d2d7;

  /* --- brand + action -------------------------------------------------- */
  --accent:        #0071e3;   /* primary action */
  --accent-hover:  #0077ed;
  --accent-press:  #006edb;
  --accent-soft:   #e8f2fd;
  --accent-text:   #0062c4;   /* 4.6:1 on white, safe for text */
  --gold:          #c9a227;   /* LAXToGo brand mark only, never body text */
  --gold-soft:     #faf3e0;

  /* --- semantic -------------------------------------------------------- */
  --success:       #1d7a44;  --success-soft: #e6f5ec;
  --danger:        #c1121f;  --danger-soft:  #fdeaec;
  --warning:       #9a6700;  --warning-soft: #fff5e0;
  --info:          #0062c4;  --info-soft:    #e8f2fd;

  /* --- spacing: 4pt rhythm --------------------------------------------- */
  --s-1: .25rem;  --s-2: .5rem;   --s-3: .75rem;  --s-4: 1rem;
  --s-5: 1.25rem; --s-6: 1.5rem;  --s-7: 1.75rem; --s-8: 2rem;
  --s-10: 2.5rem; --s-12: 3rem;   --s-16: 4rem;   --s-20: 5rem;

  /* --- radius ---------------------------------------------------------- */
  --r-xs: 6px; --r-sm: 10px; --r-md: 12px;
  --r-lg: 16px; --r-xl: 20px; --r-2xl: 24px; --r-pill: 999px;

  /* --- elevation: subtle, never muddy ---------------------------------- */
  --e-1: 0 1px 2px rgb(0 0 0 / .04);
  --e-2: 0 2px 8px rgb(0 0 0 / .05);
  --e-3: 0 8px 24px rgb(0 0 0 / .07);
  --e-4: 0 16px 48px rgb(0 0 0 / .10);

  /* --- z-index scale ---------------------------------------------------- */
  --z-base: 0; --z-sticky: 20; --z-nav: 40;
  --z-dropdown: 60; --z-overlay: 100; --z-modal: 110; --z-toast: 200;

  /* --- motion: 140–220ms ------------------------------------------------ */
  --dur-fast: 140ms; --dur: 180ms; --dur-slow: 220ms;
  --ease: cubic-bezier(.4, 0, .2, 1);
  --ease-out: cubic-bezier(0, 0, .2, 1);

  /* --- layout ----------------------------------------------------------- */
  --shell:      1180px;
  --shell-form: 48rem;    /* booking form: a focused sheet, not full-bleed */
  --sidebar-w:  248px;
  --tap:        44px;
  --field-h:    52px;     /* 50–56px on mobile per spec */
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-t: env(safe-area-inset-top, 0px);

  --font: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text",
          "Helvetica Neue", "Segoe UI", Roboto, Arial, sans-serif;

  color-scheme: light;
}

/* ------------------------------------------------------------------- reset */

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 1rem;
  line-height: 1.5;
  letter-spacing: -.01em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

img, svg, video { max-width: 100%; display: block; }
img { height: auto; }

h1, h2, h3, h4 { margin: 0; font-weight: 600; line-height: 1.12; letter-spacing: -.025em; }
h1 { font-size: clamp(1.75rem, 1.4rem + 1.4vw, 2.5rem); letter-spacing: -.035em; }
h2 { font-size: clamp(1.3rem, 1.15rem + .6vw, 1.6rem); letter-spacing: -.03em; }
h3 { font-size: 1.0625rem; }
h4 { font-size: .9375rem; }
p  { margin: 0 0 var(--s-3); }
p:last-child { margin-bottom: 0; }
a  { color: inherit; }

/* Prices, times and counters must not jitter as digits change. */
.tabular, .money, .stat__value, .summary__value, .vehicle-card__price,
.ride-card__time, .earnings-total { font-variant-numeric: tabular-nums; }

/* Focus is always visible, and always the same shape. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}
:focus:not(:focus-visible) { outline: none; }
::selection { background: var(--accent-soft); color: var(--text); }

/* --------------------------------------------------------------- a11y bits */

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; left: var(--s-4); top: -100px; z-index: var(--z-toast);
  background: var(--text); color: #fff;
  padding: var(--s-3) var(--s-5); border-radius: var(--r-pill);
  font-weight: 600; text-decoration: none;
  transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: calc(var(--s-4) + var(--safe-t)); }

/* Icons sit in the text baseline; the reset above makes svg display:block. */
.icon { flex: none; display: inline-block; vertical-align: -0.15em; }

/* ---------------------------------------------------------------- utilities */

.shell { width: 100%; max-width: var(--shell); margin-inline: auto; padding-inline: var(--s-5); }
.shell--form { max-width: var(--shell-form); }
.shell--narrow { max-width: 34rem; }
.shell--wide { max-width: 1400px; }
@media (min-width: 768px) { .shell { padding-inline: var(--s-8); } }

.stack       { display: grid; gap: var(--s-4); }
.stack--sm   { gap: var(--s-2); }
.stack--lg   { gap: var(--s-6); }
.row         { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.row--between{ justify-content: space-between; }
.grow        { flex: 1 1 auto; min-width: 0; }
.hidden      { display: none !important; }
.truncate    { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.center      { text-align: center; }

.eyebrow {
  display: inline-block; text-transform: uppercase;
  letter-spacing: .06em; font-size: .6875rem; font-weight: 600;
  color: var(--text-muted);
}
.muted  { color: var(--text-muted); }
.subtle { color: var(--text-muted); font-size: .8125rem; }
.text-sm{ font-size: .875rem; }
.text-danger  { color: var(--danger); }
.text-success { color: var(--success); }

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

.btn {
  --btn-bg: var(--surface);
  --btn-fg: var(--text);
  --btn-bd: var(--line-strong);

  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--s-2);
  min-height: var(--tap);
  padding: var(--s-3) var(--s-6);
  background: var(--btn-bg); color: var(--btn-fg);
  border: 1px solid var(--btn-bd); border-radius: var(--r-pill);
  font: inherit; font-size: .9375rem; font-weight: 500; line-height: 1;
  letter-spacing: -.01em; text-decoration: none; cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              opacity var(--dur-fast) var(--ease);
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.btn:hover:not(:disabled) { background: var(--surface-sunk); }
/* Opacity, not transform: press feedback must never move neighbouring content. */
.btn:active:not(:disabled) { opacity: .72; }

.btn--primary { --btn-bg: var(--accent); --btn-fg: #fff; --btn-bd: var(--accent); }
.btn--primary:hover:not(:disabled) { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn--primary:active:not(:disabled) { background: var(--accent-press); opacity: 1; }

.btn--dark   { --btn-bg: var(--text); --btn-fg: #fff; --btn-bd: var(--text); }
.btn--danger { --btn-bg: var(--danger); --btn-fg: #fff; --btn-bd: var(--danger); }
.btn--ghost  { --btn-bg: transparent; --btn-bd: transparent; }
.btn--ghost:hover:not(:disabled) { background: var(--surface-sunk); }
.btn--quiet  { --btn-bg: transparent; --btn-bd: var(--line-strong); }

.btn--lg { min-height: 3.25rem; font-size: 1rem; padding-inline: var(--s-8); }
.btn--sm { min-height: 2.25rem; font-size: .8125rem; padding: var(--s-2) var(--s-4); position: relative; }
/* Small controls keep a 44px hit area even when they look smaller. */
.btn--sm::after { content: ''; position: absolute; inset: -8px; }

.btn--block { width: 100%; }
.btn--split { justify-content: space-between; }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .4; cursor: not-allowed; }

/* Loading: spinner replaces the label, width preserved so nothing shifts. */
.btn[data-loading="true"] { color: transparent !important; pointer-events: none; position: relative; }
.btn[data-loading="true"] .icon { visibility: hidden; }
.btn[data-loading="true"]::before {
  content: ''; position: absolute; inset: 0; margin: auto;
  width: 1.125rem; height: 1.125rem; border-radius: 50%;
  border: 2px solid var(--btn-fg); border-top-color: transparent;
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.btn-group { display: flex; gap: var(--s-3); flex-wrap: wrap; }

.link {
  display: inline-flex; align-items: center; gap: var(--s-1);
  min-height: var(--tap); padding-inline: var(--s-1);
  background: none; border: 0; font: inherit; font-size: .9375rem; font-weight: 500;
  color: var(--accent-text); text-decoration: none; cursor: pointer;
}
.link:hover { text-decoration: underline; text-underline-offset: 3px; }

/* A link sitting inside a sentence. The base .link is a 44px-tall block built
   for standalone actions; dropped into help text it wrecks the line rhythm.
   This keeps the tap target honest via padding rather than min-height. */
.link--inline {
  min-height: 0;
  padding: 2px 0;
  font-size: inherit;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}

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

.field { display: grid; gap: var(--s-2); min-width: 0; }
.field--full { grid-column: 1 / -1; }

.field__label {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: .875rem; font-weight: 500; color: var(--text);
}
.field__optional { font-weight: 400; color: var(--text-muted); }
.field__required { color: var(--danger); }

.input, .select, .textarea {
  width: 100%;
  min-height: var(--field-h);
  padding: var(--s-3) var(--s-4);
  background: var(--surface); color: var(--text);
  border: 1px solid var(--line-strong); border-radius: var(--r-md);
  font: inherit; font-size: 1rem;   /* 16px stops iOS zooming on focus */
  letter-spacing: -.01em;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.textarea { min-height: 6.5rem; resize: vertical; line-height: 1.5; padding-block: var(--s-4); }

.input:hover, .select:hover, .textarea:hover { border-color: var(--text-subtle); }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-soft);
}
.input::placeholder, .textarea::placeholder { color: var(--text-subtle); }

.input:disabled, .select:disabled, .textarea:disabled {
  background: var(--surface-sunk); color: var(--text-subtle);
  cursor: not-allowed; opacity: .8;
}
/* Read-only reads differently from disabled. */
.input[readonly] { background: var(--surface-soft); border-style: dashed; }

/* ------------------------------------------------------- date & time fields */
/* These are .input like every other field; the only reason they looked wrong on
   a phone is user-agent chrome the stylesheet never contested. iOS gives a
   date/time input its own grey rounded pill, centres the value, and sizes the
   box to its content instead of honouring width:100% -- so next to a text field
   it sat shorter, narrower and off-baseline.

   -webkit-appearance:none is the load-bearing line. The rest aligns the shadow
   parts the UA draws inside the box.

   The input TYPE is deliberately unchanged. type=date and type=time are what
   raise the native wheel pickers on iOS and Android; replacing them with a text
   field and a custom calendar would be a downgrade on the exact devices this is
   meant to fix. */
.input[type="date"],
.input[type="time"] {
  -webkit-appearance: none;
  appearance: none;
  background-color: var(--surface);
  min-height: var(--field-h);
  line-height: 1.5;
  text-align: start;
}

/* iOS renders the value in this shadow node and centres it with its own margin. */
.input[type="date"]::-webkit-date-and-time-value,
.input[type="time"]::-webkit-date-and-time-value {
  text-align: start;
  margin: 0;
}

/* Chrome, Edge and Android Chrome add their own padding inside the edit region,
   on top of the field's, which is what made these look taller and misaligned. */
.input[type="date"]::-webkit-datetime-edit,
.input[type="time"]::-webkit-datetime-edit,
.input[type="date"]::-webkit-datetime-edit-fields-wrapper,
.input[type="time"]::-webkit-datetime-edit-fields-wrapper { padding: 0; }

/* The calendar / clock glyph gets the same treatment as the select chevron:
   20px, muted, sitting on the --s-4 inset rather than wherever the UA puts it. */
.input[type="date"]::-webkit-calendar-picker-indicator,
.input[type="time"]::-webkit-calendar-picker-indicator {
  inline-size: 20px; block-size: 20px;
  margin: 0; padding: 0;
  opacity: .55; cursor: pointer;
}
.input[type="date"]:hover::-webkit-calendar-picker-indicator,
.input[type="time"]:hover::-webkit-calendar-picker-indicator { opacity: .8; }

.select {
  appearance: none; padding-right: var(--s-10);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%236e6e73' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--s-4) center;
}

.field__help  { font-size: .8125rem; color: var(--text-muted); line-height: 1.45; }

/* Error sits under its own field, never only at the top of the form. */
.field__error {
  display: none; align-items: flex-start; gap: var(--s-2);
  font-size: .8125rem; font-weight: 500; color: var(--danger);
}
.field.is-invalid .field__error { display: flex; }
.field.is-invalid .input,
.field.is-invalid .select,
.field.is-invalid .textarea { border-color: var(--danger); }
.field.is-invalid .input:focus { box-shadow: 0 0 0 4px var(--danger-soft); }

.form-grid { display: grid; gap: var(--s-5); grid-template-columns: 1fr; }
@media (min-width: 640px) { .form-grid { grid-template-columns: 1fr 1fr; } }

fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
legend { padding: 0; font-size: .875rem; font-weight: 600; }

.check {
  display: flex; align-items: center; gap: var(--s-3);
  min-height: var(--tap); padding: var(--s-2) 0;
  font-size: .9375rem; cursor: pointer;
}
.check input[type="radio"] {
  inline-size: 1.25rem; block-size: 1.25rem;
  accent-color: var(--accent); flex: none; cursor: pointer; margin: 0;
}

/* The checkbox is drawn by the design system rather than the platform, because
   accent-color alone leaves iOS with a small square that matches nothing else
   on the form -- the "Add a return trip" box in particular read as unstyled.
   It keeps its real <input>, so the label, keyboard and screen reader all still
   work; only the paint changes. */
.check input[type="checkbox"] {
  -webkit-appearance: none;
  appearance: none;
  inline-size: 22px; block-size: 22px;
  flex: none; margin: 0; cursor: pointer;
  display: grid; place-content: center;
  background: var(--surface);
  border: 1.5px solid var(--line-strong);
  border-radius: var(--r-xs);
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}
/* The tick is a rotated L drawn in CSS: no icon request, and it cannot arrive
   late enough to shift the row. */
.check input[type="checkbox"]::after {
  content: "";
  inline-size: 10px; block-size: 5px;
  border-inline-start: 2px solid #fff;
  border-block-end:   2px solid #fff;
  transform: rotate(-45deg) translate(1px, -2px) scale(.6);
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.check input[type="checkbox"]:hover { border-color: var(--text-subtle); }
.check input[type="checkbox"]:checked {
  background: var(--accent); border-color: var(--accent);
}
.check input[type="checkbox"]:checked::after {
  opacity: 1;
  transform: rotate(-45deg) translate(1px, -2px) scale(1);
}
.check input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

/* Segmented control: service type, filters, tabs */
.segmented {
  display: inline-flex; gap: var(--s-1); padding: 4px; width: 100%;
  background: var(--surface-sunk); border-radius: var(--r-md);
}
.segmented__item {
  flex: 1 1 0; min-height: var(--tap);
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  background: transparent; border: 0; border-radius: var(--r-sm);
  font: inherit; font-size: .9375rem; font-weight: 500; color: var(--text-muted);
  text-decoration: none; cursor: pointer; white-space: nowrap;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.segmented__item:hover { color: var(--text); }

/* The selected segment is filled with the action colour, not a white pill.
   A white-on-grey pill reads as "slightly lighter" rather than "chosen", and
   on a phone in daylight it is genuinely ambiguous which side is active. */
.segmented__item[aria-pressed="true"],
.segmented__item[aria-selected="true"],
.segmented__item[aria-current="page"] {
  background: var(--accent);
  color: #fff;
  font-weight: 600;
  box-shadow: 0 1px 3px rgb(0 113 227 / .28);
}
.segmented__item[aria-pressed="true"] .icon,
.segmented__item[aria-selected="true"] .icon,
.segmented__item[aria-current="page"] .icon { color: #fff; }

.segmented__item[aria-pressed="true"]:hover,
.segmented__item[aria-selected="true"]:hover,
.segmented__item[aria-current="page"]:hover { background: var(--accent-hover); color: #fff; }

.segmented__item:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Selection must not rely on colour alone (WCAG 1.4.1). In forced-colours or
   high-contrast modes the fill is dropped, so the border carries the meaning. */
@media (forced-colors: active) {
  .segmented__item[aria-pressed="true"],
  .segmented__item[aria-selected="true"],
  .segmented__item[aria-current="page"] { border: 2px solid Highlight; }
}

.chip-group { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.chip {
  min-height: var(--tap); padding: var(--s-2) var(--s-5);
  background: var(--surface); color: var(--text-muted);
  border: 1px solid var(--line-strong); border-radius: var(--r-pill);
  font: inherit; font-size: .875rem; font-weight: 500; cursor: pointer;
  transition: all var(--dur-fast) var(--ease);
}
.chip:hover { border-color: var(--text-subtle); color: var(--text); }
.chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.chip[aria-pressed="true"] {
  background: var(--accent); color: #fff; border-color: var(--accent);
}

/* ---------------------------------------------------------------- gratuity */
/* "No tip" belongs in the group but not at the head of it. Declining is a
   legitimate choice and keeps a full 44px target and a real focus ring; it just
   stops competing visually with the rate most customers actually want. The
   dashed edge is the whole treatment -- it must NOT read as disabled, so the
   text stays at --text-muted (5.3:1) rather than fading out, and pressing it
   goes fully saturated exactly like any other chip. */
.chip--quiet {
  border-style: dashed;
  background: transparent;
  color: var(--text-muted);
}
.chip--quiet:hover { border-style: solid; }
.chip--quiet[aria-pressed="true"] {
  border-style: solid;
  background: var(--accent); color: #fff; border-color: var(--accent);
}

.tip-custom { margin-top: var(--s-3); max-inline-size: 22rem; }
.tip-custom[hidden] { display: none; }

/* The $ sits inside the field rather than beside it, so the control still reads
   as one box and the amount lines up with the other inputs. */
.tip-custom__row { position: relative; display: flex; align-items: center; }
.tip-custom__prefix {
  position: absolute; inset-inline-start: var(--s-4);
  font-size: 1rem; font-weight: 500; color: var(--text-muted);
  pointer-events: none;
}
.tip-custom__input {
  padding-inline-start: calc(var(--s-4) + 1ch + var(--s-2));
  /* Tabular figures: without them the width of the value shifts as digits
     change and the field jitters while typing. */
  font-variant-numeric: tabular-nums;
}

/* This error lives outside a .field wrapper, so the .field.is-invalid rule that
   reveals every other error does not reach it. Drive it from [hidden] instead. */
#tipAmountError { display: flex; }
#tipAmountError[hidden] { display: none; }

/* ------------------------------------------------------------------ layout */

.page { min-height: 100vh; min-height: 100dvh; display: flex; flex-direction: column; }
.page__main { flex: 1 1 auto; padding-block: var(--s-6) var(--s-16); }

/* Translucent header with backdrop blur, per the reference. */
.topbar {
  position: sticky; top: 0; z-index: var(--z-nav);
  padding-top: var(--safe-t);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  border-bottom: 1px solid var(--line);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
}
@supports not (backdrop-filter: blur(1px)) { .topbar { background: var(--bg); } }

.topbar__inner {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  min-height: 3.5rem;
  max-width: var(--shell); margin-inline: auto; padding-inline: var(--s-5);
}
@media (min-width: 768px) { .topbar__inner { padding-inline: var(--s-8); min-height: 4rem; } }

/* --- brand lockup: gold wings mark + wordmark ------------------------- */
.brand {
  display: inline-flex; align-items: center; gap: var(--s-3);
  color: inherit; text-decoration: none; min-height: var(--tap);
}
.brand__mark {
  block-size: 1.75rem; inline-size: auto;
  object-fit: contain; flex: none;
}
.brand__name {
  font-size: 1.0625rem; font-weight: 600; letter-spacing: -.03em;
  white-space: nowrap;
}
.brand__name em { font-style: normal; color: var(--gold); }
@media (min-width: 768px) { .brand__mark { block-size: 2rem; } }

.topbar__meta { display: none; font-size: .875rem; color: var(--text-muted); }
@media (min-width: 640px) { .topbar__meta { display: block; } }

/* ------------------------------------------------------ admin: sidebar shell */

.app { display: grid; min-height: 100vh; min-height: 100dvh; grid-template-columns: 1fr; background: var(--surface-soft); }
@media (min-width: 1024px) { .app { grid-template-columns: var(--sidebar-w) minmax(0, 1fr); } }

.sidebar {
  display: none;
  position: sticky; top: 0; align-self: start;
  height: 100vh; height: 100dvh; overflow-y: auto;
  padding: var(--s-5) var(--s-3) calc(var(--s-5) + var(--safe-b));
  background: var(--surface); border-right: 1px solid var(--line);
}
@media (min-width: 1024px) { .sidebar { display: block; } }

.sidebar__brand { padding: 0 var(--s-3) var(--s-6); }
.sidebar__heading {
  padding: var(--s-5) var(--s-3) var(--s-2);
  font-size: .6875rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-subtle);
}

/* Nav row: the WHOLE row is the target, icon and label both included. */
.sidebar__link {
  display: flex; align-items: center; gap: var(--s-3);
  min-height: var(--tap); padding: var(--s-2) var(--s-3);
  margin-bottom: 2px; border-radius: var(--r-sm);
  color: var(--text-muted); text-decoration: none;
  font-size: .9375rem; font-weight: 500; letter-spacing: -.01em;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
/* Icon colour tracks the row; it never shifts, scales or crops on hover. */
.sidebar__link .icon { color: var(--text-subtle); transition: color var(--dur-fast) var(--ease); }
.sidebar__link:hover { background: var(--surface-sunk); color: var(--text); }
.sidebar__link:hover .icon { color: var(--text); }
.sidebar__link:focus-visible { background: var(--surface-sunk); color: var(--text); }

/* Active: weight + tint + icon colour — never colour alone. */
.sidebar__link[aria-current="page"] { background: var(--accent-soft); color: var(--accent-text); font-weight: 600; }
.sidebar__link[aria-current="page"] .icon { color: var(--accent-text); }
.sidebar__link .badge-count { margin-left: auto; }

.sidebar__footer { margin-top: var(--s-6); padding-top: var(--s-4); border-top: 1px solid var(--line); }
.sidebar__user { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-2) var(--s-3); }
.sidebar__user strong { display: block; font-size: .875rem; font-weight: 600; }
.sidebar__user span { font-size: .75rem; color: var(--text-muted); }

.avatar {
  display: grid; place-items: center; flex: none;
  inline-size: 2.25rem; block-size: 2.25rem; border-radius: 50%;
  background: var(--surface-sunk); color: var(--text-muted);
  font-size: .8125rem; font-weight: 600;
}
.avatar--dark { background: var(--text); color: #fff; }

/* Mobile back-office header + drawer — nothing is ever hidden. */
.appbar {
  position: sticky; top: 0; z-index: var(--z-nav);
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--safe-t) var(--s-3) 0;
  min-height: calc(3.5rem + var(--safe-t));
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  border-bottom: 1px solid var(--line);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
}
@supports not (backdrop-filter: blur(1px)) { .appbar { background: var(--surface); } }
@media (min-width: 1024px) { .appbar { display: none; } }
.appbar__title { font-size: 1rem; font-weight: 600; letter-spacing: -.02em; }

.drawer { position: fixed; inset: 0; z-index: var(--z-modal); display: none; }
.drawer[open], .drawer.is-open { display: block; }
.drawer__scrim { position: absolute; inset: 0; background: rgb(0 0 0 / .45); animation: fade var(--dur) var(--ease); }
.drawer__panel {
  position: absolute; inset-block: 0; inset-inline-start: 0;
  width: min(19rem, 86vw); overflow-y: auto;
  padding: calc(var(--s-4) + var(--safe-t)) var(--s-3) calc(var(--s-4) + var(--safe-b));
  background: var(--surface);
  animation: slide-in var(--dur-slow) var(--ease-out);
}
@keyframes fade { from { opacity: 0 } }
@keyframes slide-in { from { transform: translateX(-100%) } }

/* --------------------------------------------- driver: bottom tab navigation */

.app-driver { min-height: 100vh; min-height: 100dvh; display: flex; flex-direction: column; background: var(--surface-soft); }
.app-driver__main { flex: 1 1 auto; padding-bottom: calc(4.5rem + var(--safe-b) + var(--s-4)); }

.tabbar {
  position: fixed; inset-inline: 0; bottom: 0; z-index: var(--z-nav);
  display: grid; grid-template-columns: repeat(4, 1fr);
  padding: var(--s-1) var(--s-2) calc(var(--s-1) + var(--safe-b));
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  border-top: 1px solid var(--line);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
}
@supports not (backdrop-filter: blur(1px)) { .tabbar { background: var(--surface); } }

.tabbar__item {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  min-height: 3.25rem; padding: var(--s-2) var(--s-1); border-radius: var(--r-sm);
  color: var(--text-subtle); text-decoration: none;
  font-size: .6875rem; font-weight: 500;
  -webkit-tap-highlight-color: transparent;
  transition: color var(--dur-fast) var(--ease);
}
.tabbar__item:active { background: var(--surface-sunk); }
.tabbar__item[aria-current="page"] { color: var(--accent-text); font-weight: 600; }

.tabbar__badge {
  position: absolute; transform: translate(0.85rem, -0.55rem);
  min-inline-size: 1.05rem; padding: 0 .3rem; border-radius: var(--r-pill);
  background: var(--danger); color: #fff;
  font-size: .625rem; font-weight: 600; line-height: 1.05rem; text-align: center;
}

/* ------------------------------------------------------------------ footer */

.footer {
  border-top: 1px solid var(--line);
  padding: var(--s-8) 0 calc(var(--s-10) + var(--safe-b));
  color: var(--text-muted); font-size: .8125rem;
}
.footer__inner {
  max-width: var(--shell); margin-inline: auto; padding-inline: var(--s-5);
  display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-8); justify-content: space-between;
}
@media (min-width: 768px) { .footer__inner { padding-inline: var(--s-8); } }

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

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  padding: var(--s-6);
}
@media (min-width: 768px) { .card { padding: var(--s-8); border-radius: var(--r-2xl); } }
.card--flush  { padding: 0; overflow: hidden; }
.card--raised { box-shadow: var(--e-3); border-color: transparent; }
.card--soft   { background: var(--surface-soft); }
.card--dark   { background: var(--surface-dark); color: var(--text-on-dark); border-color: transparent; }
.card--dark .muted, .card--dark .subtle { color: var(--text-on-dark-muted); }

.card__header {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s-4); margin-bottom: var(--s-5);
}
.card__title {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: 1.0625rem; font-weight: 600; letter-spacing: -.02em;
}
.card__title .icon { color: var(--text-subtle); }
.card__body  { display: grid; gap: var(--s-5); }
.card__footer{ margin-top: var(--s-6); padding-top: var(--s-5); border-top: 1px solid var(--line); }

.section-title {
  display: flex; align-items: center; gap: var(--s-2);
  margin: var(--s-10) 0 var(--s-4);
  font-size: .8125rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-muted);
}
.section-title:first-child { margin-top: 0; }
.section-title .icon { color: var(--text-subtle); }

.page-head {
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between;
  gap: var(--s-4); margin-bottom: var(--s-8);
}
.page-head__text { min-width: 0; }
.page-head h1 { margin-top: var(--s-2); }
.page-head p  { margin: var(--s-2) 0 0; color: var(--text-muted); }

/* The figure beside a page title -- a reservation total, a balance. Left with
   the heading on a phone, right beside it once there is room for both. */
.page-head__aside { text-align: left; }
.page-head__total { font-size: 1.75rem; font-weight: 750; line-height: 1.15; }
@media (min-width: 640px) { .page-head__aside { text-align: right; } }

/* ------------------------------------------------------------------ badges */

.badge {
  display: inline-flex !important; align-items: center; gap: var(--s-1);
  padding: .25rem .625rem; border-radius: var(--r-pill);
  background: var(--surface-sunk); color: var(--text-muted);
  font-size: .75rem; font-weight: 500; white-space: nowrap;
}
.badge .icon { inline-size: .875rem; block-size: .875rem; }

/* Colour is always paired with an icon and a word. */
.badge--success { background: var(--success-soft); color: var(--success); }
.badge--danger  { background: var(--danger-soft);  color: var(--danger); }
.badge--warning { background: var(--warning-soft); color: var(--warning); }
.badge--info    { background: var(--info-soft);    color: var(--info); }
.badge--accent  { background: var(--accent-soft);  color: var(--accent-text); }
.badge--solid   { background: var(--text); color: #fff; }

.badge-count {
  display: inline-grid; place-items: center;
  min-inline-size: 1.375rem; padding: 0 .35rem; block-size: 1.375rem;
  border-radius: var(--r-pill); background: var(--accent); color: #fff;
  font-size: .6875rem; font-weight: 600;
}

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

.stats { display: grid; gap: var(--s-3); grid-template-columns: repeat(2, minmax(0,1fr)); }
@media (min-width: 768px)  { .stats { grid-template-columns: repeat(4, minmax(0,1fr)); } }
@media (min-width: 1280px) { .stats--6 { grid-template-columns: repeat(6, minmax(0,1fr)); } }

.stat {
  display: block; padding: var(--s-5);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  color: inherit; text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
a.stat:hover { border-color: var(--line-strong); box-shadow: var(--e-2); }
.stat__label { display: flex; align-items: center; gap: var(--s-2); font-size: .8125rem; color: var(--text-muted); }
.stat__label .icon { color: var(--text-subtle); }
.stat__value { display: block; margin-top: var(--s-2); font-size: 1.75rem; font-weight: 600; letter-spacing: -.03em; }
.stat--alert .stat__value { color: var(--danger); }
.stat--good  .stat__value { color: var(--success); }

/* ----------------------------------------- responsive record rows (no scroll) */
/* One DOM. Desktop = aligned columns. Mobile = cards. Never sideways scroll. */

.records { display: grid; gap: var(--s-3); }
@media (min-width: 900px) {
  .records { gap: 0; border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; background: var(--surface); }
}

.records__head { display: none; }
@media (min-width: 900px) {
  .records__head {
    display: grid; gap: var(--s-4); padding: var(--s-3) var(--s-5);
    grid-template-columns: var(--cols);
    background: var(--surface-soft); border-bottom: 1px solid var(--line);
    font-size: .6875rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
    color: var(--text-muted);
  }
}

.record {
  display: grid; gap: var(--s-2); padding: var(--s-5);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  color: inherit; text-decoration: none;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.record:hover { border-color: var(--line-strong); }
@media (min-width: 900px) {
  .record {
    grid-template-columns: var(--cols); align-items: center; gap: var(--s-4);
    border: 0; border-bottom: 1px solid var(--line); border-radius: 0;
    padding: var(--s-4) var(--s-5);
  }
  .record:last-child { border-bottom: 0; }
  .record:hover { background: var(--surface-soft); }
}

/* Cells stack their own lines. These are spans (a row is an <a>, which may not
   contain block elements), so stacking is explicit — text parts only. */
.record__cell { display: block; min-width: 0; }
.record__cell::before {
  content: attr(data-label); display: block;
  font-size: .6875rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-subtle); margin-bottom: 2px;
}
@media (min-width: 900px) { .record__cell::before { display: none; } }
.record__cell--primary::before { display: none; }
.record__title { display: block; font-weight: 600; font-size: .9375rem; letter-spacing: -.01em; }
.record__sub   { display: block; font-size: .8125rem; color: var(--text-muted); }

/* True tabular data (rate grids) scrolls inside its own box, never the page. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface); }
.table { width: 100%; border-collapse: collapse; min-width: 40rem; }
.table th, .table td { padding: var(--s-3) var(--s-5); text-align: left; border-bottom: 1px solid var(--line); font-size: .875rem; }
.table th { background: var(--surface-soft); font-size: .6875rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted); white-space: nowrap; }
.table tbody tr:last-child td { border-bottom: 0; }
.table td .input { min-height: 2.5rem; padding: var(--s-2) var(--s-3); }

/* Reservation links on the dispatch board render at 23px -- something sets
   display:block and min-height:0 on them, and they are the primary way to open
   a booking from that screen. The hit area is grown with a pseudo-element
   rather than padding, so the tap target reaches 45px without moving a single
   pixel of the card around it. */
@media (max-width: 1023.98px) {
  .card a.link { position: relative; }
  .card a.link::after {
    content: ""; position: absolute; inset: -11px 0; z-index: 1;
  }
}
/* 2.5rem is 40px -- under the 44px minimum, and these are the inline editors a
   dispatcher taps one-handed on a phone. Tightened to the touch floor on small
   screens only; the desktop table keeps its denser 40px rows. */
@media (max-width: 1023.98px) {
  .table td .input,
  .table td .select { min-height: var(--tap); font-size: 1rem; }
}

/* ------------------------------------------------------------- empty states */

.empty-state {
  display: grid; justify-items: center; gap: var(--s-3);
  padding: var(--s-16) var(--s-6); text-align: center;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-xl);
}
.empty-state__icon {
  display: grid; place-items: center; inline-size: 3rem; block-size: 3rem;
  border-radius: 50%; background: var(--surface-sunk); color: var(--text-subtle);
}
.empty-state__title { font-size: 1.0625rem; font-weight: 600; }
.empty-state__body  { max-width: 34rem; color: var(--text-muted); }

/* ----------------------------------------------------------------- feedback */

.alert {
  display: flex; align-items: flex-start; gap: var(--s-3);
  padding: var(--s-4) var(--s-5); border-radius: var(--r-md);
  font-size: .9375rem; line-height: 1.5;
}
.alert__body { min-width: 0; }
.alert__title { font-weight: 600; margin-bottom: 2px; }
.alert--success { background: var(--success-soft); color: var(--success); }
.alert--danger  { background: var(--danger-soft);  color: var(--danger); }
.alert--warning { background: var(--warning-soft); color: var(--warning); }
.alert--info    { background: var(--info-soft);    color: var(--info); }

.toast-region {
  position: fixed; z-index: var(--z-toast);
  inset-inline: var(--s-4); bottom: calc(var(--s-4) + var(--safe-b));
  display: grid; gap: var(--s-2); justify-items: center; pointer-events: none;
}
@media (min-width: 640px) { .toast-region { inset-inline: auto var(--s-8); justify-items: end; } }
.toast {
  pointer-events: auto;
  display: flex; align-items: center; gap: var(--s-3);
  max-width: min(28rem, 92vw); padding: var(--s-4) var(--s-5);
  background: var(--text); color: #fff;
  border-radius: var(--r-md); box-shadow: var(--e-4); font-size: .875rem;
  animation: toast-in var(--dur-slow) var(--ease-out);
}
.toast--danger  { background: var(--danger); }
.toast--success { background: var(--success); }
@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } }

/* --------------------------------------------------------------- skeletons */

.skeleton {
  background: linear-gradient(90deg, var(--surface-sunk) 25%, var(--surface-soft) 37%, var(--surface-sunk) 63%);
  background-size: 400% 100%;
  border-radius: var(--r-md);
  animation: shimmer 1.4s ease-in-out infinite;
}
@keyframes shimmer { 0% { background-position: 100% 50% } 100% { background-position: 0 50% } }
.skeleton--card { block-size: 8.5rem; }
/* Measured against the rendered card, not guessed: 8.5rem left an 81px jump
   the moment the real vehicles replaced the placeholders. Now within a few
   pixels of the real thing at both layouts -- 228px stacked-row on a phone,
   353px centred in a grid cell. */
.vehicle-grid .skeleton--card { block-size: 14.25rem; }
@media (min-width: 720px) { .vehicle-grid .skeleton--card { block-size: 22rem; } }
.skeleton--line { block-size: .85rem; }

/* =========================================================== booking flow */

/* ========================================================= marketing band */
/* Built on laxtogo.com's own brand, harvested from the live site rather than
   guessed: brass #D6A955, warm black #1E1E1E -> #14120E, body #C6C3BB, and
   Playfair Display for the headline.

   The dark ground is not decoration. --gold on white measures 2.42:1 and cannot
   legally be text; this brass on their warm black is 7.67:1. Dark-and-gold is
   also exactly what laxtogo.com does, so the booking site reads as the same
   company rather than a bolt-on. */

.marquee {
  --brass:      #d6a955;
  --brass-lift: #f0cf8a;
  --ink:        #c6c3bb;

  margin: calc(var(--s-6) * -1) calc(50% - 50vw) var(--s-6);
  padding: var(--s-10) var(--s-5) var(--s-8);
  background: radial-gradient(125% 150% at 50% -25%, #262320 0%, #1e1e1e 48%, #14120e 100%);
  text-align: center;
  overflow: hidden;
}
.marquee__inner { max-inline-size: 46rem; margin-inline: auto; }

.marquee__kicker {
  display: inline-flex; align-items: center; gap: var(--s-2);
  margin-bottom: var(--s-4);
  font-size: .625rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase;
  color: var(--brass);
}
.marquee__kicker .icon { color: var(--brass); }

/* The headline. Playfair Display is the face laxtogo.com uses; the serif is the
   single biggest reason the band reads as luxury rather than as a SaaS banner.
   Georgia is a real fallback with similar proportions, so a failed font load
   degrades in style rather than snapping to a sans. */
.marquee__line {
  position: relative;
  margin: 0;
  font-family: "Playfair Display", Georgia, "Times New Roman", serif;
  font-weight: 700;
  font-size: clamp(1.65rem, 6.6vw, 3.1rem);
  line-height: 1.2;
  letter-spacing: -.01em;
  color: var(--brass);
  text-wrap: balance;              /* keeps the wrap even across breakpoints */
}

/* Reserves the exact final height. Typing grows text line by line, and without
   this the band would push the booking form down the page mid-read -- the worst
   possible layout shift, on the one element meant to pull people IN. */
.marquee__ghost { visibility: hidden; }
.marquee__typed {
  position: absolute; inset: 0;
  display: block;
}
/* The caret. Only present while typing; JS removes it at the end, so nothing
   blinks forever in the corner of someone's eye. */
.marquee__typed.is-typing::after {
  content: "";
  display: inline-block;
  inline-size: .06em; block-size: .92em;
  margin-inline-start: .06em;
  background: var(--brass-lift);
  vertical-align: -.12em;
  animation: caret 1s steps(1) infinite;
}
@keyframes caret { 50% { opacity: 0; } }

.marquee__sub {
  max-inline-size: 36rem; margin: var(--s-5) auto 0;
  font-size: 1rem; line-height: 1.65;
  color: var(--ink);
}

.marquee__proof {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--s-2) var(--s-5);
  margin: var(--s-5) 0 0; padding: 0; list-style: none;
}
.marquee__proof li {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: .8125rem; color: var(--ink);
}
.marquee__proof .icon { color: var(--brass); flex: none; }

/* A typing headline is precisely what triggers vestibular symptoms, and it is
   also just noise to anyone who has asked for calm. They get the finished line
   immediately, in full, with no caret. */
@media (prefers-reduced-motion: reduce) {
  .marquee__typed { position: static; }
  .marquee__ghost { display: none; }
  .marquee__typed.is-typing::after { display: none; animation: none; }
}

.hero { padding: var(--s-10) 0 var(--s-8); text-align: center; }
/* The band carries the headline now, so the hero beneath it only needs to
   carry the supporting line. Tightened so the booking form does not get pushed
   further down the page -- the whole point is to get people INTO it. */
.hero--tight { padding-top: 0; padding-bottom: var(--s-6); }
.hero h1 { max-width: 16ch; margin: var(--s-3) auto; }

/* "Paid in full" is the one line on this page a customer needs to read from
   across the room. Green, heavier, with the tick sitting on the same optical
   baseline as the text.

   --success is #1d7a44 -- 5.36:1 on white, so it clears AA as normal text and
   the weight is a bonus rather than a crutch. The tick is a second, non-colour
   signal, which is what keeps this readable for a colour-blind passenger. */
.hero__title--paid {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  max-width: 20ch;
  color: var(--success);
  font-weight: 700;
  letter-spacing: -.03em;
}
.hero__title--paid .icon { flex: none; stroke-width: 2.4; }
.hero p  { max-width: 46ch; margin-inline: auto; font-size: 1.0625rem; color: var(--text-muted); }
@media (min-width: 768px) { .hero { padding: var(--s-16) 0 var(--s-10); } }

/* On a 1440x900 laptop the hero pushed "See vehicles & prices" to 975px -- the
   one button that starts the whole funnel, below the fold, on the most common
   desktop size there is. The headline and the trust chips still lead; they just
   stop spending 104px of vertical space to do it. */
@media (min-width: 1024px) {
  .hero { padding: var(--s-10) 0 var(--s-6); }
  .hero__trust { margin-top: var(--s-4); }

  /* Pickup and drop-off share a row on a wide screen. They are full-width on a
     phone because an address needs the room; at 1024px+ each still gets ~375px,
     which is wider than any phone ever gives them.

     Measured on the live page at 1440x900: this plus the hero trim moves
     "See vehicles & prices" from 975px to 842px -- above the fold on a 1080p
     desktop, where it was not before. It does NOT reach the fold on a 13-inch
     laptop, and no amount of tightening will: a marketing hero and a six-field
     form cannot both fit in 768px. That needs hero-beside-form, which is a
     deliberate redesign rather than a padding change. */
  .form-grid { gap: var(--s-4); }
  /* .route-field is already on both address fields -- no new class needed. */
  .form-grid > .route-field { grid-column: auto; }
}

.hero__trust {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--s-3) var(--s-6); margin-top: var(--s-6);
}
.hero__trust-item { display: inline-flex; align-items: center; gap: var(--s-2); font-size: .875rem; color: var(--text-muted); }
.hero__trust-item .icon { color: var(--text-subtle); }

/* The booking flow reads as a focused sheet, never a full-bleed admin form. */
.booking-shell { max-width: var(--shell-form); margin-inline: auto; }
.booking-shell--wide { max-width: 62rem; }

/* Progress: four dots for four panels */
.steps {
  display: flex; align-items: center; gap: var(--s-1);
  margin: 0 auto var(--s-6); padding: 0; list-style: none;
  max-width: var(--shell-form);
}
.steps li { list-style: none; }
.step {
  display: flex; align-items: center; gap: var(--s-2);
  min-height: var(--tap); padding: var(--s-2);
  background: none; border: 0; border-radius: var(--r-sm);
  font: inherit; font-size: .8125rem; font-weight: 500; color: var(--text-subtle);
}
.step__num {
  display: grid; place-items: center; flex: none;
  inline-size: 1.5rem; block-size: 1.5rem; border-radius: 50%;
  border: 1.5px solid var(--line-strong);
  font-size: .6875rem; font-weight: 600;
}
.step__label { display: none; }
@media (min-width: 560px) { .step__label { display: inline; } }
.step[aria-current="step"] { color: var(--text); font-weight: 600; }
.step[aria-current="step"] .step__num { background: var(--accent); color: #fff; border-color: var(--accent); }
.step.is-done { color: var(--text-muted); }
.step.is-done .step__num { background: var(--success-soft); color: var(--success); border-color: transparent; }
.steps__sep { flex: 1 1 auto; block-size: 1.5px; background: var(--line); min-inline-size: .5rem; }

.panel { display: none; }
.panel.is-active { display: block; animation: panel-in var(--dur-slow) var(--ease-out); }
@keyframes panel-in { from { opacity: 0; transform: translateY(4px); } }

/* Pickup green, drop-off red — never the same glyph twice. */
.route-field { position: relative; }
.route-field__marker {
  position: absolute; inset-inline-start: var(--s-4); inset-block-start: 2.55rem;
  display: grid; place-items: center; pointer-events: none;
}
.route-field--pickup  .route-field__marker { color: var(--success); }
.route-field--dropoff .route-field__marker { color: var(--danger); }
.route-field .input { padding-inline-start: 3rem; }

.route-status {
  display: flex; align-items: center; gap: var(--s-2);
  min-height: 1.5rem; margin-top: var(--s-4);
  font-size: .875rem; color: var(--text-muted);
}
.route-status--error { color: var(--danger); }
.route-status__spinner {
  inline-size: 1rem; block-size: 1rem; border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent;
  animation: spin .7s linear infinite;
}

/* Google Places dropdown must never be clipped or sit under anything. */
.pac-container {
  z-index: var(--z-dropdown) !important;
  border-radius: var(--r-md); border: 1px solid var(--line);
  box-shadow: var(--e-3); margin-top: 4px;
  font-family: var(--font);
}
.pac-item { padding: var(--s-3) var(--s-4); font-size: .9375rem; border-top: 1px solid var(--line); cursor: pointer; }
.pac-item:first-child { border-top: 0; }
.pac-item:hover, .pac-item-selected { background: var(--surface-soft); }
.pac-item-query { font-size: .9375rem; color: var(--text); }
.pac-matched { font-weight: 600; }

/* Vehicle cards: compact, never unnecessarily tall on a phone */
.vehicle-grid { display: grid; gap: var(--s-3); }

/* Every vehicle is on screen from the start. The old phone layout was a
   one-at-a-time swipe carousel, which hid five of six choices behind a gesture
   with no visible affordance beyond a peeking sliver. Wider viewports simply
   get more columns; nothing is ever collapsed or paged. */
@media (min-width: 720px)  { .vehicle-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-4); } }
@media (min-width: 1200px) { .vehicle-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* ---------------------------------------------------------- vehicle card */
/* One row: round portrait, details, price. The portrait is a circle with the
   car sitting INSIDE it at a comfortable distance -- object-fit: contain, not
   cover. Cover crops a side-profile car into an unreadable slab, which is what
   made these look broken. */
.vehicle-card {
  position: relative;        /* anchor for the selected tick */
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: var(--s-4);
  inline-size: 100%;
  padding: var(--s-4);
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease);
}

/* In a multi-column grid a cell is roughly a third of the page, far too narrow
   for portrait + details + price side by side. The card centres and stacks
   instead, which also gives the artwork the room it deserves. */
@media (min-width: 720px) {
  .vehicle-card {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    text-align: center;
    gap: var(--s-3);
    padding: var(--s-5) var(--s-4);
    block-size: 100%;          /* equal-height cards across a row */
  }
  .vehicle-card__body    { justify-items: center; }
  .vehicle-card__specs   { justify-content: center; }
  .vehicle-card__examples {
    white-space: normal;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-clamp: 2;
  }
}

.vehicle-card:hover { border-color: var(--line-strong); box-shadow: var(--e-2); }
.vehicle-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.vehicle-card[aria-pressed="true"] {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent), var(--e-2);
}

/* Colour is never the only signal. Selection also thickens the ring above and
   plants a tick on the portrait, so the state survives greyscale, low vision
   and every form of colour blindness. aria-pressed carries it to a screen
   reader. The badge is drawn in CSS -- it costs no icon request and cannot
   arrive late enough to shift the layout. */
.vehicle-card__check {
  position: absolute;
  /* On the card, not on the portrait: the portrait is a circle with
     overflow:hidden, which would have clipped a corner badge in half. */
  inset-block-start: var(--s-3);
  inset-inline-end: var(--s-3);
  inline-size: 30px;
  block-size: 30px;
  display: none;
  place-items: center;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--surface);
}
.vehicle-card__check::after {
  content: "";
  inline-size: 10px;
  block-size: 6px;
  /* White, not a token: this sits on --accent (#0071e3) in both themes, and
     there is no text-on-accent token in the palette to point at. */
  border-inline-start: 2px solid #fff;
  border-block-end:   2px solid #fff;
  transform: rotate(-45deg) translate(1px, -1px);
}
.vehicle-card[aria-pressed="true"] .vehicle-card__check { display: grid; }
@media (min-width: 720px) { .vehicle-card__check { inline-size: 34px; block-size: 34px; } }

/* --- round portrait --- */
.vehicle-card__media {
  position: relative;
  /* Sized so the vehicle is actually identifiable. 76px rendered a stretch
     limo as an unreadable smudge, which is a poor basis for a $400 choice.
     112px is the ceiling for the phone row layout, not a preference: at 120px
     a 320px screen was left with 72px for the name, specs and price, and the
     price overflowed the card into a horizontal page scroll. */
  inline-size: 112px;
  block-size: 112px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--surface-sunk);
  overflow: hidden;
  transition: background var(--dur-fast) var(--ease);
}
@media (min-width: 720px) { .vehicle-card__media { inline-size: 144px; block-size: 144px; } }

.vehicle-card__media img {
  inline-size: 90%;
  block-size: 90%;
  object-fit: contain;      /* the whole vehicle, never cropped */
  display: block;
}
/* An <img>-loaded SVG is its own document: page CSS, including `color`, never
   reaches inside it. The silhouette colours are therefore baked into the files,
   and selection is signalled by the surrounding circle instead. */
.vehicle-card__media--photo img { inline-size: 100%; block-size: 100%; object-fit: cover; }
.vehicle-card[aria-pressed="true"] .vehicle-card__media { background: var(--accent-soft); }

/* --- details --- */
.vehicle-card__body { display: grid; gap: 2px; min-inline-size: 0; }
.vehicle-card__class {
  font-size: .6875rem; font-weight: 600; letter-spacing: .06em;
  /* --text-subtle is 3.9:1 and its own definition restricts it to "large text
     and glyphs only". At 11px this is neither, so it was failing AA at 3.62:1
     against the card. --text-muted is 5.3:1 and passes. */
  text-transform: uppercase; color: var(--text-muted);
}
.vehicle-card__name { font-size: 1.0625rem; font-weight: 600; letter-spacing: -.02em; color: var(--text); }
.vehicle-card__examples {
  font-size: .8125rem; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.vehicle-card__specs {
  display: flex; flex-wrap: wrap; gap: var(--s-1) var(--s-4);
  margin-top: var(--s-1); font-size: .8125rem; color: var(--text-muted);
}
.vehicle-card__spec { display: inline-flex; align-items: center; gap: 5px; }

/* Phone-only parts, off everywhere else. The brief capacity line would repeat
   the specs row word for word on a desktop card, and the chevron would promise
   an expand/collapse that only exists on the phone accordion. Both are switched
   back on inside the max-width:719.98px block. */
.vehicle-card__brief,
.vehicle-card__chev { display: none; }
.vehicle-card__spec .icon { color: var(--text-subtle); }

/* --- price --- */
.vehicle-card__pricing {
  grid-column: 2;
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 0 var(--s-2);
  margin-top: var(--s-2);
  /* Both required. Without them the price and its note sit on one unbreakable
     line, and a narrow phone gets a horizontally scrolling page rather than a
     second line. */
  min-inline-size: 0;
}
.vehicle-card__price-note, .vehicle-card__saving { flex: none; }
@media (min-width: 720px) {
  .vehicle-card__pricing {
    grid-column: 1;
    flex-direction: column; align-items: center; gap: 2px;
    margin-top: auto;        /* pinned to the bottom, so prices line up across a row */
    padding-top: var(--s-2);
  }
}
.vehicle-card__price { font-size: 1.375rem; font-weight: 600; letter-spacing: -.03em; color: var(--text); }
.vehicle-card__price-note { font-size: .75rem; color: var(--text-muted); }
.vehicle-card__saving {
  font-size: .75rem; font-weight: 600; color: var(--success);
  background: var(--success-soft); padding: 2px 8px; border-radius: var(--r-pill);
}

/* Pressing should feel physical, but never move neighbouring cards. */
@media (hover: hover) {
  .vehicle-card:active { transform: scale(.994); }
}

/* Checkout: sticky summary on desktop, sticky total bar on mobile */
.checkout { display: grid; gap: var(--s-5); }
@media (min-width: 1000px) { .checkout { grid-template-columns: minmax(0,1.55fr) minmax(19rem,.8fr); align-items: start; } }
.summary { display: grid; gap: var(--s-3); }
@media (min-width: 1000px) { .summary { position: sticky; top: var(--s-8); } }

.summary__lines { display: grid; gap: var(--s-2); }
.summary__line  { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-4); font-size: .9375rem; }
.summary__line span { color: var(--text-muted); }
.summary__value { font-weight: 500; }
.summary__total {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-4);
  margin-top: var(--s-2); padding-top: var(--s-4); border-top: 1px solid var(--line);
  font-size: 1.25rem; font-weight: 600; letter-spacing: -.02em;
}

/* Exactly one total and one Reserve button at every width.
   Below 1000px the sticky action bar owns both; the summary card hides its
   copies. Above 1000px the action bar hides and the summary card owns them.
   Rendering both is what gave mobile customers two competing checkouts. */
@media (max-width: 999px) {
  .summary__cta { display: none !important; }
}

/* Mobile-only sticky bar keeps the CTA one thumb away */
.action-bar {
  position: sticky; bottom: 0; z-index: var(--z-sticky);
  display: flex; align-items: center; gap: var(--s-4);
  margin: var(--s-6) calc(var(--s-5) * -1) 0;
  padding: var(--s-3) var(--s-5) calc(var(--s-3) + var(--safe-b));
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  border-top: 1px solid var(--line);
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
}
@supports not (backdrop-filter: blur(1px)) { .action-bar { background: var(--surface); } }
.action-bar__total { display: grid; }
.action-bar__total span { font-size: .6875rem; color: var(--text-muted); }
.action-bar__total strong { font-size: 1.125rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.action-bar .btn { flex: 1 1 auto; }
@media (min-width: 1000px) { .action-bar { display: none; } }

.extras-list { display: grid; }
.extra-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-4) 0; border-bottom: 1px solid var(--line);
}
.extra-row:last-child { border-bottom: 0; }
.extra-row__info strong { display: block; font-size: .9375rem; font-weight: 500; }
.extra-row__info span   { display: block; font-size: .8125rem; color: var(--text-muted); }
.extra-row .select { inline-size: auto; min-inline-size: 6rem; }

.confirmation { display: grid; justify-items: center; gap: var(--s-4); padding: var(--s-12) var(--s-5); text-align: center; }
.confirmation__icon {
  display: grid; place-items: center; inline-size: 4rem; block-size: 4rem;
  border-radius: 50%; background: var(--success-soft); color: var(--success);
}
.confirmation__code {
  padding: var(--s-4) var(--s-6); border-radius: var(--r-md);
  background: var(--surface-soft); border: 1px solid var(--line);
  font-size: 1.5rem; font-weight: 600; letter-spacing: .06em; font-variant-numeric: tabular-nums;
}

/* ============================================================ driver portal */

.driver-hero { padding: var(--s-6) 0 var(--s-3); }
.driver-hero__greeting { font-size: 1.5rem; font-weight: 600; letter-spacing: -.03em; }
.next-ride { display: grid; gap: var(--s-5); }
.next-ride__countdown { font-size: 2rem; font-weight: 600; letter-spacing: -.04em; font-variant-numeric: tabular-nums; }

.ride-card {
  display: grid; gap: var(--s-4); padding: var(--s-5);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  color: inherit; text-decoration: none;
}
.ride-card:active { opacity: .9; }
.ride-card__top { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s-3); }
.ride-card__when { display: grid; }
.ride-card__day  { font-size: .75rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: .05em; }
.ride-card__time { font-size: 1.5rem; font-weight: 600; letter-spacing: -.03em; }

/* Route with a connector: pickup and drop-off unmistakable */
.route-line { display: grid; gap: var(--s-2); }
.route-point { display: grid; grid-template-columns: 1.25rem minmax(0,1fr); gap: var(--s-3); align-items: start; }
.route-point__dot { display: grid; place-items: center; padding-top: 2px; }
.route-point--from .route-point__dot { color: var(--success); }
.route-point--to   .route-point__dot { color: var(--danger); }
.route-point__label { display: block; font-size: .6875rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--text-subtle); }
.route-point__value { display: block; font-size: .9375rem; font-weight: 500; }
.route-connector { grid-column: 1; inline-size: 1.25rem; display: grid; place-items: center; }
.route-connector::before { content: ''; inline-size: 1.5px; block-size: 1rem; background: var(--line-strong); }

/* One big action. Reachable one-handed, impossible to confuse. */
.ride-action { position: sticky; bottom: calc(4.5rem + var(--safe-b)); z-index: var(--z-sticky); margin-top: var(--s-8); }
.ride-action .btn { min-height: 3.5rem; font-size: 1.0625rem; font-weight: 600; box-shadow: var(--e-3); }

.contact-row { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: var(--s-3); }

.detail-list { display: grid; gap: var(--s-4); }
.detail-item { display: grid; grid-template-columns: 1.25rem minmax(0,1fr); gap: var(--s-3); align-items: start; }
.detail-item__icon { color: var(--text-subtle); padding-top: 2px; }
.detail-item__label { display: block; font-size: .6875rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--text-subtle); }
.detail-item__value { display: block; font-size: .9375rem; }
.earnings-total { font-size: 2.25rem; font-weight: 600; letter-spacing: -.04em; }

/* ============================================================ admin extras */

/* ====================================================== trip route + people */
/* Green pickup, orange stop, red drop-off -- matched exactly to the map markers
   so the list and the map are read as one object. Every point also carries an
   icon and a word, because colour alone is meaningless to a colour-blind
   dispatcher and the SHAPE is what someone who does not read English uses. */

.routeline { display: grid; gap: 0; }

.routeline__point {
  position: relative;
  display: grid; grid-template-columns: 30px minmax(0, 1fr);
  gap: var(--s-3);
  padding-block: var(--s-2);
}
/* The connecting spine, drawn from each dot to the next. Not on the last one. */
.routeline__point:not(:last-child)::before {
  content: "";
  position: absolute;
  inset-inline-start: 14px;
  inset-block: 32px -6px;
  inline-size: 2px;
  background: var(--line-strong);
}
.routeline__dot {
  inline-size: 30px; block-size: 30px;
  display: grid; place-items: center;
  border-radius: 50%;
  color: #fff;
  z-index: 1;                       /* sits over the spine */
}
.routeline__point--pickup  .routeline__dot { background: var(--success); }
.routeline__point--stop    .routeline__dot { background: var(--warning); }
.routeline__point--dropoff .routeline__dot { background: var(--danger); }

.routeline__body { min-inline-size: 0; display: grid; gap: 1px; padding-top: 3px; }
.routeline__label {
  font-size: .625rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
}
.routeline__point--pickup  .routeline__label { color: var(--success); }
.routeline__point--stop    .routeline__label { color: var(--warning); }
.routeline__point--dropoff .routeline__label { color: var(--danger); }
/* Addresses WRAP. Truncating the one thing a chauffeur needs to drive to would
   be the worst possible economy on this page. */
.routeline__value { font-size: .9375rem; color: var(--text); line-height: 1.45; word-break: break-word; }
.routeline__meta {
  display: inline-flex; align-items: center; gap: var(--s-1);
  margin-top: 2px; font-size: .8125rem; color: var(--text-muted);
}
.routeline__meta .icon { flex: none; }

/* ---- the map ---- */
.tripmap {
  margin-top: var(--s-4);
  block-size: 200px; inline-size: 100%;
  background: var(--surface-sunk);      /* reserved box, visible while tiles load */
  border: 1px solid var(--line); border-radius: var(--r-lg);
  overflow: hidden;
}
@media (min-width: 900px) { .tripmap { block-size: 260px; } }

.tripmap__legend {
  display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-4);
  margin: var(--s-2) 0 0; font-size: .75rem; color: var(--text-muted);
}
.tripmap__key { display: inline-flex; align-items: center; gap: var(--s-1); }
.tripmap__key i { inline-size: 9px; block-size: 9px; border-radius: 50%; flex: none; }
.tripmap__key--pickup  i { background: var(--success); }
.tripmap__key--stop    i { background: var(--warning); }
.tripmap__key--dropoff i { background: var(--danger); }

/* ---- the passenger ---- */
.person { display: flex; align-items: center; gap: var(--s-3); }
.person__avatar {
  inline-size: 42px; block-size: 42px; flex: none;
  display: grid; place-items: center;
  background: var(--accent-soft); color: var(--accent-text);
  border-radius: 50%;
}
.person__name { font-size: 1.0625rem; font-weight: 600; color: var(--text); min-inline-size: 0; word-break: break-word; }

/* A phone number and an email are the two things tapped in a hurry. Full-width
   rows at a real touch size, with the icon leading, rather than inline links. */
.contactrow {
  display: flex; align-items: center; gap: var(--s-3);
  min-block-size: var(--tap);
  padding: var(--s-2) var(--s-3);
  background: var(--surface-soft);
  border: 1px solid var(--line); border-radius: var(--r-md);
  text-decoration: none; color: var(--text);
}
.contactrow:hover { border-color: var(--line-strong); }
.contactrow:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.contactrow__icon {
  inline-size: 30px; block-size: 30px; flex: none;
  display: grid; place-items: center; border-radius: 50%; color: #fff;
}
.contactrow__icon--call { background: var(--success); }
.contactrow__icon--mail { background: var(--accent); }
.contactrow__text { font-size: .9375rem; min-inline-size: 0; overflow-wrap: anywhere; }

/* Icons in the detail rows. Muted so they guide the eye without competing with
   the value beside them. */
.kv__key { display: inline-flex; align-items: center; gap: var(--s-2); }
.kv__key .icon { flex: none; color: var(--text-subtle); }

/* ============================================================== week view */
/* A week STRIP, not a seven-column grid. A grid gives each day about 50px on a
   phone -- too narrow for a time or a name -- and degenerates into coloured
   slivers nobody can read. The strip answers "how busy is each day" at a
   glance; the list below answers "what exactly is on". */

.weeknav {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-2); margin-bottom: var(--s-3);
}
.weeknav__step {
  inline-size: var(--tap); block-size: var(--tap);
  display: grid; place-items: center;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--line-strong); border-radius: var(--r-md);
  text-decoration: none;
}
.weeknav__step:hover { border-color: var(--text-subtle); }
.weeknav__step:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.weeknav__today {
  min-height: var(--tap); display: inline-flex; align-items: center; padding: 0 var(--s-4);
  color: var(--accent-text); font-size: .9375rem; font-weight: 600; text-decoration: none;
}

.weekstrip { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--s-1); }

.weekday {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  min-block-size: 62px; padding: var(--s-2) 0;
  background: var(--surface); color: var(--text-muted);
  border: 1px solid var(--line); border-radius: var(--r-md);
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.weekday:hover { border-color: var(--line-strong); }
.weekday:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.weekday__dow { font-size: .625rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; }
.weekday__num { font-size: 1.0625rem; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
.weekday__count { font-size: .6875rem; font-weight: 600; color: var(--accent-text); }
.weekday__count--none { color: var(--line-strong); }
.weekday__dots { display: flex; gap: 2px; }
.weekday__dot {
  inline-size: 5px; block-size: 5px; border-radius: 50%;
  background: var(--warning);            /* something still owes money */
}
.weekday__dot--paid { background: var(--success); }

/* Today is marked with a ring, the SELECTED day with fill -- two different
   facts, so they get two different treatments and can both show at once. */
.weekday.is-today { border-color: var(--accent); }
.weekday.is-picked {
  background: var(--accent); border-color: var(--accent); color: #fff;
}
.weekday.is-picked .weekday__num,
.weekday.is-picked .weekday__count { color: #fff; }
.weekday.is-picked .weekday__dot { background: rgb(255 255 255 / .75); }

/* ---- the selected day's trips ---- */
.daylist { display: grid; gap: var(--s-2); }
.daytrip {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center; gap: var(--s-3);
  min-block-size: var(--tap);
  padding: var(--s-3) var(--s-4);
  background: var(--surface);
  border: 1px solid var(--line); border-left: 3px solid var(--warning);
  border-radius: var(--r-md);
  text-decoration: none; color: inherit;
}
.daytrip:hover { border-color: var(--line-strong); border-left-color: var(--warning); box-shadow: var(--e-1); }
.daytrip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Paid trips carry a green edge. The badge on the right says so in words too --
   the stripe is a scanning aid, never the only signal. */
.daytrip--paid { border-left-color: var(--success); }
.daytrip--paid:hover { border-left-color: var(--success); }

.daytrip__time {
  display: flex; flex-direction: column; align-items: center;
  min-inline-size: 3.25rem;
  font-variant-numeric: tabular-nums; line-height: 1.15;
}
.daytrip__time strong { font-size: 1rem; color: var(--text); }
.daytrip__time span { font-size: .625rem; font-weight: 600; color: var(--text-muted); }

.daytrip__body { min-inline-size: 0; display: grid; gap: 1px; }
.daytrip__name { font-size: .9375rem; font-weight: 600; color: var(--text); }
.daytrip__route,
.daytrip__meta {
  font-size: .8125rem; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.daytrip__right { display: grid; justify-items: end; gap: 2px; }
.daytrip__price { font-size: .9375rem; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }

/* On a phone the price column and the badge would squeeze the name to nothing.
   Drop to two rows so the passenger and the route keep the full width. */
@media (max-width: 519.98px) {
  .daytrip { grid-template-columns: auto minmax(0, 1fr); }
  .daytrip__right {
    grid-column: 2; justify-items: start;
    display: flex; align-items: center; gap: var(--s-2); margin-top: var(--s-1);
  }
  .daytrip__meta { display: none; }   /* confirmation code lives on the detail page */
}

/* ------------------------------------------------------------- quick views */
/* A horizontal row of one-tap saved views sitting above the filter panel.
   Scrolls sideways only if it has to -- with three chips it never does on a
   phone, so this is not a hidden-content gesture. */
.quickviews {
  display: flex; gap: var(--s-2);
  margin-bottom: var(--s-3);
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.quickviews::-webkit-scrollbar { display: none; }
.quickviews__chip {
  flex: none;
  display: inline-flex; align-items: center;
  min-height: var(--tap); padding: 0 var(--s-4);
  background: var(--surface); color: var(--text-muted);
  border: 1px solid var(--line-strong); border-radius: var(--r-pill);
  font-size: .875rem; font-weight: 500; text-decoration: none;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}
.quickviews__chip:hover { border-color: var(--text-subtle); color: var(--text); }
.quickviews__chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* aria-current carries the state to a screen reader; colour alone never does. */
.quickviews__chip.is-on {
  background: var(--accent); color: #fff; border-color: var(--accent); font-weight: 600;
}

/* ------------------------------------------------- admin filters on a phone */
/* Six stacked controls came to 501px -- 60% of a phone screen spent on filters
   before a single booking was visible, on the screen an operator opens most.
   Collapsed behind one button, the list starts at the top where it belongs.
   Desktop is untouched: the toggle is hidden and the row always shows. */
.filters__toggle { display: none; }

@media (max-width: 1023.98px) {
  .filters__toggle {
    display: inline-flex; align-items: center; justify-content: space-between;
    gap: var(--s-2); inline-size: 100%; min-height: var(--tap);
    padding: var(--s-2) var(--s-4);
    background: var(--surface); color: var(--text);
    border: 1px solid var(--line-strong); border-radius: var(--r-md);
    font: inherit; font-size: .9375rem; font-weight: 500; cursor: pointer;
  }
  .filters__toggle .icon { transition: transform var(--dur) var(--ease); }
  .filters__toggle[aria-expanded="true"] .icon { transform: rotate(180deg); }
  /* A count so a filtered list never looks like an empty one. */
  .filters__count {
    min-inline-size: 20px; padding: 1px 7px; border-radius: var(--r-pill);
    background: var(--accent); color: #fff; font-size: .75rem; font-weight: 600;
  }
  .filters__row[hidden] { display: none; }
}

.filters { display: grid; gap: var(--s-3); margin-bottom: var(--s-6); }
@media (min-width: 768px) { .filters__row { display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: end; } }
.filters .field { min-inline-size: 10rem; }
.search-field { position: relative; flex: 1 1 16rem; }
.search-field .input { padding-inline-start: 2.9rem; }
.search-field__icon { position: absolute; inset-inline-start: var(--s-4); inset-block-start: 50%; transform: translateY(-50%); color: var(--text-subtle); pointer-events: none; }

.pagination { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); margin-top: var(--s-6); }

.timeline { display: grid; gap: var(--s-5); }
.timeline__item { display: grid; grid-template-columns: 1.75rem minmax(0,1fr); gap: var(--s-3); }
.timeline__dot { display: grid; place-items: center; inline-size: 1.75rem; block-size: 1.75rem; border-radius: 50%; background: var(--surface-sunk); color: var(--text-muted); }
.timeline__meta { font-size: .75rem; color: var(--text-subtle); }
.timeline__title { font-size: .875rem; font-weight: 500; }

.kv { display: grid; gap: var(--s-4); }
.kv__row { display: grid; gap: 2px; }
@media (min-width: 480px) { .kv__row { grid-template-columns: 10rem minmax(0,1fr); gap: var(--s-4); align-items: baseline; } }
.kv__key { font-size: .8125rem; color: var(--text-muted); }
.kv__val { font-size: .9375rem; }

.price-lines { display: grid; gap: var(--s-2); }
.price-line { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-4); font-size: .9375rem; }
.price-line span { color: var(--text-muted); }
.price-line strong { font-variant-numeric: tabular-nums; font-weight: 500; }
.price-line--total { margin-top: var(--s-2); padding-top: var(--s-4); border-top: 1px solid var(--line); font-size: 1.125rem; font-weight: 600; }

.dispatch-columns { display: grid; gap: var(--s-5); }
@media (min-width: 900px)  { .dispatch-columns { grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (min-width: 1280px) { .dispatch-columns { grid-template-columns: repeat(3, minmax(0,1fr)); } }

/* Asymmetric two-column layouts -- a main body with a narrower sidebar.
   These were written as inline style="grid-template-columns:..." on four admin
   pages, and an inline style beats every selector in this file. So the
   mobile-first rule above was silently dead: a 390px phone was forced into a
   230px and a 145px column, which is what made the reservation detail read as
   one jumbled block. Expressed as classes, the breakpoints work again. */
.dispatch-columns--aside,
.dispatch-columns--split { align-items: start; }

@media (min-width: 900px) {
  .dispatch-columns--aside { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); }
  .dispatch-columns--split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
.dispatch-column__head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); margin-bottom: var(--s-3); }

.modal { position: fixed; inset: 0; z-index: var(--z-modal); display: grid; place-items: end center; }
@media (min-width: 640px) { .modal { place-items: center; } }
.modal__scrim { position: absolute; inset: 0; background: rgb(0 0 0 / .45); animation: fade var(--dur) var(--ease); }
.modal__panel {
  position: relative; width: min(32rem, 100%); max-height: 90dvh; overflow-y: auto;
  padding: var(--s-6) var(--s-6) calc(var(--s-6) + var(--safe-b));
  background: var(--surface); border-radius: var(--r-2xl) var(--r-2xl) 0 0; box-shadow: var(--e-4);
  animation: sheet-in var(--dur-slow) var(--ease-out);
}
@media (min-width: 640px) { .modal__panel { border-radius: var(--r-2xl); padding-bottom: var(--s-6); } }
@keyframes sheet-in { from { transform: translateY(14px); opacity: 0; } }

/* ------------------------------------------------------- email preview sheet */
/* Wider than the confirm-style modal because it holds a real email, and the
   email decides its own width. Near-full height on a phone: an email read
   through a 300px letterbox is not a preview of anything. */
.modal--mail .modal__panel {
  width: min(46rem, 100%);
  max-height: 92dvh;
  padding: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;                 /* the iframe owns the scrolling */
}
.mail-preview__head {
  display: flex; align-items: flex-start; gap: var(--s-3);
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.mail-preview__meta { flex: 1 1 auto; min-inline-size: 0; }
.mail-preview__subject {
  display: block; font-size: .9375rem; font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail-preview__to {
  display: block; margin-top: 2px; font-size: .8125rem; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail-preview__close {
  flex: none; inline-size: var(--tap); block-size: var(--tap);
  display: grid; place-items: center;
  background: none; border: 0; border-radius: var(--r-md);
  color: var(--text-muted); cursor: pointer;
}
.mail-preview__close:hover { background: var(--surface-sunk); color: var(--text); }
.mail-preview__close:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* The email renders in its own document -- the admin stylesheet must not reach
   inside it, or the preview would not be showing what was actually sent. */
.mail-preview__frame {
  flex: 1 1 auto; inline-size: 100%; min-block-size: 60vh;
  border: 0; background: #f5f7fa;
}
@media (min-width: 640px) { .mail-preview__frame { min-block-size: 32rem; } }

/* Integration status pill (Not configured / Testing / Active / Error) */
.conn-status { display: inline-flex; align-items: center; gap: var(--s-2); font-size: .8125rem; font-weight: 500; }
.conn-status__dot { inline-size: .5rem; block-size: .5rem; border-radius: 50%; background: var(--text-subtle); flex: none; }
.conn-status--active  .conn-status__dot { background: var(--success); }
.conn-status--error   .conn-status__dot { background: var(--danger); }
.conn-status--testing .conn-status__dot { background: var(--warning); animation: pulse 1.2s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: .35 } }

/* =============================================== reduced motion + printing */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important; scroll-behavior: auto !important;
  }
  .btn:active:not(:disabled), .vehicle-card:active, .ride-card:active { opacity: 1; }
}

@media (hover: none) {
  .record:hover, .vehicle-card:hover, .stat:hover { box-shadow: none; }
}

@media print {
  .topbar, .sidebar, .appbar, .tabbar, .action-bar, .ride-action, .footer, .btn { display: none !important; }
  body { background: #fff; color: #000; }
  .card { border: 1px solid #999; box-shadow: none; break-inside: avoid; }
}

/* An unmissable failure state. A muted line under a fold-hidden button was
   read by real users as "the button is broken", because functionally it was. */
.route-status--error {
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-sm);
  background: var(--danger-soft);
  border: 1px solid color-mix(in srgb, var(--danger) 28%, transparent);
  color: var(--danger);
  font-weight: 500;
  line-height: 1.5;
}

/* ============================================================ fleet editor */
/* Photo column beside the fields on a wide screen, stacked on a phone.
   The photo is the thing an admin is judging, so it leads on mobile. */
.vehicle-edit {
  display: grid;
  gap: var(--s-5);
  grid-template-columns: 1fr;
}
@media (min-width: 900px) {
  .vehicle-edit { grid-template-columns: minmax(260px, 340px) 1fr; align-items: start; }
}

.vehicle-shot {
  aspect-ratio: 16 / 9;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--surface-sunk);
  border: 1px solid var(--line);
}
.vehicle-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }

.thumb-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-top: var(--s-3);
}
.thumb {
  position: relative;
  margin: 0;
  width: 96px;
  border-radius: var(--r-sm);
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--surface-sunk);
}
.thumb--main { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.thumb img { width: 100%; height: 64px; object-fit: cover; display: block; }

.thumb__actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2px;
  padding: 2px;
  background: var(--surface);
  border-top: 1px solid var(--line);
}
.thumb__actions form { display: contents; }
.thumb__flag {
  font-size: 11px;
  font-weight: 600;
  color: var(--accent);
  padding-left: 4px;
  letter-spacing: .01em;
}

.upload-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  margin-top: var(--s-4);
  padding-top: var(--s-4);
  border-top: 1px solid var(--line);
}
.upload-row .field__help { flex-basis: 100%; margin: 0; }

/* File inputs are one of the few controls browsers will not let us restyle
   fully, so we style the frame and leave the button to the platform. */
.input--file {
  flex: 1 1 180px;
  min-width: 0;
  padding: 8px 10px;
  font-size: 13px;
}
.input--file::file-selector-button {
  font: inherit;
  margin-right: var(--s-2);
  padding: 5px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface-sunk);
  color: var(--text);
  cursor: pointer;
}
.input--file::file-selector-button:hover { background: var(--line); }

.card__header-meta { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }

.form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
  margin-top: var(--s-5);
}

.btn--xs {
  padding: 3px 9px;
  font-size: 11px;
  min-height: 0;
  line-height: 1.4;
}
.btn--xs .icon { width: 12px; height: 12px; }

.u-hidden { display: none; }

/* ====================================================== code + disclosure */
.code-block {
  margin: 0;
  padding: var(--s-4);
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12.5px;
  line-height: 1.65;
  color: var(--text);
  white-space: pre-wrap;
  word-break: break-word;
  overflow-wrap: anywhere;
}
/* Long SQL previews scroll inside their own box; the page never scrolls sideways. */
.code-block--scroll { max-height: 22rem; overflow: auto; }

.disclosure {
  margin-top: var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}
.disclosure > summary {
  cursor: pointer;
  padding: var(--s-3) var(--s-4);
  font-weight: 590;
  font-size: 14px;
  list-style: none;
  border-radius: var(--r-md);
  transition: background var(--dur-fast) var(--ease);
}
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure > summary::before {
  content: "";
  display: inline-block;
  width: 0; height: 0;
  margin-right: var(--s-2);
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  vertical-align: 1px;
  transition: transform var(--dur-fast) var(--ease);
}
.disclosure[open] > summary::before { transform: rotate(90deg); }
.disclosure > summary:hover { background: var(--surface-sunk); }
.disclosure > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.disclosure > *:not(summary) { margin: 0 var(--s-4) var(--s-4); }

/* ============================================================ payment page */
.hero--compact { padding-block: var(--s-10) var(--s-6); }
.hero--compact h1 { font-size: clamp(1.75rem, 4vw, 2.25rem); }

.card--success {
  border-color: color-mix(in srgb, var(--success) 30%, var(--line));
  background: var(--success-soft);
}
.card--success p { margin: 0; display: flex; align-items: center; gap: var(--s-2); }

/* Sits under the receipt rows, inside the same card, so it reads as a footnote
   to the details rather than a separate alert. */
.kv__note {
  margin: var(--s-4) 0 0;
  padding-top: var(--s-4);
  border-top: 1px solid var(--line);
  font-size: .8125rem;
  line-height: 1.6;
  color: var(--text-muted);
}
.kv__note strong { color: var(--text); font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------------- notes */
/* A quiet inline explanation. Not an .alert -- nothing has gone wrong, and
   borrowing the alert styling would make a routine sentence look like a
   problem. */
.note {
  display: flex; align-items: flex-start; gap: var(--s-2);
  margin: 0;
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  font-size: .8125rem; line-height: 1.6;
  color: var(--text-muted);
  background: var(--surface-sunk);
}
.note .icon { flex: none; margin-top: 2px; }
.note strong { color: var(--text); font-weight: 600; }
.note--info { background: var(--info-soft); color: var(--text-muted); }
.note--info .icon { color: var(--info); }

/* ------------------------------------------------------- round-trip saving */
/* The offer beside the return-trip checkbox. Green because it is money the
   customer keeps, and it carries a tag icon so the meaning does not rest on
   colour alone. --success is 5.36:1 on white, so it clears AA as body text. */
.field__help--offer {
  display: inline-flex; align-items: center; gap: var(--s-2);
  margin-top: var(--s-1);
  color: var(--success);
  font-weight: 500;
}
.field__help--offer .icon { flex: none; }

/* The same money, restated where the customer actually agrees to the price.
   Green and negative, so it reads as a deduction rather than a charge. */
.summary__line--save { color: var(--success); font-weight: 500; }
.summary__line--save[hidden] { display: none; }
.summary__line--save .summary__value { color: var(--success); }

/* Same treatment on the paid receipt, so the saving reads identically on the
   page the customer keeps as it did on the page where they agreed to it. */
.kv__row--save .kv__key,
.kv__row--save .kv__val { color: var(--success); font-weight: 500; }

/* --------------------------------------------------------------- route map */
/* Height is reserved before a single tile loads. A map that appears late and
   pushes the vehicle list down the page is the textbook layout shift, and it
   happens exactly when someone is mid-scroll choosing a car. */
.route-map {
  margin-bottom: var(--s-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.route-map[hidden] { display: none; }

.route-map__canvas {
  block-size: 190px;
  inline-size: 100%;
  background: var(--surface-sunk);   /* the reserved box, visible while tiles load */
}
@media (min-width: 720px)  { .route-map__canvas { block-size: 240px; } }
@media (min-width: 1200px) { .route-map__canvas { block-size: 280px; } }

.route-map__facts {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: var(--s-1) var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-top: 1px solid var(--line);
}
.route-map__primary {
  font-size: .9375rem; font-weight: 600; color: var(--text);
  font-variant-numeric: tabular-nums;
}
.route-map__note { font-size: .8125rem; color: var(--text-muted); }

/* ============================================ vehicle list, phone (accordion)
   Six cards at 195px each is 1,230px of list -- one and a half screens, with
   only four visible at a time. Comparing a $75 sedan against a $320 van meant
   scrolling back and forth and holding the numbers in your head.

   Collapsed, every vehicle is a scannable 80px row: portrait, name, capacity,
   price. All six fit on one screen. Selecting one expands it in place to the
   full card; tapping it again collapses it WITHOUT losing the selection, so
   the choice can never be lost by tidying the list up.

   Desktop is untouched -- the 2/3-column grid already shows everything. */
@media (max-width: 719.98px) {

  .vehicle-card {
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--s-3);
    padding: var(--s-3) var(--s-4);
  }

  .vehicle-card__media { inline-size: 56px; block-size: 56px; }
  .vehicle-card__media img { inline-size: 86%; block-size: 86%; }

  /* Hidden while collapsed, restored below when expanded. */
  .vehicle-card__examples,
  .vehicle-card__specs,
  .vehicle-card__price-note,
  .vehicle-card__saving { display: none; }

  .vehicle-card__class { font-size: .625rem; }
  .vehicle-card__name  { font-size: 1rem; }

  /* Capacity moves into the collapsed row -- it is the one fact besides price
     that decides whether a vehicle is even a candidate. */
  .vehicle-card__brief {
    display: block;
    margin-top: 1px;
    font-size: .8125rem;
    color: var(--text-muted);
  }

  .vehicle-card__pricing {
    grid-column: 3; grid-row: 1;
    flex-direction: column; align-items: center;
    gap: 2px; margin: 0;
  }
  .vehicle-card__price { font-size: 1.125rem; }

  /* The affordance. Rotates when the row opens, so "tap again to close" is
     visible rather than something you have to guess. */
  .vehicle-card__chev {
    display: grid; place-items: center;
    inline-size: 20px; block-size: 20px;
    color: var(--text-subtle);
    transition: transform var(--dur) var(--ease);
  }

  /* ---- expanded ---- */
  .vehicle-card[data-expanded="true"] {
    grid-template-columns: auto minmax(0, 1fr);
    align-items: start;
    gap: var(--s-4);
    padding: var(--s-4);
  }
  .vehicle-card[data-expanded="true"] .vehicle-card__media { inline-size: 112px; block-size: 112px; }
  .vehicle-card[data-expanded="true"] .vehicle-card__media img { inline-size: 90%; block-size: 90%; }
  .vehicle-card[data-expanded="true"] .vehicle-card__examples { display: block; }
  .vehicle-card[data-expanded="true"] .vehicle-card__specs { display: flex; }
  .vehicle-card[data-expanded="true"] .vehicle-card__price-note { display: block; }
  .vehicle-card[data-expanded="true"] .vehicle-card__saving { display: inline-block; }
  .vehicle-card[data-expanded="true"] .vehicle-card__brief { display: none; }
  .vehicle-card[data-expanded="true"] .vehicle-card__pricing {
    grid-column: 2; grid-row: auto;
    flex-direction: row; align-items: baseline; flex-wrap: wrap;
    gap: 0 var(--s-2); margin-top: var(--s-2);
  }
  .vehicle-card[data-expanded="true"] .vehicle-card__price { font-size: 1.375rem; }
  /* Rotated to point up, and pushed to the end of the now-horizontal price row
     rather than sitting under the number. */
  .vehicle-card[data-expanded="true"] .vehicle-card__chev {
    transform: rotate(180deg);
    margin-inline-start: auto;
  }
  /* The tick would collide with the chevron in the collapsed row. */
  .vehicle-card[data-expanded="true"] .vehicle-card__check { inset-block-start: var(--s-2); inset-inline-end: var(--s-2); }
  .vehicle-card:not([data-expanded="true"]) .vehicle-card__check { display: none; }
}

/* A row that changes height must not do it instantly, and must not do it at all
   for anyone who asked for calm. */
@media (max-width: 719.98px) and (prefers-reduced-motion: no-preference) {
  .vehicle-card__media,
  .vehicle-card__media img { transition: inline-size var(--dur) var(--ease), block-size var(--dur) var(--ease); }
}
.card--success .icon { color: var(--success); flex: none; }

.kv__row--total {
  border-top: 1px solid var(--line-strong);
  margin-top: var(--s-2);
  padding-top: var(--s-3);
  font-size: 1.0625rem;
}

/* Stripe renders its own iframe here; we only reserve the space so the page
   does not jump when the form finishes loading. */
.pay-element {
  min-height: 220px;
  margin-block: var(--s-4) var(--s-5);
}

.pay-error {
  margin: 0 0 var(--s-4);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-sm);
  background: var(--danger-soft);
  color: var(--danger);
  font-size: .875rem;
  line-height: 1.5;
}
.pay-error[hidden] { display: none; }

.pay-note {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  margin: var(--s-4) 0 0;
  font-size: .8125rem;
  color: var(--text-muted);
}
.pay-note .icon { flex: none; }

/* The pay button carries the amount, so it must stay legible while disabled. */
#payButton[aria-busy="true"] { opacity: .72; cursor: progress; }


/* ================================================= booking flow polish */

/* Wider viewports get a little more air between the step rail and the sheet,
   and the sheet itself sits higher so the first field is above the fold on a
   laptop without scrolling. */
@media (min-width: 1024px) {
  .hero { padding-block: var(--s-12) var(--s-8); }
  .booking-shell { max-width: 52rem; }
  .panel { padding: var(--s-8); }
}

/* The examples line was truncating on every card at the old width. Give it two
   lines on desktop instead of an ellipsis -- "GMC Yukon Denali XL, Lincoln
   Navigator" is the reason a customer picks this class. */
@media (min-width: 720px) {
  .vehicle-card__examples {
    white-space: normal;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-clamp: 2;
  }
}

/* Selecting a vehicle jumps to the next step. Without a moment of confirmation
   the jump reads as "the page moved on its own", so the chosen card holds its
   selected state briefly before the step changes. */
.vehicle-card[aria-pressed="true"] .vehicle-card__price { color: var(--accent-text); }

/* Step rail: a soft halo makes the current step obvious at a glance without
   adding another colour. The real classes are .step / .step__num. */
.step[aria-current="step"] .step__num { box-shadow: 0 0 0 4px var(--accent-soft); }

/* Payment page: match the booking sheet so the hand-off does not feel like a
   different product. */
.pay-element { min-height: 240px; }
@media (min-width: 720px) {
  .shell--narrow { max-width: 40rem; }
}

/* ===================================================== hidden attribute fix */
/* Spec 5.1. `display: flex` / `grid` on a component beats the `hidden`
   attribute, so an empty alert still occupied space and rendered as a blank
   red panel. The attribute must win everywhere; state logic is fixed
   separately in booking.js rather than relying on this alone. */
[hidden] { display: none !important; }

/* ==================================================== footer credit + spark */
/* A quiet endorsement line, with one deliberately bright element: the
   limoform.com link. Everything around it stays muted so the gradient reads as
   a signature rather than decoration competing with the booking form. */
.footer__credit {
  max-width: var(--shell);
  margin: var(--s-5) auto 0;
  padding: var(--s-5) var(--s-5) 0;
  border-top: 1px solid var(--line);
  text-align: center;
}

.footer__blurb {
  margin: 0 auto var(--s-2);
  max-width: 46ch;              /* keeps the sentence readable, not edge-to-edge */
  font-size: .8125rem;
  line-height: 1.6;
  color: var(--text-muted);
}

.footer__approval {
  margin: 0;
  font-size: .8125rem;
  line-height: 1.7;
  color: var(--text-subtle);
}
.footer__pitch { color: var(--text-subtle); }

/* --- the gradient link ---------------------------------------------------- */
/* background-clip: text is what tints the glyphs. It needs a real `color` as
   the fallback: where clipping is unsupported the text would otherwise inherit
   `transparent` and vanish entirely. */
.spark-link {
  position: relative;
  display: inline-block;
  font-weight: 700;
  letter-spacing: -.01em;
  text-decoration: none;
  color: #7c3aed;                          /* fallback — never transparent */
  white-space: nowrap;
}

.spark-link__text {
  background-image: linear-gradient(
    100deg,
    #2f6fed 0%,
    #6d4aef 28%,
    #a13bd8 52%,
    #cf3f9e 74%,
    #e8524f 100%
  );
  /* 100% at rest so the FULL blue -> purple -> red spectrum spans the word,
     matching the reference. A wider size would show only the blue end. */
  background-size: 100% 100%;
  background-position: 0% 50%;
  -webkit-background-clip: text;
  background-clip: text;
  transition: background-size 500ms var(--ease), background-position 500ms var(--ease);
}

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .spark-link__text { color: transparent; -webkit-text-fill-color: transparent; }
}

/* The sweep is the whole "spark": the gradient slides once on hover rather than
   looping forever. A permanent animation in a footer is noise, and it would
   keep repainting for the entire session. */
/* The spark: on hover the gradient widens and slides, so the colours travel
   through the letterforms once. It settles back on leave -- no infinite loop
   repainting a footer for the whole session. */
.spark-link:hover .spark-link__text,
.spark-link:focus-visible .spark-link__text {
  background-size: 200% 100%;
  background-position: 100% 50%;
}

/* Colour alone must not be the only signal that this is a link (WCAG 1.4.1),
   so an underline grows from the gradient itself. */
.spark-link::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -2px;
  height: 1.5px;
  border-radius: 2px;
  background: linear-gradient(100deg, #2f6fed, #a13bd8, #e8524f);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 260ms var(--ease);
}
.spark-link:hover::after,
.spark-link:focus-visible::after { transform: scaleX(1); }

.spark-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 3px; }

@media (prefers-reduced-motion: reduce) {
  .spark-link__text { transition: none; }
  .spark-link::after { transition: none; transform: scaleX(1); }
}

/* Forced-colours mode strips gradients entirely, so fall back to a plain
   underlined link rather than an invisible one. */
@media (forced-colors: active) {
  .spark-link__text { color: LinkText; -webkit-text-fill-color: LinkText; background: none; }
  .spark-link { text-decoration: underline; }
}

@media (max-width: 599px) {
  .footer__credit { padding-inline: var(--s-4); }
  .footer__pitch { display: block; }   /* its own line so the link stays prominent */
}

/* ======================================================= footer reviews */
.footer__reviews { text-align: center; margin-top: var(--s-5); }

.reviews {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  text-decoration: none;
  color: var(--text);
  font-size: .8125rem;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
a.reviews:hover { border-color: var(--line-strong); box-shadow: var(--e-1); }
a.reviews:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.reviews__score { font-weight: 700; letter-spacing: -.01em; }
.reviews__count { color: var(--text-muted); }

/* Two stacked star rows: grey underneath, gold on top clipped to the exact
   percentage. A 4.8 therefore looks like 4.8 instead of rounding up to five,
   which would overstate the rating. */
.stars { position: relative; display: inline-block; line-height: 0; flex: none; }
.stars__base, .stars__row { display: inline-flex; gap: 1px; }
.stars__base .icon { color: var(--line-strong); fill: var(--line-strong); }
.stars__row  .icon { color: #f5a623; fill: #f5a623; }
.stars__fill {
  position: absolute; inset: 0;
  overflow: hidden;
  white-space: nowrap;
  pointer-events: none;
}

/* --- footer brand mark --- */
.footer__brand { margin-bottom: var(--s-3); }
.footer__brand img {
  height: 40px;
  width: auto;
  opacity: .85;               /* sits behind the message, not shouting over it */
}

@media (forced-colors: active) {
  .stars__row .icon { fill: Highlight; color: Highlight; }
}

/* ================================================= vehicle selection bar
   What was here: a max-width:720px block that turned the vehicle list into a
   horizontal scroll-snap carousel with arrows, dots and a sticky bar. It is
   gone. One card at a time meant the other five vehicles were discoverable
   only by swiping, and a horizontal gesture over the main content fights the
   vertical page scroll. Every vehicle is now laid out at once and the whole
   carousel apparatus -- track, arrows, dots -- has no reason to exist.

   The confirmation bar survives, and now applies at EVERY width. Tapping a
   card never advances a step by itself on any device; it fills this bar, and
   Continue is the only thing that moves forward. Desktop used to teleport you
   to passenger details on a single click, so one stray click on the wrong card
   both chose a vehicle and left the step -- with no confirmation of either. */

.select-bar {
  position: sticky;
  inset-block-end: 0;
  z-index: 40;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  row-gap: var(--s-2);
  column-gap: var(--s-3);
  margin-top: var(--s-4);
  padding: var(--s-3) var(--s-4);
  /* Keeps the button clear of the home indicator on a notched iPhone.
     --safe-b is the project's existing env(safe-area-inset-bottom) token;
     using it rather than a second raw env() keeps one definition. */
  padding-block-end: calc(var(--s-3) + var(--safe-b));
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--e-2);
}
.select-bar[hidden] { display: none; }

/* flex-basis 0 with min-inline-size 0 is what actually lets the name shrink
   and ellipsize instead of forcing the bar wider than the screen. */
.select-bar__text { flex: 1 1 0; display: grid; min-inline-size: 0; }
.select-bar__label {
  font-size: .625rem; font-weight: 600; letter-spacing: .08em;
  /* 10px is emphatically not "large text" -- see the note on
     .vehicle-card__class. Same fix. */
  text-transform: uppercase; color: var(--text-muted);
}
.select-bar__name {
  font-size: .9375rem; font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.select-bar__price {
  flex: none;
  font-size: 1.0625rem; font-weight: 600; letter-spacing: -.02em; color: var(--text);
}

/* Two rows below 720px on purpose. Squeezing name, price and button onto one
   line left 69px for the name on a 320px screen -- "Sprinter Limo" became
   "Sprinte...". Wrapping gives the name the full width and turns Continue into
   a full-width target, which is the better small-screen CTA regardless. */
.select-bar .btn { flex: 1 0 100%; min-block-size: 44px; }

@media (min-width: 720px) {
  .select-bar .btn { flex: 0 0 auto; min-inline-size: 160px; }
}

/* =============================================== international phone field
   Spec 11/12. A country button welded to the number input, and a searchable
   country list that only exists once someone opens it.

   The two controls share one border so they read as a single field. That is
   why the button and the input each drop the edge they meet on, and why the
   focus ring is drawn on the wrapper rather than on either half -- a ring
   around only the right-hand box would look like the field had split in two. */

.phone-input {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  transition: border-color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease);
}
.phone-input:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.phone-input__country {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  flex: none;
  min-block-size: 48px;          /* comfortably over the 44px minimum */
  padding-inline: var(--s-3);
  background: var(--surface-soft);
  border: 0;
  border-inline-end: 1px solid var(--line);
  border-start-start-radius: var(--r-md);
  border-end-start-radius: var(--r-md);
  color: var(--text);
  font: inherit;
  cursor: pointer;
}
.phone-input__country:hover { background: var(--surface-sunk); }
.phone-input__country:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Flag glyphs sit slightly high in most emoji fonts, and a platform with no
   flag glyph shows the two letters instead -- which is still legible, and is
   why there is no image sprite here to fail under CSP. */
.phone-input__flag { font-size: 1.125rem; line-height: 1; }
.phone-input__dial { font-variant-numeric: tabular-nums; font-weight: 600; font-size: .9375rem; }
.phone-input__caret { display: grid; place-items: center; color: var(--text-subtle); }

/* The inner input owns no border or ring of its own; the wrapper draws both. */
.phone-input__number {
  flex: 1 1 auto;
  min-inline-size: 0;
  border: 0;
  border-radius: 0;
  border-start-end-radius: var(--r-md);
  border-end-end-radius: var(--r-md);
  background: transparent;
}
.phone-input__number:focus { outline: none; box-shadow: none; }

/* ---- the country list ---- */
.phone-picker {
  position: relative;
  z-index: 30;
  margin-top: var(--s-2);
  padding: var(--s-2);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--e-3);
}
.phone-picker__search { margin-bottom: var(--s-2); }

.phone-picker__list {
  max-block-size: 15rem;
  overflow-y: auto;
  overscroll-behavior: contain;   /* stops the page scrolling once the list ends */
  margin: 0;
  padding: 0;
  list-style: none;
}
.phone-picker__list li { list-style: none; }

.phone-picker__option {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  inline-size: 100%;
  min-block-size: 44px;
  padding: var(--s-2) var(--s-3);
  background: none;
  border: 0;
  border-radius: var(--r-sm);
  text-align: left;
  font: inherit;
  color: var(--text);
  cursor: pointer;
}
.phone-picker__option:hover,
.phone-picker__option.is-active { background: var(--surface-soft); }
.phone-picker__option[aria-selected="true"] { background: var(--accent-soft); color: var(--accent-text); }
.phone-picker__option:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.phone-picker__flag { font-size: 1.125rem; line-height: 1; flex: none; }
.phone-picker__name {
  flex: 1 1 auto; min-inline-size: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: .9375rem;
}
.phone-picker__dial {
  flex: none;
  font-variant-numeric: tabular-nums;
  font-size: .875rem;
  color: var(--text-muted);
}
.phone-picker__empty { margin: var(--s-3); font-size: .875rem; color: var(--text-muted); }


/* ============================== classes the markup used but nobody defined
   Found by cross-referencing every class="" in the templates against the
   stylesheet. Each of these has been in the markup for releases, doing
   nothing: the element rendered with no styling at all and the layout it was
   meant to create simply never happened. */

/* The secondary action next to a primary one. The design system had --quiet,
   --ghost, --dark and --danger but no --secondary, while three admin screens
   asked for --secondary -- so "Upload", "Test the fallback" and "Save" all
   rendered as plain default buttons indistinguishable from a link. */
.btn--secondary { --btn-bg: var(--surface-soft); --btn-bd: var(--line-strong); }
.btn--secondary:hover:not(:disabled) { background: var(--surface-sunk); border-color: var(--text-subtle); }

/* Actions parked at the end of a page header. Without this the "Add a class"
   button wrapped underneath the title instead of sitting beside it. */
.page-head__actions {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  margin-inline-start: auto;
}

/* .vehicle-edit is a two-column grid at 720px and up, but its two children
   were unnamed to the stylesheet, so neither could hold its own column
   contents together or stop the form from stretching. */
.vehicle-edit__media { display: grid; gap: var(--s-3); align-content: start; min-inline-size: 0; }
.vehicle-edit__form  { display: grid; gap: var(--s-4); align-content: start; min-inline-size: 0; }

/* ============================ passenger details: layout corrections
   Measured on the live desktop site at 1440px before this block existed:

     .field is display:grid with align-content:normal, which is stretch. The
     name field sat beside the phone field, the phone field is taller (it
     carries a picker, a hidden input and help text), so the grid row became
     160px and the NAME INPUT STRETCHED TO 91px while every other input on the
     page was 52px. Computed gridTemplateRows read "60.2656px 91.2656px".

     And the form grid was two 179px columns, so inside the phone field the
     country button took 89px and left the number input 87.7px. A phone number
     box narrower than the button next to it is what "stretched, crooked,
     detached" describes.

   Both are fixed here, at the cause rather than with a height override. */

/* Rows keep their natural height. A tall neighbour can no longer inflate an
   input in the column beside it. */
.field { align-content: start; }

/* The inputs themselves never absorb leftover row space either. */
.field > .input,
.field > .select,
.field > .phone-input { align-self: start; }

/* --- the phone row ---------------------------------------------------- */
/* One line whenever there is honestly room for one, wrapping only when the
   number would be squeezed below something usable. flex-wrap plus a
   min-inline-size on the number is what decides that -- not a breakpoint
   guess, so it stays correct at any width and after any rotation. */
/* The phone field owns its own stacking context for the picker, which is
   absolutely the last thing that should be trapped behind a later field. */
.field--phone { position: relative; z-index: 2; }

.phone-input { flex-wrap: wrap; }

.phone-input__country { flex: 0 0 auto; }

/* 10rem, measured not guessed: the country button is 89px, so a 160px basis
   keeps both halves on one line from a 288px card (320px phone) all the way
   up. Above that the number simply grows. It wraps only when the container is
   genuinely too narrow for a usable number field -- which is the rule asked
   for, expressed as a constraint rather than a breakpoint. */
.phone-input__number {
  flex: 1 1 10rem;
  min-inline-size: 0;
}

/* Once wrapped, both halves take the full row and keep square inner corners. */
@supports (selector(:has(*))) {
  .phone-input:has(.phone-input__number:only-child) { flex-wrap: nowrap; }
}

/* --- the card itself --------------------------------------------------- */
/* A form column that keeps growing on a 27-inch monitor reads as unfinished.
   Cap the measure; the summary beside it keeps its own width. */
.checkout > .card { max-inline-size: 46rem; }

/* --- touch sizing ------------------------------------------------------ */
/* Applied by pointer capability, not by viewport width and never by sniffing
   a user-agent string: a touch laptop gets the larger targets, a narrow
   desktop window does not. */
@media (pointer: coarse) {
  .field .input,
  .field .select,
  .phone-input__country,
  .phone-input__number,
  .checkout .btn,
  .action-bar .btn { min-block-size: 52px; }

  .phone-input__country { padding-inline: var(--s-4); }
}

/* Below 640px the form grid is already one column, so the phone row simply
   fills it. Keep the country button from eating the line on a 320px screen. */
@media (max-width: 400px) {
  .phone-input__number { flex: 1 1 8rem; }
  .phone-input__country { padding-inline: var(--s-2); gap: 2px; }
}

/* An on-screen keyboard shrinks the visual viewport, not the layout viewport.
   svh tracks the smaller of the two, so a sticky bar cannot end up underneath
   the keyboard on iOS. Progressive: browsers without svh keep the old value. */
@supports (height: 100svh) {
  .action-bar, .select-bar { max-block-size: 100svh; }
}

/* Compact airport pickup switch, scoped to the booking form. */
.booking-shell .airport-card {
  min-width: 0; margin: 0; padding: var(--s-5);
  border: 1px solid var(--line-strong); border-radius: var(--r-lg);
  background: var(--surface-soft);
}
.airport-toggle { position: relative; display: flex; align-items: center; gap: var(--s-4); min-height: 48px; cursor: pointer; touch-action: manipulation; }
.airport-toggle__copy { flex: 1; min-width: 0; }
.airport-toggle__title { display: flex; align-items: center; gap: var(--s-2); font-size: 1rem; font-weight: 600; line-height: 1.4; }
.airport-toggle__title .icon { flex: 0 0 auto; }
.airport-toggle__help { display: block; margin-top: var(--s-1); font-size: .875rem; line-height: 1.5; color: var(--text-muted); }
.airport-toggle__input { position: absolute; right: 0; top: 50%; transform: translateY(-50%); width: 52px; height: 48px; opacity: 0; margin: 0; cursor: pointer; }
.airport-toggle__track { flex: 0 0 52px; height: 32px; padding: 4px; border-radius: var(--r-pill); background: var(--line-strong); pointer-events: none; }
.airport-toggle__track::before { content: ''; display: block; width: 24px; height: 24px; border-radius: 50%; background: var(--surface); box-shadow: var(--e-2); }
.airport-toggle__input:checked + .airport-toggle__track { background: var(--accent); }
.airport-toggle__input:checked + .airport-toggle__track::before { transform: translateX(20px); }
.airport-toggle__input:focus-visible + .airport-toggle__track { outline: 3px solid var(--accent-text); outline-offset: 4px; }
.airport-flight { margin-top: var(--s-4); }
.airport-flight[hidden] { display: none; }
#flightNumber { text-transform: uppercase; }
@media (max-width: 639px) {
  .booking-shell .airport-card { padding: var(--s-4); }
  .booking-shell .input, .booking-shell .textarea, .booking-shell .select { min-width: 0; font-size: 16px; }
  .booking-shell .segmented__item { min-height: 48px; padding-inline: var(--s-2); font-size: .9375rem; }
  .booking-shell .field__optional { color: var(--text-muted); }
}

/* ==========================================================================
   2026-09 booking experience redesign
   Builds on the tokens above. One primary colour, light borders, restrained
   motion. Everything here is additive: earlier rules still apply.
   ========================================================================== */

/* ---- the marketing band belongs to the first screen only ---------------
   Once someone is choosing a car or typing their name they are in checkout;
   a headline that types itself and a paragraph of sales copy above every step
   is noise. booking.js sets body.is-booking from step 2 onward. */
body.is-booking .marquee,
body.is-booking .hero--tight { display: none; }
body.is-booking .page__main { padding-top: var(--s-5); }

/* ---- panels ------------------------------------------------------------- */
.panel__foot {
  display: flex; align-items: center; justify-content: center; gap: var(--s-2);
  margin: var(--s-4) 0 0; font-size: .8125rem; color: var(--text-muted);
}
.panel__foot .icon { color: var(--text-subtle); }
.page-head--step { margin-bottom: var(--s-5); }
.page-head--step h2 { margin-top: 0; }
.section-title__opt { font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--text-subtle); }

/* ---- verified address line ---------------------------------------------
   Appears under the input once Google has verified the place. Green tick +
   the short label; the input above keeps the full text the server geocodes. */
.route-field__verified {
  display: flex; align-items: flex-start; gap: var(--s-2);
  margin: 0; font-size: .8125rem; font-weight: 500; color: var(--success);
  animation: verified-in var(--dur) var(--ease-out);
}
.route-field__verified .icon { flex: none; margin-top: 2px; }
.route-field__verified[hidden] { display: none; }
@keyframes verified-in { from { opacity: 0; transform: translateY(-2px); } }
.route-field .input.is-verified { border-color: color-mix(in srgb, var(--success) 45%, var(--line-strong)); }

/* ---- card-style checkbox (return trip) ---------------------------------- */
.check--card {
  align-items: flex-start; gap: var(--s-3);
  padding: var(--s-4); border: 1px solid var(--line-strong); border-radius: var(--r-md);
  background: var(--surface);
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.check--card:hover { border-color: var(--text-subtle); }
.check--card:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
.check--card input[type="checkbox"] { margin-top: 2px; }
.check__body { display: grid; gap: 2px; min-width: 0; }
.check__title { font-weight: 500; }
.check__help { font-size: .8125rem; }

/* ---- trip strip (step 3) ------------------------------------------------ */
.trip-strip {
  display: grid; gap: var(--s-2);
  margin-bottom: var(--s-5); padding: var(--s-4) var(--s-5);
  background: var(--surface-soft); border: 1px solid var(--line); border-radius: var(--r-lg);
}
.trip-strip[hidden] { display: none; }
.trip-strip__route { display: grid; gap: var(--s-1); }
.trip-strip__point {
  display: flex; align-items: flex-start; gap: var(--s-2);
  font-size: .9375rem; font-weight: 500; line-height: 1.4; min-width: 0;
}
.trip-strip__point .icon { flex: none; margin-top: 3px; }
.trip-strip__point--from .icon { color: var(--success); }
.trip-strip__point--to   .icon { color: var(--danger); }
.trip-strip__point[hidden] { display: none; }
.trip-strip__meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2);
  font-size: .8125rem; color: var(--text-muted);
}
.trip-strip__dot { color: var(--line-strong); }
.trip-strip__edit {
  margin-inline-start: auto; display: inline-flex; align-items: center; gap: 4px;
  min-height: 32px; padding: 0 var(--s-2); background: none; border: 0;
  font: inherit; font-size: .8125rem; font-weight: 600; color: var(--accent-text); cursor: pointer;
  border-radius: var(--r-xs);
}
.trip-strip__edit:hover { text-decoration: underline; }

/* ---- flight card -------------------------------------------------------- */
.flight-reveal { margin: var(--s-5) 0 0; }
.flight-reveal[hidden] { display: none; }
.flight-card {
  margin-top: var(--s-6); padding: var(--s-5);
  border: 1px solid var(--line-strong); border-radius: var(--r-lg);
  background: var(--surface-soft);
  animation: panel-in var(--dur-slow) var(--ease-out);
}
.flight-card[hidden] { display: none; }
.flight-card__head { display: flex; gap: var(--s-3); align-items: flex-start; margin-bottom: var(--s-4); }
.flight-card__icon {
  flex: none; display: grid; place-items: center;
  inline-size: 36px; block-size: 36px; border-radius: 50%;
  background: var(--accent-soft); color: var(--accent-text);
}
.flight-card__title { font-size: 1.0625rem; font-weight: 600; letter-spacing: -.02em; }
.flight-card__title--sm { font-size: .9375rem; margin: 0 0 var(--s-3); }
.flight-card__sub { margin: 2px 0 0; font-size: .8125rem; color: var(--text-muted); line-height: 1.5; }
.flight-card__pick { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-bottom: var(--s-4); }
.flight-card__pick[hidden] { display: none; }
.flight-card__second { margin-top: var(--s-5); padding-top: var(--s-4); border-top: 1px solid var(--line); }
.flight-card__second[hidden] { display: none; }
.flight-card .form-grid { gap: var(--s-4); }
#flightNumber, #departingFlight { text-transform: uppercase; }

/* Radio rendered as a chip: the input stays real for keyboard and screen readers. */
.chip--radio { position: relative; display: inline-flex; align-items: center; padding: 0; }
.chip--radio input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.chip--radio span { display: inline-flex; align-items: center; min-height: var(--tap); padding: var(--s-2) var(--s-4); }
.chip--radio:has(input:checked) { background: var(--accent); color: #fff; border-color: var(--accent); }
.chip--radio:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---- add-ons: a switch per service, quantity only once it is on -------- */
.extra-row { align-items: center; gap: var(--s-3); }
.extra-row__main {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: var(--s-3);
  min-height: var(--tap); cursor: pointer;
}
.extra-row__toggle {
  -webkit-appearance: none; appearance: none;
  flex: none; inline-size: 44px; block-size: 26px; margin: 0;
  border-radius: var(--r-pill); background: var(--line-strong);
  position: relative; cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
.extra-row__toggle::after {
  content: ""; position: absolute; top: 3px; left: 3px;
  inline-size: 20px; block-size: 20px; border-radius: 50%;
  background: #fff; box-shadow: var(--e-2);
  transition: transform var(--dur-fast) var(--ease);
}
.extra-row__toggle:checked { background: var(--accent); }
.extra-row__toggle:checked::after { transform: translateX(18px); }
.extra-row__toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.extra-row__info { display: grid; gap: 1px; min-width: 0; }
.extra-row__qty { flex: none; }
.extra-row__qty[hidden] { display: none; }
.extra-row__qty .select { min-height: var(--tap); min-inline-size: 5.5rem; padding-block: var(--s-2); }
.extra-row:has(.extra-row__toggle:checked) .extra-row__info strong { color: var(--accent-text); }

/* ---- summary ------------------------------------------------------------ */
.summary__head { display: grid; gap: 2px; margin: var(--s-3) 0 var(--s-4); }
.summary__head strong { font-size: 1.0625rem; font-weight: 600; letter-spacing: -.02em; }
.summary__line[hidden] { display: none; }
.summary__note { display: flex; align-items: flex-start; gap: var(--s-2); margin-top: var(--s-4); }
.summary__note .icon { flex: none; margin-top: 2px; color: var(--text-subtle); }

/* ---- confirmation / reservation page (step 4 and pay.php) --------------- */
.receipt { display: grid; gap: 0; }
.receipt__status {
  display: grid; justify-items: center; gap: var(--s-2);
  padding: var(--s-4) 0 var(--s-6); text-align: center;
  border-bottom: 1px solid var(--line);
}
.receipt__icon {
  display: grid; place-items: center; inline-size: 3.5rem; block-size: 3.5rem;
  margin-bottom: var(--s-2); border-radius: 50%;
  background: var(--success-soft); color: var(--success);
}
.receipt__status--warning .receipt__icon { background: var(--warning-soft); color: var(--warning); }
.receipt__status--info    .receipt__icon { background: var(--info-soft);    color: var(--info); }
.receipt__status--danger  .receipt__icon { background: var(--danger-soft);  color: var(--danger); }
.receipt__title { font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2rem); letter-spacing: -.03em; margin: 0; }
.receipt__code { margin: 0; font-size: .9375rem; color: var(--text-muted); }
.receipt__code strong { color: var(--text); font-variant-numeric: tabular-nums; letter-spacing: .02em; }
.receipt__lede { max-width: 40ch; margin: var(--s-2) 0 0; font-size: .9375rem; color: var(--text-muted); line-height: 1.55; }
.receipt__section { padding: var(--s-6) 0; border-bottom: 1px solid var(--line); }
.receipt__section:last-of-type { border-bottom: 0; }
.receipt__section .section-title { margin-top: 0; }
.receipt__section--pay { padding-top: var(--s-6); }
.receipt__payhead { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-2); margin-bottom: var(--s-2); }
.receipt__payhead .section-title { margin: 0; font-size: 1rem; text-transform: none; letter-spacing: -.02em; color: var(--text); }
.receipt__next { margin: 0; font-size: .9375rem; line-height: 1.6; color: var(--text-muted); }
.receipt__actions { padding-top: var(--s-6); }
.receipt .pay-element { margin-top: var(--s-3); }

.next-steps { margin: 0; padding-inline-start: 1.25rem; display: grid; gap: var(--s-2); font-size: .9375rem; line-height: 1.55; color: var(--text-muted); }
.next-steps li::marker { color: var(--accent-text); font-weight: 600; }

.detail { display: grid; gap: var(--s-4); margin: 0; }
.detail__row { display: grid; gap: 2px; margin: 0; }
.detail__row dt {
  display: flex; align-items: center; gap: var(--s-1);
  font-size: .6875rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--text-muted);
}
.detail__row dt .icon { color: var(--text-subtle); }
.detail__row dd { margin: 0; display: grid; gap: 1px; font-size: 1rem; line-height: 1.4; word-break: break-word; }
.detail__row dd strong { font-weight: 600; letter-spacing: -.01em; }
.detail__sub { font-size: .8125rem; color: var(--text-muted); }
.detail__time { font-size: 1rem; font-weight: 500; }
.detail__tz { font-size: .75rem; color: var(--text-subtle); font-weight: 500; }
.detail--compact { gap: var(--s-2); }
.detail--compact .detail__row { grid-template-columns: 5rem minmax(0, 1fr); align-items: baseline; gap: var(--s-3); }
.detail--compact .detail__row dt { text-transform: none; letter-spacing: 0; font-size: .8125rem; font-weight: 500; }
.detail--compact .detail__row dd { font-size: .9375rem; }
.detail__paystate {
  display: flex; align-items: center; gap: var(--s-2);
  margin: var(--s-3) 0 0; font-size: .8125rem; font-weight: 500; color: var(--text-muted);
}
.detail__paystate .icon { color: var(--text-subtle); }
@media (min-width: 640px) {
  .detail__row { grid-template-columns: 8rem minmax(0, 1fr); align-items: baseline; gap: var(--s-4); }
  .detail__row dt { padding-top: 3px; }
}

/* Step-4 panel is a card; on pay.php the .receipt IS the card. Same padding. */
.panel .receipt { padding: 0; }
.panel[data-panel="4"] .receipt.card { padding: var(--s-6); }
@media (min-width: 768px) { .panel[data-panel="4"] .receipt.card { padding: var(--s-8); } }

/* ---- small polish ------------------------------------------------------- */
.select-bar .btn .icon { margin-inline-start: 2px; }
.vehicle-card__price-note { letter-spacing: 0; }
@media (max-width: 719.98px) { .vehicle-card__price { font-size: 1.1875rem; } }
.action-bar .btn { font-weight: 600; }
.action-bar__total strong { font-size: 1.1875rem; }
/* Toasts must not sit under the sticky Reserve bar on a phone. */
@media (max-width: 999px) { body.is-booking .toast-region { bottom: calc(5.5rem + var(--safe-b)); } }
/* Uppercasing is for the typed code, not the hint. */
#flightNumber::placeholder, #departingFlight::placeholder { text-transform: none; }
.section-title { flex-wrap: wrap; row-gap: 2px; }

/* ==========================================================================
   2026-09 admin clean-up: rates, add-ons, vehicles, CRM views
   ========================================================================== */
.rate-grid { display: grid; gap: var(--s-4); }
@media (min-width: 1100px) { .rate-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.rate-card { padding: var(--s-5); }
.rate-card__head { display: flex; align-items: center; gap: var(--s-4); margin-bottom: var(--s-4); }
.rate-card__photo {
  flex: none; inline-size: 96px; block-size: 64px; border-radius: var(--r-sm); overflow: hidden;
  background: var(--surface-sunk); border: 1px solid var(--line); display: grid; place-items: center;
}
.rate-card__photo img { inline-size: 100%; block-size: 100%; object-fit: cover; }
.rate-card__photo--art img { inline-size: 80%; block-size: 80%; object-fit: contain; }
.rate-card__title { min-width: 0; flex: 1 1 auto; }
.rate-card__title h2 { font-size: 1.0625rem; margin: 0; }
.rate-card__title p { margin: 0; }
.rate-card__active { flex: none; }
.rate-card__fields { display: grid; gap: var(--s-3); grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 640px) { .rate-card__fields { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.rate-card__fields .field__help { font-size: .75rem; }
.money-input { position: relative; display: flex; align-items: center; }
.money-input__prefix { position: absolute; inset-inline-start: var(--s-3); color: var(--text-muted); font-weight: 500; pointer-events: none; }
.money-input .input { padding-inline-start: calc(var(--s-3) + 1ch + var(--s-2)); }
@media (max-width: 519.98px) { .rate-card__head { flex-wrap: wrap; } .rate-card__active { flex-basis: 100%; } }

/* Vehicle editor: the photo is a thumbnail beside the fields, not a banner. */
.vehicle-shot { max-inline-size: 340px; }
@media (min-width: 900px) { .vehicle-edit { grid-template-columns: 300px 1fr; } }
.upload-row .field__help { font-size: .75rem; }

/* CRM quick views wrap on a phone instead of scrolling off-screen. */
.quickviews { flex-wrap: wrap; overflow: visible; }

/* ---- Airport trip switch (step 3): green when on, always reachable ---- */
.booking-shell .airport-card { margin-block: var(--s-5) var(--s-3); }
.airport-toggle__input:checked + .airport-toggle__track { background: var(--success); }
.airport-toggle__track { transition: background var(--dur) var(--ease); }
.airport-toggle__track::before { transition: transform var(--dur) var(--ease); }
.flight-card .flight-req { display: none; color: var(--danger, #b3261e); }
.flight-card.is-on .flight-req { display: inline; }
