/* ==========================================================================
   Cabzy guest app
   --------------------------------------------------------------------------
   Built on the tokens in tokens.css. Nothing in this file should contain a raw
   hex value, a hard-coded shadow or a bare pixel radius — if one is missing
   from the token set, add it there rather than inventing it here.

   Structure, top to bottom:
     1. reset and base
     2. layout shell — header, footer, tab bar, views
     3. components — buttons, fields, cards, badges, toasts
     4. pages — home, wizard, contact, auth, bookings
     5. responsive

   The previous stylesheet painted a full-screen Google map behind everything
   and floated a translucent header over it. That is gone: the map now lives
   inside the booking wizard, where it is doing a job, rather than behind pages
   that never referred to it.
   ========================================================================== */

/* ==========================================================================
   1. Reset and base
   ========================================================================== */

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

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

body {
  margin: 0;
  background: var(--surface-page);
  color: var(--text-body);
  font: var(--type-body);
  /* Reserve the tab bar's height on small screens so the last element of a
     page is never trapped underneath it. */
  padding-bottom: env(safe-area-inset-bottom);
}

img {
  max-width: 100%;
  display: block;
}

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

button {
  font-family: inherit;
}

h1,
h2,
h3,
h4 {
  color: var(--text-heading);
  font-family: var(--font-primary);
  letter-spacing: var(--tracking-tight);
  margin: 0;
  text-wrap: pretty;
}

p {
  margin: 0;
}

/* Focus is a yellow ring, never a border-colour change — a colour swap moves
   nothing and is invisible to anyone who cannot distinguish the two.

   `:not(.location__input)` because that input sits inside a rounded pill that
   draws the ring itself on :focus-within. Without the exception both fired and
   the field got two rings: the pill's rounded one plus a rectangular one
   hugging the text box inside it. */
:focus-visible:not(.location__input) {
  outline: none;
  box-shadow: var(--ring-focus);
}

.hidden {
  display: none !important;
}

/* Visible to screen readers, not on screen. Used for the live region that
   announces wizard step changes. */
.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;
}

/* ==========================================================================
   2. Layout shell
   ========================================================================== */

.shell {
  max-width: var(--app-max);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

/* --- header --------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 60;
  background: var(--surface-card);
  box-shadow: var(--shadow-sm);
}

.site-header__inner {
  max-width: var(--app-max);
  margin: 0 auto;
  padding: 0 var(--gutter);
  height: var(--header-h);
  display: flex;
  align-items: center;
  gap: var(--space-7);
}

/* The wordmark is type-set rather than an image: Poppins 800 italic with "zy"
   in yellow, which is how cabzy-app has always drawn it. */
/* The wordmark is type-set: Poppins 800 italic with "zy" in yellow. That yellow
   measures 2.03:1 against white, which WCAG exempts as a logotype — the mark is
   still legible because "Cab" carries the contrast and the shape is the brand.
   Do not "fix" it by darkening the yellow; fix it by supplying a real lockup. */
.logo {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  font-family: var(--font-primary);
  font-weight: var(--weight-black);
  font-style: italic;
  font-size: 1.625rem;
  letter-spacing: var(--tracking-display);
  color: var(--text-heading);
  text-decoration: none;
  line-height: 1;
  white-space: nowrap;
}

.logo em {
  font-style: inherit;
  color: var(--accent);
}

.logo--sm {
  font-size: 1.5rem;
}

.site-nav {
  display: flex;
  gap: var(--space-9);
  margin-left: var(--space-4);
}

.site-nav a {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  text-decoration: none;
  color: var(--text-heading);
  padding: var(--space-2) 0;
  border-bottom: 2px solid transparent;
  transition: border-color var(--transition);
}

.site-nav a:hover,
.site-nav a[aria-current='page'] {
  border-bottom-color: var(--accent);
}

.site-header__end {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--space-6);
}

/* Show prices in.

   A native <select>, because on a phone that opens the platform's own wheel —
   faster than any custom menu and accessible without a line of code. Styled
   down to sit quietly beside Sign in: this changes how the page reads, and it
   should not compete with the button that starts a booking. */
.currency-picker__select {
  appearance: none;
  padding: 0.35rem 1.6rem 0.35rem 0.6rem;
  border: 1px solid var(--border, rgba(15, 23, 42, 0.14));
  border-radius: 999px;
  background-color: transparent;
  /* A chevron as a data: URI rather than a second element, so the control
     stays one native <select> and keeps its platform behaviour. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.55rem center;
  color: var(--text-muted);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  line-height: 1.4;
  cursor: pointer;
}
.currency-picker__select:hover {
  color: var(--text, inherit);
}
.currency-picker__select:focus-visible {
  outline: 2px solid var(--brand, #f5a623);
  outline-offset: 2px;
}
/* The dropdown list itself is drawn by the OS and inherits the page's colours
   on some platforms, where `transparent` would render as unreadable. */
.currency-picker__select option {
  background: #ffffff;
  color: #0f172a;
}

.site-header__signin {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  text-decoration: none;
  color: var(--text-muted);
  background: none;
  border: none;
  cursor: pointer;
  padding: var(--space-4) var(--space-2);
}

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

.account {
  position: relative;
}

.account__trigger {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  border: none;
  background: none;
  padding: var(--space-1);
  cursor: pointer;
  border-radius: var(--radius-pill);
}

.avatar {
  width: 38px;
  height: 38px;
  border-radius: var(--radius-round);
  background: var(--surface-subtle);
  color: var(--text-heading);
  display: grid;
  place-items: center;
  font-family: var(--font-ui);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  flex-shrink: 0;
}

.account__menu {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  width: 240px;
  background: var(--surface-card);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  z-index: 80;
}

.account__who {
  padding: var(--space-6);
  border-bottom: 1px solid var(--border-subtle);
}

.account__name {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-heading);
  overflow-wrap: anywhere;
}

.account__email {
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  color: var(--text-muted);
  margin-top: var(--space-1);
  overflow-wrap: anywhere;
}

.account__item {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  width: 100%;
  padding: 14px var(--space-6);
  border: none;
  background: none;
  cursor: pointer;
  text-align: left;
  text-decoration: none;
  color: var(--text-body);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  border-bottom: 1px solid var(--border-subtle);
  transition: background var(--transition);
}

.account__item:last-child {
  border-bottom: none;
}

.account__item:hover {
  background: var(--surface-hover);
}

/* --- views ---------------------------------------------------------------- */

.app-main {
  min-height: calc(100vh - var(--header-h));
}

.view {
  display: none;
}

.view.active {
  display: block;
  /* 8px rise over 0.3s — the one entrance animation in the system. */
  animation: view-in var(--duration-slow) var(--ease-standard);
}

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

@media (prefers-reduced-motion: reduce) {
  .view.active {
    animation: none;
  }
  * {
    transition-duration: 0.01ms !important;
  }
}

.page {
  max-width: var(--app-max);
  margin: 0 auto;
  padding: var(--space-11) var(--gutter) var(--space-11);
}

.page__title {
  font: var(--type-display);
  font-size: clamp(2rem, 3.6vw, 2.75rem);
  letter-spacing: var(--tracking-display);
  margin: var(--space-5) 0 0;
}

.page__lede {
  font-family: var(--font-ui);
  font-size: var(--text-lg);
  line-height: var(--leading-normal);
  color: var(--text-muted);
  margin: var(--space-7) 0 0;
  max-width: 52ch;
}

.crumbs {
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  color: var(--text-muted);
}

/* Same 24px minimum-target reason as the footer links: a 12px breadcrumb is a
   14px-tall target otherwise. */
.crumbs a {
  display: inline-flex;
  align-items: center;
  min-height: 26px;
  color: var(--text-muted);
  text-decoration: none;
}

.crumbs a:hover {
  text-decoration: underline;
}

.crumbs strong {
  color: var(--text-heading);
  font-weight: var(--weight-semibold);
}

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

.site-footer {
  background: var(--surface-card);
  margin-top: var(--space-11);
}

.site-footer__grid {
  max-width: var(--app-max);
  margin: 0 auto;
  padding: 56px var(--space-8) var(--space-9);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-10);
}

.site-footer h4 {
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-heading);
  margin: 0 0 14px;
}

.site-footer__links {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  color: var(--text-muted);
}

/* Padded to clear WCAG 2.5.8's 24px minimum target size. A 14px line of text
   with a 10px gap under it — which is what a bare footer link measures — is a
   21px target, and these are the links people reach for one-handed on a phone.
   The gap shrinks to compensate, so the visual rhythm is unchanged. */
/* The address is the one contact detail with a shape of its own. The office
   types it into a textarea in Settings and whatever line breaks they put there
   are what shows here — pre-line collapses the runs of spaces and the leading
   indentation that come with pasted text, and keeps the newlines. `pre-wrap`
   would keep the accidents too. Nothing turns \n into <br>: this is text the
   admin typed, and it is written with textContent. */
[data-field='address'] {
  white-space: pre-line;
}

.site-footer__links a,
.site-footer__links span {
  display: inline-flex;
  align-items: center;
  min-height: 26px;
  color: var(--text-muted);
  text-decoration: none;
}

.site-footer__links a:hover {
  color: var(--text-heading);
}

/* A button, not a link, because it performs an action rather than going
   anywhere — but it sits in a column of links, so it is drawn as one. The
   underline is what tells the eye it is pressable, since there is no other
   affordance at this size. */
/* [hidden] FIRST, and it is not optional. The `hidden` attribute works by way
   of `[hidden] { display: none }` in the browser's own stylesheet, and any
   `display` declared in an author stylesheet — including the inline-flex below
   — outranks it. Without this rule the button is visible in every browser that
   cannot install anything. */
.site-footer__install[hidden] {
  display: none;
}

.site-footer__install {
  display: inline-flex;
  align-items: center;
  min-height: 26px;
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  text-align: left;
  color: var(--text-link);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.site-footer__install:hover {
  color: var(--text-heading);
}

.site-footer__blurb {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--text-muted);
  margin: 14px 0 0;
  max-width: 30ch;
}

.site-footer__legal {
  max-width: var(--app-max);
  margin: 0 auto;
  padding: var(--space-7) var(--space-8) var(--space-10);
  border-top: 1px solid var(--border-subtle);
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  color: var(--text-muted);
}

/* --- mobile tab bar ------------------------------------------------------- */

.tab-bar {
  display: none;
}

.tab-bar__link {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
  text-decoration: none;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-muted);
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
}

/*
  The design marks the active tab by colouring it Cabzy Yellow. That measures
  2.03:1 against the white bar — WCAG wants 4.5:1 for the label and 3:1 for the
  icon — and this is the one control on a phone that answers "where am I?", so
  it is not somewhere to lose legibility.

  The accent still does the signalling; it just does it as a fill rather than as
  ink, which is the system's own rule ("yellow is fill, black is ink"). The
  icon sits in a yellow wash, the label goes navy and semibold, and the tab
  reads as selected from across the room instead of dissolving into the bar.
*/
.tab-bar__link[aria-current='page'] {
  color: var(--text-heading);
  font-weight: var(--weight-semibold);
}

.tab-bar__link[aria-current='page'] svg {
  stroke-width: 2.25;
  background: var(--accent-wash-strong);
  border-radius: var(--radius-pill);
  box-sizing: content-box;
  padding: var(--space-2) var(--space-6);
  margin: calc(var(--space-2) * -1) 0;
}

/* ==========================================================================
   3. Components
   ========================================================================== */

/* --- icons ---------------------------------------------------------------- */

/* Inline 24px outline glyphs with stroke="currentColor". The design system
   standardises on Lucide; these are drawn inline rather than loaded from a CDN
   so the app has no third-party script on the critical path and works offline
   under the service worker. Paths are Lucide's. */
.icon {
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  stroke: currentColor;
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.icon--sm {
  width: 18px;
  height: 18px;
}

.icon--tab {
  width: 20px;
  height: 20px;
  stroke-width: 1.75;
}

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

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  min-height: var(--tap-min);
  padding: 0 var(--space-7);
  border: none;
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--on-accent);
  font: var(--type-button);
  letter-spacing: var(--tracking-button);
  text-decoration: none;
  cursor: pointer;
  transition: background var(--transition), transform var(--duration-fast) var(--ease-standard);
}

.btn:hover {
  background: var(--accent-hover);
}

.btn:active {
  background: var(--accent-press);
  transform: scale(var(--press-scale));
}

/* Disabled is opacity plus a blocked cursor, never a grey repaint — a repaint
   makes a disabled primary button look like a secondary one. */
.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}

.btn--lg {
  min-height: 52px;
  font-size: var(--text-base);
}

.btn--sm {
  min-height: 40px;
  padding: 0 var(--space-6);
  font-size: var(--text-sm);
}

.btn--full {
  width: 100%;
}

.btn--ghost {
  background: transparent;
  border: 2px solid var(--border-strong);
  color: var(--text-heading);
}

.btn--ghost:hover,
.btn--ghost:active {
  background: var(--surface-hover);
}

.btn--quiet {
  background: none;
  border: none;
  padding: var(--space-4);
  min-height: auto;
  color: var(--text-heading);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: normal;
}

.btn--quiet:hover {
  background: none;
  text-decoration: underline;
}

.btn__loader {
  width: 18px;
  height: 18px;
  border: 2px solid rgba(17, 17, 17, 0.25);
  border-top-color: var(--on-accent);
  border-radius: var(--radius-round);
  animation: spin 0.6s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* --- pills and badges ----------------------------------------------------- */

.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) 14px;
  border-radius: var(--radius-pill);
  background: var(--surface-card);
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  color: var(--text-body);
  border: none;
  cursor: default;
  white-space: nowrap;
}

button.pill,
a.pill {
  cursor: pointer;
  text-decoration: none;
  transition: background var(--transition);
}

button.pill:hover,
a.pill:hover {
  background: var(--accent-wash);
}

.pill--accent {
  background: var(--accent-wash);
  font-weight: var(--weight-semibold);
  color: var(--text-heading);
}

.pill--subtle {
  background: var(--surface-subtle);
}

.badge {
  display: inline-flex;
  align-items: center;
  padding: var(--space-2) 10px;
  border-radius: var(--radius-pill);
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  text-transform: capitalize;
  background: var(--surface-subtle);
  color: var(--text-heading);
}

.badge--pending {
  background: var(--status-pending-bg);
}

.badge--complete {
  background: var(--status-complete-bg);
  color: var(--status-success);
}

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

/* White, shadow only, no borders. The single exception is
   .reservation-card's yellow left rule. */
.card {
  background: var(--surface-card);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: var(--space-7);
}

.card--panel {
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  padding: var(--space-8);
}

.card--float {
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--space-8);
}

.card--inverse {
  background: var(--surface-inverse);
  color: var(--text-inverse);
  border-radius: var(--radius-lg);
  padding: var(--space-8);
}

.card--inverse h2,
.card--inverse h3 {
  color: var(--text-inverse);
}

.card__title {
  font-family: var(--font-primary);
  font-weight: var(--weight-bold);
  font-size: var(--text-lg);
  letter-spacing: var(--tracking-tight);
  color: var(--text-heading);
  margin: 0 0 var(--space-3);
}

.card__body {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--text-muted);
}

/* --- form fields ---------------------------------------------------------- */

.field {
  margin-bottom: var(--space-5);
}

.field__label {
  display: block;
  font-family: var(--font-ui);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--text-heading);
  margin-bottom: var(--space-4);
}

.field__hint {
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  line-height: var(--leading-snug);
  color: var(--text-muted);
  margin-top: var(--space-3);
}

.input {
  width: 100%;
  min-height: var(--tap-min);
  border: none;
  background: var(--surface-subtle);
  border-radius: var(--radius-sm);
  padding: 14px var(--space-6);
  font-family: var(--font-ui);
  font-size: var(--text-md);
  color: var(--text-body);
  outline: none;
}

.input::placeholder {
  color: var(--text-muted);
}

.input:focus {
  box-shadow: var(--ring-focus);
}

textarea.input {
  line-height: var(--leading-normal);
  resize: vertical;
  min-height: 88px;
}

.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-5);
}

/* Six digits, widely spaced, centred — the shape of the thing being copied
   out of an email. */
.input--otp {
  text-align: center;
  font-size: var(--text-2xl);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-otp);
  /* The tracking adds a trailing gap after the last digit; pull it back so the
     row reads as centred rather than sitting slightly left. */
  text-indent: var(--tracking-otp);
}

/* --- location field ------------------------------------------------------- */

.location {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-5);
  background: var(--surface-subtle);
  border-radius: var(--radius-pill);
  padding: 0 var(--space-7);
  min-height: 52px;
  margin-bottom: var(--space-5);
}

.location:focus-within {
  box-shadow: var(--ring-focus);
}

.location__icon {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  color: var(--text-heading);
}

/* Pickup is a hollow ring, drop-off a filled pin — the map's own vocabulary,
   so the field and the marker agree. */
.location__icon--pickup svg {
  fill: none;
  stroke: var(--accent);
  stroke-width: 3;
}

.location__icon--dropoff svg {
  fill: var(--text-heading);
  stroke: none;
}

.location__input {
  flex: 1;
  min-width: 0;
  border: none;
  background: none;
  /* Both, and both deliberate: `outline` kills the UA ring, `box-shadow` stops
     the global :focus-visible rule reaching this element on browsers that do
     not support the :not() selector above. The pill draws the only ring. */
  outline: none;
  box-shadow: none;
  font-family: var(--font-ui);
  font-size: var(--text-md);
  color: var(--text-body);
  padding: 14px 0;
  /* A long place name scrolls inside the field instead of widening the pill. */
  text-overflow: ellipsis;
}

.location__input:focus {
  outline: none;
  box-shadow: none;
}

.location__input::placeholder {
  color: var(--text-muted);
}

.suggestions {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: 40;
  background: var(--surface-card);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  list-style: none;
  margin: 0;
  padding: var(--space-3);
  max-height: 280px;
  overflow-y: auto;
}

.suggestions li {
  padding: var(--space-5) var(--space-6);
  border-radius: var(--radius-sm);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  color: var(--text-body);
  cursor: pointer;
}

.suggestions li:hover,
.suggestions li.active {
  background: var(--accent-wash);
}

/* The place name leads; where it is sits under it in muted type. A long
   business name truncates rather than wrapping the row to three lines. */
.autocomplete-item__main {
  font-weight: var(--weight-medium);
  color: var(--text-heading);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.autocomplete-item__secondary {
  font-size: var(--text-2xs);
  color: var(--text-muted);
  margin-top: var(--space-1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Destination shortcut chips. The name comes from the admin's `destinations`
   row, which can be anything they typed — one long airport name would
   otherwise push the row past the card's edge. */
.chips .pill {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: inline-block;
}

/* --- counter -------------------------------------------------------------- */

.counter {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  padding: var(--space-6) 0;
  border-bottom: 1px solid var(--border-subtle);
}

.counter:last-of-type {
  border-bottom: none;
}

.counter__text {
  flex: 1;
  min-width: 0;
}

.counter__label {
  display: block;
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-heading);
}

.counter__hint {
  display: block;
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  color: var(--text-muted);
  margin-top: var(--space-1);
}

.counter__controls {
  display: flex;
  align-items: center;
  gap: var(--space-5);
}

.counter__btn {
  width: var(--tap-min);
  height: var(--tap-min);
  border-radius: var(--radius-round);
  border: 2px solid var(--border-strong);
  background: var(--surface-card);
  color: var(--text-heading);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: background var(--transition), border-color var(--transition);
}

.counter__btn:hover:not(:disabled) {
  background: var(--accent-wash);
  border-color: var(--accent);
}

.counter__btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.counter__value {
  min-width: 24px;
  text-align: center;
  font-family: var(--font-ui);
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  color: var(--text-heading);
  font-variant-numeric: tabular-nums;
}

/* --- vehicle card --------------------------------------------------------- */

.vehicle {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  width: 100%;
  text-align: left;
  padding: var(--space-6);
  border: 2px solid transparent;
  border-radius: var(--radius-md);
  background: var(--surface-subtle);
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
}

.vehicle:hover {
  background: var(--accent-wash);
}

.vehicle.selected {
  background: var(--surface-card);
  border-color: var(--accent);
  box-shadow: var(--shadow-sm);
}

/* The vehicle image slot.
   No photography was supplied and vehicle_types has no image column, so this
   holds a silhouette today. It is sized for a real photo: swap the <svg> for an
   <img> and nothing around it has to move. */
.vehicle__thumb {
  width: 56px;
  height: 44px;
  flex-shrink: 0;
  border-radius: var(--radius-sm);
  background: var(--surface-card);
  display: grid;
  place-items: center;
  color: var(--text-heading);
  overflow: hidden;
}

.vehicle__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The slot can BE the image rather than contain one — that is what the wizard
   renders when a vehicle type has a photo. `display: block` overrides the grid
   above, which does nothing useful on a replaced element and leaves a baseline
   gap under it. Photos arrive in whatever shape the office's phone produced, so
   cover crops them to the slot instead of letting a tall one stretch the row. */
img.vehicle__thumb {
  display: block;
  object-fit: cover;
  background: var(--surface-card);
}

.vehicle.selected .vehicle__thumb {
  background: var(--accent-wash);
}

.vehicle__thumb--sm {
  width: 40px;
  height: 32px;
  background: var(--surface-card);
}

.vehicle__thumb--sm .icon {
  width: 20px;
  height: 20px;
}

/* The chosen vehicle on the details step. */
.recap-vehicle {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  min-width: 0;
}

.recap-vehicle .summary__value,
.recap-vehicle .summary__label {
  display: block;
  text-align: left;
}

.vehicle__text {
  flex: 1;
  min-width: 0;
}

/* Block, because these are <span>s inside a <button> — a button's content is
   inline by default, and without this the vehicle name and its seats/bags line
   run together on one row. */
.vehicle__text,
.vehicle__name,
.vehicle__meta {
  display: block;
}

.vehicle__name {
  font-family: var(--font-ui);
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  color: var(--text-heading);
}

.vehicle__meta {
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  color: var(--text-muted);
  margin-top: var(--space-1);
}

.vehicle__price {
  font-family: var(--font-ui);
  font-size: var(--text-base);
  font-weight: var(--weight-bold);
  color: var(--text-heading);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* --- reservation card ----------------------------------------------------- */

/* The one place a border is allowed: a 4px yellow left rule. */
.reservation {
  background: var(--surface-card);
  border-radius: var(--radius-md);
  border-left: 4px solid var(--accent);
  box-shadow: var(--shadow-sm);
  padding: var(--space-7);
  margin-bottom: var(--space-5);
}

.reservation__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  margin-bottom: var(--space-5);
}

.reservation__ref {
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

.reservation__route {
  display: flex;
  gap: var(--space-5);
  align-items: flex-start;
}

.reservation__stops {
  flex: 1;
  min-width: 0;
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  color: var(--text-body);
}

.reservation__stop {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
}

.reservation__stop + .reservation__stop {
  margin-top: var(--space-4);
}

.reservation__dot {
  width: 10px;
  height: 10px;
  border-radius: var(--radius-round);
  flex-shrink: 0;
  transform: translateY(-1px);
}

.reservation__dot--from {
  border: 2px solid var(--accent);
}

.reservation__dot--to {
  background: var(--text-heading);
}

.reservation__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border-subtle);
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  color: var(--text-muted);
}

.reservation__fare {
  margin-left: auto;
  font-size: var(--text-base);
  font-weight: var(--weight-bold);
  color: var(--text-heading);
  font-variant-numeric: tabular-nums;
}

/* --- summary rows --------------------------------------------------------- */

.summary {
  background: var(--surface-subtle);
  border-radius: var(--radius-md);
  padding: var(--space-6);
}

.summary__row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-5);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  color: var(--text-body);
  padding: var(--space-3) 0;
}

.summary__row + .summary__row {
  border-top: 1px solid var(--border-strong);
}

.summary__label {
  color: var(--text-muted);
  flex-shrink: 0;
}

.summary__value {
  text-align: right;
  overflow-wrap: anywhere;
}

.summary__row--total {
  font-size: var(--text-base);
  font-weight: var(--weight-bold);
  color: var(--text-heading);
}

/* --- empty state and toasts ----------------------------------------------- */

.empty {
  text-align: center;
  padding: var(--space-10) var(--space-7);
}

.empty__text {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin-bottom: var(--space-6);
}

.toast-container {
  position: fixed;
  left: 50%;
  bottom: var(--space-8);
  transform: translateX(-50%);
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  align-items: center;
  width: calc(100% - var(--space-9));
  max-width: 420px;
  pointer-events: none;
}

.toast {
  pointer-events: auto;
  background: var(--surface-inverse);
  color: var(--text-inverse);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-lg);
  padding: 14px var(--space-7);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  text-align: center;
  animation: toast-in var(--duration-base) var(--ease-standard);
}

.toast--error {
  background: var(--status-error);
}

.toast--success {
  background: var(--status-success);
}

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

.sw-toast {
  pointer-events: auto;
  background: var(--surface-inverse);
  color: var(--text-inverse);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  padding: var(--space-6);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  width: 100%;
}

.sw-toast__actions {
  display: flex;
  gap: var(--space-4);
}

.sw-toast__btn {
  flex: 1;
  min-height: 36px;
  border: none;
  border-radius: var(--radius-sm);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  cursor: pointer;
}

.sw-toast__btn--primary {
  background: var(--accent);
  color: var(--on-accent);
}

.sw-toast__btn--secondary {
  background: transparent;
  color: var(--text-inverse);
  border: 1px solid rgba(255, 255, 255, 0.3);
}

/* --------------------------------------------------------------------------
   Connection bar
   --------------------------------------------------------------------------
   Sticky rather than fixed, so it takes part in layout: the page starts 34px
   lower while it is up and nothing is hidden underneath it. The sticky header
   is pushed down by the same amount by .has-connection-bar below, which is set
   on <html> at the same time as the bar is shown.

   The height is fixed and the text is clipped to one line on purpose. The
   header offset is a constant, and a message that wrapped to two lines on a
   320px screen would slide the header out from under it.
   -------------------------------------------------------------------------- */
/* See the note on .site-footer__install[hidden]: the display below would
   otherwise beat the browser's own [hidden] rule and leave an empty 34px bar
   across the top of every page. */
.connection-bar[hidden] {
  display: none;
}

.connection-bar {
  position: sticky;
  top: 0;
  z-index: 70;
  /* A block box, not a flex one. text-overflow only applies to block
     containers: centred with flex, a message too long for the screen overflows
     BOTH edges and is clipped at both, so it loses its first word as well as
     its last and no ellipsis is ever drawn. line-height does the centring here
     instead, which is exact because the height is fixed. */
  display: block;
  height: var(--connection-bar-h);
  line-height: var(--connection-bar-h);
  padding: 0 var(--gutter);
  text-align: center;
  font-family: var(--font-ui);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  color: var(--text-inverse);
  background: var(--gray-800);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* --status-success (#16a34a) is the badge colour and measures 3.3:1 against
   white — fine behind the dark badge text it was chosen for, not fine under
   13px white type. This is the same green two steps darker, at 5:1. */
.connection-bar[data-state='online'] {
  background: #15803d;
}

.has-connection-bar .site-header {
  top: var(--connection-bar-h);
}

/* --------------------------------------------------------------------------
   Install card
   --------------------------------------------------------------------------
   Shares the toast container and the service-worker toast's shape, because it
   is the same kind of thing: an interruption the guest can wave away. It is
   not a modal and never blocks the page.
   -------------------------------------------------------------------------- */
.install-card {
  pointer-events: auto;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding: var(--space-6);
  border-radius: var(--radius-md);
  background: var(--surface-inverse);
  color: var(--text-inverse);
  box-shadow: var(--shadow-lg);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  animation: toast-in var(--duration-base) var(--ease-standard);
}

.install-card__text {
  margin: 0;
  line-height: var(--leading-snug);
}

.install-card__actions {
  display: flex;
  gap: var(--space-4);
}

.install-card__btn {
  flex: 1;
  min-height: 36px;
  border: none;
  border-radius: var(--radius-sm);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  cursor: pointer;
}

.install-card__btn--primary {
  background: var(--accent);
  color: var(--on-accent);
}

.install-card__btn--secondary {
  background: transparent;
  color: var(--text-inverse);
  border: 1px solid rgba(255, 255, 255, 0.3);
}

/* ==========================================================================
   4. Pages
   ========================================================================== */

/* --- home: hero + wizard -------------------------------------------------- */

/* --- hero background video ------------------------------------------------ */

/* Full-bleed: .hero keeps its own max-width and centring, this sits behind it
   and runs to both edges of the viewport. */
.hero-media {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}

.hero-media__video,
.hero-media__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}

.hero-media__video {
  width: 100%;
  height: 100%;
  /* The clip is 2.35:1 and the hero is nearer 16:9, so cover crops top and
     bottom. `center` keeps the horizon, which is where every one of these
     aerial shots puts its subject. */
  object-fit: cover;
  object-position: center;
  z-index: -2;
  /* The poster and the first video frame are the same image, so there is
     nothing to cross-fade — this only softens the moment the still starts
     moving. */
  opacity: 0.999;
  transition: opacity var(--duration-slow, 0.3s) ease;
}

/* Darker on the left, where the headline is; lighter on the right, where the
   wizard card supplies its own white background. The second gradient is a flat
   darkening so that a bright frame — the sunrise shots — cannot wash the text
   out on its own. */
.hero-media__scrim {
  background:
    linear-gradient(
      to right,
      rgba(15, 23, 42, 0.84) 0%,
      rgba(15, 23, 42, 0.62) 45%,
      rgba(15, 23, 42, 0.4) 100%
    ),
    linear-gradient(to bottom, rgba(15, 23, 42, 0.34), rgba(15, 23, 42, 0.18));
}

/* The hero text inverts, because it is now on footage rather than on the page.
   Scoped to .hero-media so the same classes elsewhere are untouched. */
.hero-media .hero__title,
.hero-media .stat__value {
  color: var(--text-inverse, #fff);
}
.hero-media .hero__lede,
.hero-media .stat__label {
  color: rgba(255, 255, 255, 0.82);
}
/* --accent-wash is a 15% yellow meant for a white page; on footage it is
   invisible. Solid accent with dark ink instead. */
.hero-media .pill--accent {
  background: var(--accent, #f5a623);
  color: var(--on-accent, #111);
}

.hero {
  max-width: var(--app-max);
  margin: 0 auto;
  padding: 56px var(--space-8);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--space-11);
  align-items: center;
  align-content: center;
  min-height: calc(100vh - var(--header-h));
  /* Above the video and the scrim, both of which are at negative z inside
     .hero-media's own stacking context. */
  position: relative;
  z-index: 0;
}

.hero__title {
  font: var(--type-display);
  font-size: clamp(2.25rem, 4.6vw, 3.4rem);
  letter-spacing: var(--tracking-display);
  margin: var(--space-7) 0 0;
}

.hero__lede {
  font-family: var(--font-ui);
  font-size: var(--text-lg);
  line-height: var(--leading-normal);
  color: var(--text-muted);
  margin: var(--space-7) 0 0;
  max-width: 44ch;
}

.hero__stats {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  margin-top: 36px;
}

.stat__value {
  font-family: var(--font-primary);
  font-weight: var(--weight-black);
  font-size: var(--text-2xl);
  color: var(--text-heading);
}

.stat__label {
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  color: var(--text-muted);
  margin-top: var(--space-1);
}

/* --- wizard --------------------------------------------------------------- */

.wizard {
  display: flex;
  flex-direction: column;
}

.wizard__progress {
  display: flex;
  gap: var(--space-3);
  margin-bottom: 22px;
}

.wizard__tick {
  flex: 1;
  min-width: 0;
}

.wizard__bar {
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--border-strong);
  transition: background var(--transition);
}

.wizard__tick.done .wizard__bar,
.wizard__tick.current .wizard__bar {
  background: var(--accent);
}

.wizard__tick-label {
  font-family: var(--font-ui);
  font-size: 10px;
  font-weight: var(--weight-bold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-top: var(--space-4);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.wizard__tick.current .wizard__tick-label {
  color: var(--text-heading);
}

.wizard__step {
  display: none;
}

.wizard__step.active {
  display: block;
  animation: step-in var(--duration-slow) var(--ease-standard);
}

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

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

.wizard__title {
  font-family: var(--font-primary);
  font-weight: var(--weight-bold);
  font-size: var(--text-xl);
  letter-spacing: var(--tracking-tight);
  margin: 0 0 var(--space-6);
}

.wizard__sub {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin: 0 0 var(--space-7);
}

/* A sub-heading sits tight under its title. Pulling it up with a negative
   margin was how this worked and it was wrong — the offset had to exactly
   cancel the title's bottom margin, so changing one silently broke the other,
   and it overlapped outright wherever the title's margin was different.
   Cancel the title's margin instead; the two rules cannot disagree. */
.wizard__title:has(+ .wizard__sub) {
  margin-bottom: var(--space-2);
}

.wizard__nav {
  display: flex;
  gap: var(--space-5);
  margin-top: var(--space-7);
}

.wizard__nav .btn--ghost {
  flex: 0 0 96px;
}

.wizard__nav .btn:not(.btn--ghost) {
  flex: 1;
}

.wizard__note {
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  color: var(--text-muted);
  text-align: center;
  margin: 14px 0 0;
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: var(--space-2);
}

.vehicle-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* The route sketch shown above the vehicle list. */
.trip-map {
  height: 160px;
  border-radius: var(--radius-md);
  background: var(--surface-subtle);
  margin-bottom: var(--space-6);
  overflow: hidden;
}

.confirm {
  text-align: center;
  padding: var(--space-5) 0 var(--space-2);
}

.confirm__tick {
  width: 56px;
  height: 56px;
  border-radius: var(--radius-round);
  background: var(--accent-wash);
  display: grid;
  place-items: center;
  margin: 0 auto var(--space-6);
  font-family: var(--font-ui);
  font-size: 24px;
  font-weight: var(--weight-bold);
  color: var(--text-heading);
}

.confirm__body {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--text-muted);
  margin: 0 auto var(--space-7);
  max-width: 38ch;
}

/* --- home: content sections ----------------------------------------------- */

.trust {
  background: var(--surface-card);
}

.trust__inner {
  max-width: var(--app-max);
  margin: 0 auto;
  padding: var(--space-7) var(--space-8);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-6);
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  color: var(--text-muted);
  text-align: center;
}

/* Icon first, then the words, on one line. Centring the pair rather than the
   text means the icon moves with its label when the grid reflows instead of
   sitting under the previous one. */
.trust__inner span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3, 8px);
}
.trust__inner .icon {
  flex: none;
  color: var(--accent);
}

.section {
  max-width: var(--app-max);
  margin: 0 auto;
  padding: var(--space-11) var(--space-8) 0;
}

.section__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-6);
  flex-wrap: wrap;
  margin-bottom: var(--space-7);
}

.section__title {
  font-family: var(--font-primary);
  font-weight: var(--weight-bold);
  font-size: var(--text-2xl);
  letter-spacing: var(--tracking-tight);
  margin: 0;
}

.section__aside {
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  color: var(--text-muted);
}

.section__aside a {
  color: var(--text-heading);
  font-weight: var(--weight-semibold);
}

.section__lede {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin: var(--space-4) 0 var(--space-7);
  max-width: 56ch;
}

.grid {
  display: grid;
  gap: var(--space-6);
}

.grid--3 {
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

.grid--4 {
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}

.grid--2 {
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
}

.from-price {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  color: var(--text-heading);
  margin-top: var(--space-6);
  font-variant-numeric: tabular-nums;
}

/* Numbered steps. The circle is yellow because it is the only chrome on the
   row and yellow is the system's one accent. */
.step {
  display: flex;
  gap: var(--space-5);
  align-items: flex-start;
}

.step__num {
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  border-radius: var(--radius-round);
  background: var(--accent);
  color: var(--on-accent);
  display: grid;
  place-items: center;
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
}

/* ---- Popular route cards ------------------------------------------------- */
/*
   Every card has the same three bands — a media panel, the name and its meta
   line, then the fare — so a row lines up whether or not the office has
   uploaded photographs yet.

   These rules were rewritten from four overlapping patches into one pass. The
   duplicates they left behind were not harmless: `.route-tile__photo--empty`
   had drifted ABOVE `.route-tile__photo`, and at equal specificity the later
   rule wins, so the placeholder rendered as a plain grey panel with its
   initial invisible. Order is load-bearing here; keep the `--empty` override
   after the base rule it overrides.
*/
.route-tile {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  background: var(--surface-card);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: var(--space-6);
  transition: box-shadow var(--transition), transform var(--duration-fast) var(--ease-standard);
}

/* With a photo the card becomes image-over-text, and the padding moves off the
   card onto the body so the picture runs to the corners. */
.route-tile--photo {
  padding: 0;
  overflow: hidden;
}

.route-tile__photo {
  display: block;
  width: 100%;
  /* Fixed ratio so a row of cards lines up whatever shape the uploads are. */
  aspect-ratio: 16 / 10;
  object-fit: cover;
  background: var(--surface-subtle);
}

/* No photograph yet: the destination's initial on a wash of the brand yellow.
   Deliberately not a grey box — grey reads as an image that failed to load,
   and this has to read as a card that is simply waiting for a picture. */
.route-tile__photo--empty {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-wash);
  color: var(--accent);
  font-family: var(--font-primary);
  font-weight: var(--weight-black, 800);
  font-size: 2.5rem;
  line-height: 1;
  user-select: none;
}

/* A column, so the fare can be pushed down with `margin-top: auto`. That is
   what keeps the fares on one line across a row of cards whose names wrap to
   different heights. An earlier patch set `display: block` here for photo
   cards, which quietly cost exactly that. */
.route-tile__body {
  display: flex;
  flex-direction: column;
  flex: 1;
}

.route-tile--photo .route-tile__body {
  padding: var(--space-6);
}

.route-tile:hover {
  box-shadow: var(--shadow-md);
}

.route-tile:active {
  transform: scale(var(--press-scale));
}

/* These were <div>s and are now <span>s inside the card's <a>, so they need
   telling to be blocks — otherwise the name and the distance run together on
   one line. */
.route-tile__name,
.route-tile__meta {
  display: block;
}

.route-tile__name {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-heading);
}

.route-tile__meta {
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  color: var(--text-muted);
  margin-top: var(--space-1);
}

.route-tile__fare {
  display: block;
  margin-top: auto;
  padding-top: var(--space-4, 10px);
  font-family: var(--font-ui);
  font-weight: var(--weight-semibold);
  color: var(--text-heading);
}

.route-tile__price {
  font-family: var(--font-ui);
  font-size: var(--text-md);
  font-weight: var(--weight-bold);
  color: var(--text-heading);
  margin-top: var(--space-5);
  font-variant-numeric: tabular-nums;
}

.quote {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--text-body);
  margin: var(--space-4) 0 var(--space-6);
}

.quote__who {
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  color: var(--text-heading);
}

.quote__trip {
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  color: var(--text-muted);
}

.cta-band {
  max-width: var(--app-max);
  margin: var(--space-11) auto 0;
  padding: 0 var(--space-8);
}

.cta-band__inner {
  background: var(--surface-inverse);
  color: var(--text-inverse);
  border-radius: var(--radius-lg);
  padding: var(--space-9) var(--space-8);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-7);
}

.cta-band h2 {
  color: var(--text-inverse);
  font-size: var(--text-xl);
  margin: 0 0 var(--space-3);
}

.cta-band p {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  color: rgba(255, 255, 255, 0.72);
  max-width: 46ch;
}

/* --- contact -------------------------------------------------------------- */

.contact-card {
  background: var(--surface-card);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: var(--space-7);
  text-decoration: none;
  display: block;
  transition: box-shadow var(--transition);
}

a.contact-card:hover {
  box-shadow: var(--shadow-md);
}

.contact-card__label {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  color: var(--text-heading);
}

.contact-card__value {
  font-family: var(--font-ui);
  font-size: var(--text-md);
  color: var(--text-body);
  margin-top: var(--space-3);
  overflow-wrap: anywhere;
}

.contact-card__note {
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  color: var(--text-muted);
  margin-top: var(--space-3);
}

.hours {
  display: flex;
  justify-content: space-between;
  gap: var(--space-6);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  padding: var(--space-4) 0;
}

.hours + .hours {
  border-top: 1px solid var(--border-subtle);
}

.hours__when {
  color: var(--text-muted);
  text-align: right;
}

.faq {
  font-family: var(--font-ui);
}

.faq__q {
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  color: var(--text-heading);
  cursor: pointer;
  padding: var(--space-5) 0;
  list-style: none;
}

.faq__q::-webkit-details-marker {
  display: none;
}

.faq__q::before {
  content: '+';
  display: inline-block;
  width: 1.2em;
  color: var(--accent);
  font-weight: var(--weight-bold);
}

details[open] > .faq__q::before {
  content: '\2212';
}

.faq__a {
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--text-muted);
  padding: 0 0 var(--space-5) 1.2em;
}

/* --- auth ----------------------------------------------------------------- */

.split {
  max-width: var(--app-max);
  margin: 0 auto;
  padding: 56px var(--space-8);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--space-11);
  align-items: center;
  min-height: calc(100vh - var(--header-h));
}

.split__panel {
  max-width: var(--auth-max);
  width: 100%;
  justify-self: end;
}

.benefit + .benefit {
  margin-top: var(--space-7);
}

.benefit__title {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  color: var(--text-heading);
}

.benefit__body {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--text-muted);
  margin-top: var(--space-2);
}

.auth-panel {
  display: none;
}

.auth-panel.active {
  display: block;
}

/* --- bookings ------------------------------------------------------------- */

.bookings {
  max-width: var(--app-max);
  margin: 0 auto;
  padding: var(--space-10) var(--space-8) var(--space-11);
  display: grid;
  grid-template-columns: 300px 1fr;
  gap: var(--space-9);
  align-items: start;
}

.bookings__aside {
  position: sticky;
  top: calc(var(--header-h) + var(--space-6));
}

.bookings__identity {
  text-align: center;
  padding: var(--space-8) var(--space-7);
}

.bookings__name {
  font-family: var(--font-primary);
  font-weight: var(--weight-bold);
  font-size: var(--text-lg);
  color: var(--text-heading);
  margin-top: var(--space-5);
  overflow-wrap: anywhere;
}

.bookings__email {
  font-family: var(--font-ui);
  font-size: var(--text-2xs);
  color: var(--text-muted);
  margin-top: var(--space-1);
  overflow-wrap: anywhere;
}

.bookings__avatar {
  width: 64px;
  height: 64px;
  font-size: var(--text-lg);
  margin: 0 auto;
}

/* ==========================================================================
   5. Responsive
   ========================================================================== */

/* 768px — the wizard becomes a full-width card, the desktop nav gives way to
   the bottom tab bar, and the split layouts stack. */
@media (max-width: 767px) {
  .site-nav,
  .site-header__signin {
    display: none;
  }

  .hero,
  .split {
    padding: var(--space-9) var(--gutter);
    gap: var(--space-9);
    min-height: 0;
  }

  .hero__stats {
    gap: var(--space-7);
  }

  .section,
  .cta-band,
  .page {
    padding-left: var(--gutter);
    padding-right: var(--gutter);
  }

  .trust__inner {
    grid-template-columns: repeat(2, 1fr);
    text-align: left;
    padding: var(--space-6) var(--gutter);
  }

  .split__panel {
    justify-self: stretch;
    max-width: none;
  }

  .bookings {
    grid-template-columns: 1fr;
    padding: var(--space-8) var(--gutter) var(--space-9);
  }

  .bookings__aside {
    position: static;
  }

  .site-footer__grid,
  .site-footer__legal {
    padding-left: var(--gutter);
    padding-right: var(--gutter);
  }

  /* The tab bar is fixed, so the page needs to end above it. 78px is the bar
     plus its safe-area padding. */
  .app-main {
    padding-bottom: 78px;
  }

  .tab-bar {
    display: grid;
    /* FOUR — DAY TOURS — HIDDEN UNTIL THE TOURS ARE FINALISED.

       This number and the number of <a> tags in .tab-bar must agree. Hiding
       the Tours tab without changing this leaves an empty fifth column and
       four tabs squashed into four fifths of the bar, which reads as a
       layout bug rather than a missing feature. Back to 5 when the tab
       comes back — index.html carries the same marker.

       At 390px, four tabs is 97px each, comfortably over the 44px tap
       minimum with the label under the icon. */
  grid-template-columns: repeat(4, 1fr);
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 90;
    background: var(--surface-card);
    border-top: 1px solid var(--border-subtle);
    padding: 10px 0 calc(14px + env(safe-area-inset-bottom));
    box-shadow: var(--shadow-lg);
  }

  .toast-container {
    bottom: 92px;
  }

  .field-row {
    grid-template-columns: 1fr;
  }

  .cta-band__inner {
    padding: var(--space-8) var(--space-7);
  }
}

/* Below 380px the counter row runs out of width before the controls do. */
@media (max-width: 380px) {
  .counter {
    flex-wrap: wrap;
    gap: var(--space-5);
  }

  .counter__controls {
    margin-left: auto;
  }
}
