/* Nado customer portal — design system + portal styles.
   No Django template syntax in this file — keeps the parser out of it
   and means changes don't require a container restart. */

* { box-sizing: border-box; }

:root {
  /* Let the UA theme native controls (date picker, select, scrollbars,
     autofill) to match the active scheme — without this, iOS Safari
     renders them against a light assumption while the page is dark. */
  color-scheme: light dark;
  --bg: #F2F2F4;
  --surface: #FFFFFF;
  --surface-subtle: #F7F7F9;
  --text-display: #06060A;
  --text-body: #1F1F22;
  --text-muted: #5A5A60;
  /* BOOK-8: ≥ 4.5:1 on --surface (was #8A8A90, ≈3.4:1). */
  --text-subtle: #6B6B72;
  --border: #E4E4E9;
  --border-strong: #D0D0D6;
  --primary: #06060A;
  --primary-hover: #1F1F22;
  /* Text/icon colour that sits ON a --primary surface. Paired with
     --primary so both flip together in dark mode (where --primary
     becomes near-white and this must become near-black). */
  --on-primary: #FFFFFF;
  --accent: #0A6CFF;
  --accent-soft: rgba(10, 108, 255, 0.10);
  --map-ink: #2C2C33;
  --map-water: #DDE3EC;
  --danger: #C53030;
  --radius-xl: 24px;
  --radius-lg: 18px;
  --radius-md: 12px;
  --radius-sm: 8px;
  --shadow-card: 0 1px 2px rgba(0,0,0,0.05), 0 24px 48px -16px rgba(15, 23, 42, 0.12);
  --max-content: 1080px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #08080B;
    --surface: #15151B;
    --surface-subtle: #1C1C24;
    --text-display: #F8F8FB;
    --text-body: #E4E4E9;
    --text-muted: #9A9AA0;
    /* BOOK-8: ≥ 4.5:1 on the dark surface (was #6A6A70, ≈3.5:1). */
    --text-subtle: #9A9AA3;
    --border: #2A2A33;
    --border-strong: #3A3A45;
    --primary: #F8F8FB;
    --primary-hover: #E4E4E9;
    /* On the now near-white --primary, text/icons must be near-black. */
    --on-primary: #08080B;
    --accent: #5BA1FF;
    --accent-soft: rgba(91, 161, 255, 0.16);
    --map-ink: #4A4A55;
    --map-water: #1B2330;
    /* Lift the danger tone so error-banner text clears contrast on the
       dark surface (the light-mode #C53030 is too dark on near-black). */
    --danger: #FF6B6B;
  }
}

html, body { min-height: 100%; }
body {
  margin: 0;
  background: var(--bg);
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Segoe UI", Inter, Roboto, Helvetica, Arial, sans-serif;
  color: var(--text-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  letter-spacing: -0.01em;
  font-feature-settings: "ss01", "cv11";
}

.gv-map {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  width: 100%;
  height: 100%;
  opacity: 0.45;
}
@media (prefers-color-scheme: dark) { .gv-map { opacity: 0.30; } }
@media (prefers-reduced-motion: reduce) { .gv-map { opacity: 0.25; } }

.shell {
  position: relative;
  z-index: 1;
  max-width: var(--max-content);
  margin: 0 auto;
  padding: 24px 24px 80px;
}
@media (min-width: 720px) { .shell { padding: 28px 40px 120px; } }
@media (min-width: 1024px) { .shell { padding: 40px 56px 160px; } }

.topbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px;
  margin-bottom: 56px;
}
.brand-lockup {
  display: flex; align-items: center; gap: 12px;
  text-decoration: none;
  color: inherit;
}
.brand-mark {
  width: 42px; height: 42px;
  display: block;
  object-fit: contain;
  border-radius: 10px;
}
.brand-wordmark {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.025em;
  color: var(--text-display);
  line-height: 1;
}
.brand-wordmark .accent { color: var(--accent); }
.brand-tagline {
  font-size: 13px;
  color: var(--text-muted);
  letter-spacing: 0.02em;
  margin-top: 2px;
}
@media (max-width: 480px) {
  .brand-tagline { display: none; }
}

/* Top nav links — sit on the right of the topbar on desktop,
   wrap below the brand-lockup on small phones. */
.topnav-links {
  display: flex;
  gap: 14px;
  align-items: center;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -0.005em;
}
.topnav-links a {
  color: var(--text-muted);
  text-decoration: none;
  padding: 6px 2px;
  transition: color 160ms ease;
}
.topnav-links a:hover { color: var(--text-display); }
.topnav-links .sep { color: var(--border-strong); user-select: none; }
@media (max-width: 600px) {
  .topbar { flex-wrap: wrap; }
  .topnav-links { width: 100%; justify-content: flex-start; padding-top: 4px; }
}

/* Landing already shows the logo as the hero — hide the small
   brand-lockup in the topbar there so we don't double up.
   Every other page keeps it because that's the only spot
   the brand appears. */
body[data-page="landing"] .brand-lockup { display: none; }
body[data-page="landing"] .topbar { justify-content: flex-end; }

.topnav-copy {
  color: var(--text-subtle);
  user-select: none;
}

/* ─── Portal home tiles ─────────────────────────────────────── */
.portal-tile {
  display: block;
  padding: 22px 20px;
  border-radius: var(--radius-lg);
  background: var(--surface-subtle);
  border: 1px solid var(--border);
  color: var(--text-body);
  transition: background 160ms ease, border-color 160ms ease,
              transform 120ms ease, box-shadow 160ms ease;
}
.portal-tile:hover {
  background: var(--surface);
  border-color: var(--border-strong);
  transform: translateY(-1px);
  box-shadow: 0 8px 24px -10px rgba(15, 23, 42, 0.16);
}
.portal-tile:active { transform: translateY(0); }

.portal-tile-icon {
  width: 44px; height: 44px;
  border-radius: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-display);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 14px;
}
.portal-tile-title {
  font-size: 17px;
  font-weight: 700;
  color: var(--text-display);
  letter-spacing: -0.01em;
  margin-bottom: 4px;
}
.portal-tile-sub {
  font-size: 14px;
  color: var(--text-muted);
  line-height: 1.4;
}

/* Primary tile — accent-tinted, used for the main "Book a pickup"
   call to action so it visually leads the pair. */
.portal-tile-primary {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-primary);
}
.portal-tile-primary .portal-tile-icon {
  background: color-mix(in srgb, var(--on-primary) 10%, transparent);
  border-color: color-mix(in srgb, var(--on-primary) 20%, transparent);
  color: var(--on-primary);
}
.portal-tile-primary .portal-tile-title { color: var(--on-primary); }
.portal-tile-primary .portal-tile-sub {
  color: color-mix(in srgb, var(--on-primary) 72%, transparent);
}
.portal-tile-primary:hover {
  background: var(--primary-hover);
  border-color: var(--primary-hover);
  box-shadow: 0 12px 28px -10px rgba(0, 0, 0, 0.35);
}

@media (max-width: 640px) {
  /* Stack tiles on phones — single column reads better. */
  .card .portal-tile { padding: 18px 16px; }
}

/* ─── Recent bookings row (portal home) ─────────────────────── */
.portal-recent-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  background: var(--surface-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-body);
  transition: background 160ms ease, border-color 160ms ease;
}
.portal-recent-row:hover {
  background: var(--surface);
  border-color: var(--border-strong);
}

/* ─── Same-day cutoff hint (shown on the booking form) ───── */
.cutoff-hint {
  margin-top: 8px;
  padding: 12px 14px;
  border-radius: var(--radius-md);
  background: rgba(10, 108, 255, 0.06);
  border: 1px solid rgba(10, 108, 255, 0.20);
  color: var(--text-body);
  font-size: 14px;
  line-height: 1.5;
}
.cutoff-hint strong { color: var(--text-display); }

/* Blocked state — past the cutoff, the form below is disabled and
   this hint becomes the primary thing on screen. Bigger type, an
   icon flag on the left, and the strong red surface so the customer
   can't miss why they can't submit. */
.cutoff-hint[data-state="blocked"] {
  background: rgba(197, 48, 48, 0.10);
  border-color: rgba(197, 48, 48, 0.45);
  border-width: 1.5px;
  color: #7A1818;
  padding: 18px 18px 18px 56px;
  font-size: 15px;
  position: relative;
  box-shadow: 0 6px 18px -8px rgba(197, 48, 48, 0.30);
}
.cutoff-hint[data-state="blocked"]::before {
  content: "";
  position: absolute;
  top: 18px;
  left: 18px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: #C53030 url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><line x1='12' y1='8' x2='12' y2='13'/><circle cx='12' cy='17' r='1.2' fill='white' stroke='none'/></svg>") no-repeat center center;
}
.cutoff-hint[data-state="blocked"] strong { color: #7A1818; }

/* ─── Disabled-form state while past cutoff ──────────────── */
/* The pickup date input + its label + the cutoff hint stay
   interactive; everything else inside the form is dimmed and
   non-interactive so the customer's only option is to change the
   date (or call us). The actual submit button is also disabled at
   the attribute level by the JS — this rule is the visual cue. */
form[data-cutoff-blocked="true"] > *:not(label[for="pickup_date"]):not(#pickup_date):not(.cutoff-hint) {
  opacity: 0.45;
  filter: grayscale(0.4);
  pointer-events: none;
}
form[data-cutoff-blocked="true"] .button[type="submit"] {
  background: var(--text-subtle);
  cursor: not-allowed;
}
form[data-cutoff-blocked="true"] .button[type="submit"]:hover {
  background: var(--text-subtle);
}

/* ─── Status pills (portal home + history) ──────────────────── */
.status-pill {
  display: inline-block;
  padding: 4px 12px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.2px;
}
.status-pending     { background: #FFF5E0; color: #7A5A10; }
.status-accepted    { background: #E8F1FF; color: #1A4480; }
.status-scheduled   { background: #E8F1FF; color: #1A4480; }
.status-in_progress { background: #E6F6EA; color: #0F6225; }
.status-delivered   { background: #E6F6EA; color: #0F6225; }
.status-declined    { background: #FFE5E5; color: #A13030; }
.status-cancelled   { background: #F0F0F0; color: #666666; }

/* Hero-sized brand mark on the landing page. Lifts the logo from
   "small accent" to "this is the brand". */
.hero-logo {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.hero-mark {
  width: 100%;
  max-width: 360px;
  height: auto;
  display: block;
  margin: 0 0 28px 0;
  border-radius: 24px;
}
@media (min-width: 960px) {
  .hero-mark { max-width: 420px; }
}
@media (max-width: 600px) {
  .hero-mark { max-width: 260px; }
}

/* Shown in place of .hero-mark when the tenant hasn't uploaded a logo.
   Never fall back to another company's mark — the company's own name,
   set as display type, is the honest alternative. Sized to hold the
   same visual weight in the hero as the logo it replaces. */
.hero-wordmark {
  font-size: 44px;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.05;
  color: var(--text-display);
  margin: 0 0 28px 0;
  max-width: 420px;
}
@media (min-width: 960px) {
  .hero-wordmark { font-size: 52px; }
}
@media (max-width: 600px) {
  .hero-wordmark { font-size: 34px; }
}

/* Address autocomplete dropdown. Positioned inside the address
   field's wrapper, sits just below the input. */
.address-autocomplete {
  position: relative;
}
.ac-results {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 30;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: 0 12px 32px -8px rgba(15, 23, 42, 0.18);
  max-height: 280px;
  overflow-y: auto;
  padding: 4px;
  display: none;
}
.ac-results[data-open="true"] { display: block; }
.ac-results button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 10px 12px;
  border: none;
  background: transparent;
  border-radius: 8px;
  cursor: pointer;
  font-family: inherit;
  font-size: 14px;
  color: var(--text-body);
}
.ac-results button:hover,
.ac-results button[data-active="true"] {
  background: var(--surface-subtle);
}
.ac-results .ac-main {
  font-weight: 600;
  color: var(--text-display);
}
.ac-results .ac-secondary {
  font-size: 13px;
  color: var(--text-muted);
  margin-top: 2px;
}
.ac-results .ac-empty {
  padding: 10px 12px;
  font-size: 13px;
  color: var(--text-subtle);
}

.layout {
  display: grid;
  gap: 32px;
  align-items: start;
}
@media (min-width: 960px) {
  .layout {
    grid-template-columns: minmax(0, 1.05fr) minmax(380px, 0.95fr);
    gap: 80px;
  }
}

.hero h1 {
  font-size: clamp(40px, 5.5vw, 64px);
  line-height: 1.04;
  margin: 0 0 18px 0;
  color: var(--text-display);
  font-weight: 700;
  letter-spacing: -0.035em;
  text-wrap: balance;
}
.hero .tagline {
  font-size: clamp(17px, 1.6vw, 19px);
  color: var(--text-body);
  margin: 0;
  max-width: 30em;
  line-height: 1.55;
}
.hero-meta {
  display: flex; gap: 18px; flex-wrap: wrap;
  margin-top: 28px;
  font-size: 14px;
  color: var(--text-muted);
}
.hero-meta .dot {
  display: inline-block; width: 6px; height: 6px;
  background: var(--accent); border-radius: 50%;
  margin-right: 8px; vertical-align: middle;
}

.card {
  background: var(--surface);
  border-radius: var(--radius-xl);
  padding: 32px 28px;
  box-shadow: var(--shadow-card);
  border: 1px solid var(--border);
}
@media (min-width: 720px) { .card { padding: 40px 36px; } }

.card h2 {
  font-size: 22px;
  margin: 0 0 4px 0;
  color: var(--text-display);
  font-weight: 700;
  letter-spacing: -0.018em;
}
.card .lede {
  font-size: 14px;
  color: var(--text-muted);
  margin: 0 0 24px 0;
}

.float-field,
.card .float-field {
  position: relative;
  margin-bottom: 14px;
}
.float-field input,
.card .float-field input {
  width: 100%;
  /* Top padding leaves space for the floated label sitting at the
     top of the field. Bottom padding balances visually. */
  padding: 28px 16px 12px 16px;
  font-size: 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-subtle);
  color: var(--text-body);
  font-family: inherit;
  transition: border-color 160ms ease, background 160ms ease, box-shadow 160ms ease;
  line-height: 1.25;
}
.float-field input::placeholder,
.card .float-field input::placeholder { color: transparent; }
.float-field input:focus,
.card .float-field input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--surface);
  box-shadow: 0 0 0 4px var(--accent-soft);
}
.float-field label,
.card .float-field label {
  position: absolute;
  top: 20px; left: 16px;
  font-size: 15px;
  color: var(--text-muted);
  pointer-events: none;
  transform-origin: left top;
  transition: transform 160ms ease, color 160ms ease;
  letter-spacing: 0;
  font-weight: 500;
  text-transform: none;
  /* Reset the .card label baseline so it doesn't appear as the
     small uppercase mini-label used for non-floating fields. */
  display: block;
  margin: 0;
}
/* Focus-while-empty: label floats up so the field still labels
   itself while you're typing the first character. */
.float-field input:focus + label,
.card .float-field input:focus + label {
  transform: translateY(-14px) scale(0.7);
  color: var(--accent);
}

/* As soon as there's any typed value (or Chrome autofills),
   the label fades out completely — the field's own contents
   tell the user what it is, so the floated mini-label is
   redundant. Transitions match the focus animation so the
   handoff between focused-empty → typing is smooth. */
.float-field input:not(:placeholder-shown) + label,
.float-field input:-webkit-autofill + label,
.float-field input:autofill + label,
.card .float-field input:not(:placeholder-shown) + label,
.card .float-field input:-webkit-autofill + label,
.card .float-field input:autofill + label {
  opacity: 0;
  transform: translateY(-14px) scale(0.7);
  pointer-events: none;
}

/* Smooth fade — opacity transition added on top of the existing
   transform/color transitions. */
.float-field label,
.card .float-field label {
  transition: transform 160ms ease, color 160ms ease, opacity 160ms ease;
}

/* Chrome paints autofilled inputs with a yellow background and forces
   the text colour — override both so our subtle surface tone is kept. */
.float-field input:-webkit-autofill,
.card .float-field input:-webkit-autofill {
  -webkit-box-shadow: 0 0 0 1000px var(--surface-subtle) inset;
  -webkit-text-fill-color: var(--text-body);
  caret-color: var(--text-body);
  transition: background-color 9999s ease-out, color 9999s ease-out;
}

.help-text {
  display: block; margin: 6px 4px 0;
  font-size: 13px; color: var(--text-subtle);
}

/* BOOK-3 — the `hidden` attribute must win over any display rule.
   `.button { display: inline-flex }` below out-specifies the browser's
   own `[hidden] { display: none }`, so hiding a button by setting
   `hidden` silently did nothing — the Remove control stayed visible on
   a booking's only delivery, offering to remove the one thing that
   cannot be removed. Caught by driving the form, not by a test: the
   tests assert what the form POSTs, and this changes nothing about
   that. */
[hidden] { display: none !important; }

.button {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  gap: 8px;
  width: 100%;
  background: var(--primary);
  color: var(--on-primary);
  text-decoration: none;
  padding: 16px 24px;
  border-radius: var(--radius-md);
  font-size: 16px;
  font-weight: 600;
  border: none;
  cursor: pointer;
  margin-top: 14px;
  letter-spacing: -0.005em;
  transition: background 160ms ease, transform 120ms ease, box-shadow 160ms ease;
}
.button:hover { background: var(--primary-hover); }
.button:active { transform: scale(0.985); }
.button:disabled { opacity: 0.55; cursor: not-allowed; }
.button[data-busy="true"] .button-label { opacity: 0; }
.button[data-busy="true"]::after {
  content: "";
  position: absolute;
  width: 18px; height: 18px;
  border: 2.5px solid color-mix(in srgb, var(--on-primary) 35%, transparent);
  border-top-color: var(--on-primary);
  border-radius: 50%;
  animation: spin 700ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .button[data-busy="true"]::after { animation: none; }
}

.segmented {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  padding: 4px;
  background: var(--surface-subtle);
  border: 1px solid var(--border);
  border-radius: 12px;
  margin-bottom: 24px;
}
.segmented::before {
  content: "";
  position: absolute;
  top: 4px; bottom: 4px;
  width: calc(50% - 4px);
  border-radius: 8px;
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(0,0,0,0.08), 0 2px 4px rgba(0,0,0,0.04);
  transition: transform 280ms cubic-bezier(0.22, 0.78, 0.36, 1);
  transform: translateX(0);
  left: 4px;
}
.segmented[data-active="1"]::before { transform: translateX(100%); }
@media (prefers-reduced-motion: reduce) { .segmented::before { transition: none; } }
.segmented .tab {
  position: relative; z-index: 1;
  padding: 10px 16px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-muted);
  background: transparent;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  transition: color 200ms ease;
  letter-spacing: -0.005em;
}
.segmented .tab[aria-selected="true"] { color: var(--text-display); }

.welcome-back {
  display: flex; gap: 12px; align-items: center;
  padding: 12px 14px;
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
  border-radius: var(--radius-md);
  margin-bottom: 20px;
  font-size: 14px;
  color: var(--text-display);
}
.welcome-back .avatar {
  width: 36px; height: 36px; border-radius: 50%;
  background: linear-gradient(135deg, var(--accent), color-mix(in srgb, var(--accent), #06060A 30%));
  color: #FFFFFF;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 14px;
  letter-spacing: 0.5px;
  flex-shrink: 0;
}
.welcome-back .email { font-weight: 600; }
.welcome-back .meta { font-size: 11px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.06em; }
.welcome-back .clear {
  margin-left: auto;
  font-size: 13px; color: var(--text-muted);
  background: transparent; border: none; cursor: pointer;
  padding: 6px 10px; border-radius: 6px;
}
.welcome-back .clear:hover { background: rgba(0,0,0,0.05); }

.footer {
  margin-top: 64px;
  font-size: 13px;
  color: var(--text-subtle);
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  align-items: center;
}
.footer a { color: var(--text-muted); text-decoration: none; }
.footer a:hover { color: var(--text-display); }
.footer .sep { color: var(--border-strong); }

.error-banner {
  padding: 12px 14px;
  background: rgba(197, 48, 48, 0.08);
  border: 1px solid rgba(197, 48, 48, 0.22);
  color: var(--danger);
  border-radius: var(--radius-md);
  margin-bottom: 16px;
  font-size: 14px;
}
/* Auto-hide the banner when the server didn't pass any text. Uses
   :empty pseudo so the template doesn't need an {% if %} branch. */
.error-banner:empty { display: none; }

/* -------------------------------------------------------------------
   Baseline form-element styling inside .card
   The portal + history forms use bare <label>/<input>/<select>/<textarea>
   markup (no .float-field wrapper). These rules give them the same
   Apple-tier look without requiring template changes.
   ------------------------------------------------------------------- */
.card label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: var(--text-muted);
  margin: 0 0 8px 2px;
}
.card input[type="text"],
.card input[type="email"],
.card input[type="password"],
.card input[type="number"],
.card input[type="date"],
.card input[type="tel"],
.card input[type="search"],
.card select,
.card textarea {
  display: block;
  width: 100%;
  padding: 13px 14px;
  font-size: 16px;
  font-family: inherit;
  color: var(--text-body);
  background: var(--surface-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  line-height: 1.3;
  transition: border-color 160ms ease, background 160ms ease, box-shadow 160ms ease;
  appearance: none;
  -webkit-appearance: none;
}
.card select {
  /* Subtle chevron — inline SVG so no extra asset request */
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1.5l5 5 5-5' fill='none' stroke='%235A5A60' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 12px 8px;
  padding-right: 38px;
  cursor: pointer;
}
.card textarea {
  resize: vertical;
  min-height: 80px;
  line-height: 1.45;
}
.card input:focus,
.card select:focus,
.card textarea:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--surface);
  box-shadow: 0 0 0 4px var(--accent-soft);
}

/* Headline + lede inside any .card (portal "Book a pickup" etc.) */
.card h1 {
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text-display);
  margin: 0 0 6px 0;
  line-height: 1.15;
}
.card .lede {
  font-size: 15px;
  color: var(--text-muted);
  margin: 0 0 22px 0;
  line-height: 1.5;
}

/* Pill list (account-picker, history rows) */
.pill-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.pill-list li { margin: 0; }
.pill {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 14px 16px;
  background: var(--surface-subtle) !important;
  border: 1px solid var(--border) !important;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease, transform 120ms ease;
  font-size: 15px;
  color: var(--text-body);
  text-align: left;
}
.pill:hover {
  background: var(--surface) !important;
  border-color: var(--border-strong) !important;
}
.pill:active { transform: scale(0.995); }
.pill-title {
  font-weight: 600;
  color: var(--text-display);
  font-size: 15px;
  letter-spacing: -0.005em;
}
.pill-sub {
  font-size: 13px;
  color: var(--text-muted);
  margin-top: 2px;
}

/* Secondary (pill-shaped, lighter) button — for History / Sign out */
.button.secondary {
  background: var(--surface-subtle);
  color: var(--text-display);
  border: 1px solid var(--border);
}
.button.secondary:hover {
  background: var(--surface);
  border-color: var(--border-strong);
}

/* ─── Dark-mode component overrides ──────────────────────────────
   Rules whose colours are hardcoded (not token-driven) and need an
   explicit dark variant. Kept in one block so the dark theme has a
   single place to audit. */
@media (prefers-color-scheme: dark) {
  /* Blocked same-day cutoff — the light theme uses dark-red ink on a
     translucent-red panel; over the dark surface that composites to
     near-black-on-near-black. Lift the ink to a light red. */
  .cutoff-hint[data-state="blocked"] { color: #FFB4B4; }
  .cutoff-hint[data-state="blocked"] strong { color: #FFC9C9; }

  /* Status pills carry fixed light pastels. Give them translucent
     tinted fills with light ink so they read on the dark card. */
  .status-pending     { background: rgba(255, 193, 60, 0.16);  color: #FFD98A; }
  .status-accepted,
  .status-scheduled   { background: rgba(91, 161, 255, 0.16);  color: #A9CCFF; }
  .status-in_progress,
  .status-delivered   { background: rgba(45, 200, 120, 0.16);  color: #86E6AC; }
  .status-declined    { background: rgba(255, 107, 107, 0.16); color: #FFB0B0; }
  .status-cancelled   { background: rgba(150, 150, 160, 0.16); color: #B8B8C0; }

  /* Clear-button hover was a black wash — invisible on dark. */
  .welcome-back .clear:hover { background: rgba(255, 255, 255, 0.06); }
}

/* ─── Booking history list ───────────────────────────────────────
   Replaces the old fixed-width <table> (which cramped on phones and
   hardcoded light hex). Token-driven, so it themes for free, and it
   stacks below 560px. Tap targets meet the 44px touch minimum. */
.history-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 10px;
}
.history-item {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 14px 16px;
  background: var(--surface-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.history-route {
  font-weight: 600; color: var(--text-display); font-size: 15px;
  letter-spacing: -0.005em;
}
.history-meta { font-size: 13px; color: var(--text-muted); margin-top: 2px; }
.history-side { display: flex; align-items: center; gap: 12px; flex-shrink: 0; }
.history-actions { display: flex; gap: 8px; }
.history-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 8px 16px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text-display);
  font-size: 14px; font-weight: 500; text-decoration: none; cursor: pointer;
  font-family: inherit;
  transition: border-color 160ms ease, background 160ms ease;
}
.history-btn:hover { border-color: var(--border-strong); background: var(--surface-subtle); }
.history-btn-danger {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 40%, var(--border));
}
.history-empty { padding: 56px 20px; text-align: center; color: var(--text-muted); }
.history-empty-title { font-size: 18px; color: var(--text-display); margin-bottom: 8px; }
@media (max-width: 560px) {
  .history-item { flex-direction: column; align-items: stretch; gap: 12px; }
  /* NB-223 — the status pill and three buttons do not fit a phone. At
     390 px the row is about 250 px wide inside the card and Cancel +
     Track + Book again measure about 272 px on their own, so with the
     pill beside them "Book again" was painted past the right edge of
     the white panel and off the screen.
     Nothing here let a line break: .history-side did not wrap, so the
     actions could not leave the pill's line, and .history-actions did
     not wrap, so the buttons could not leave each other's. Both may
     now, and the actions claim a whole line, so the pill sits above
     them and they fill the width below, taking a second line when they
     need one. No width is named, so a longer status label ("Pending
     review" already wraps at 390 px) or a wider font wraps rather than
     overflows. Nothing changes at 561 px and up, where all four fit
     across. The 44px tap minimum on .history-btn is untouched on
     purpose — shrinking the buttons to fit is the fix this must not
     become. */
  .history-side { justify-content: space-between; flex-wrap: wrap; }
  .history-actions { flex: 1 1 100%; flex-wrap: wrap; }
}


/* -------------------------------------------------------------------
   BOOK-8 — booking wizard, "Request sent", portal tiles
   ------------------------------------------------------------------- */
.wizard-card { max-width: 640px; margin: 0 auto; }
.wizard-topbar {
  display: flex; justify-content: space-between; align-items: center;
  gap: 12px; margin-bottom: 20px;
}
.eyebrow {
  font-size: 13px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.8px; color: var(--text-muted);
}
/* NB-484 — the home header was inline styles with no wrap, so a long name
   (or just "Halden Engineering" at 320 px) pushed Account and Sign out
   past the card. It now shares .wizard-topbar with the wizard and account
   bars: one rule for all three, wrapped under 560 px below. The name block
   may shrink and a no-space name breaks inside the card; the buttons never
   shrink (NB-223) and never break mid-label. Home keeps its own 28px. */
.wizard-topbar-home { margin-bottom: 28px; }
.wizard-id { min-width: 0; }
.wizard-actions { display: flex; gap: 8px; }
.wizard-contact { font-size: 18px; font-weight: 700; color: var(--text-display); overflow-wrap: anywhere; }
.button.compact {
  width: auto; margin: 0; padding: 10px 14px; font-size: 14px;
  min-height: 44px; white-space: nowrap;
}

.wizard-progress {
  display: flex; gap: 6px; list-style: none; padding: 0; margin: 0 0 22px 0;
}
.wizard-progress-item {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px;
  font-size: 13px; font-weight: 600; color: var(--text-subtle);
}
.wizard-progress-dot {
  display: block; width: 100%; height: 6px; border-radius: 3px;
  background: var(--border);
}
.wizard-progress-item[data-state="done"] .wizard-progress-dot { background: var(--primary); }
.wizard-progress-item[data-state="done"] { color: var(--text-muted); }
.wizard-progress-item[data-state="current"] .wizard-progress-dot { background: var(--accent); }
.wizard-progress-item[data-state="current"] { color: var(--text-display); }

.wizard-title {
  font-size: 24px; font-weight: 700; letter-spacing: -0.02em;
  color: var(--text-display); margin: 0 0 16px 0; line-height: 1.2;
}
.wizard-block {
  padding: 16px; background: var(--surface-subtle);
  border: 1px solid var(--border); border-radius: var(--radius-md);
}
.wizard-block input[type="text"], .wizard-block input[type="tel"],
.wizard-block input[type="number"], .wizard-block select { background: var(--surface); }
.field-help { margin-top: 6px; font-size: 14px; color: var(--text-muted); line-height: 1.5; }
.field-error {
  margin-top: 8px; padding: 10px 12px; font-size: 14px; font-weight: 600;
  color: var(--danger); background: rgba(197, 48, 48, 0.08);
  border: 1px solid rgba(197, 48, 48, 0.22); border-radius: var(--radius-sm);
}
.card input[aria-invalid="true"], .card select[aria-invalid="true"] { border-color: var(--danger); }
.weight-math {
  margin-top: 8px; padding: 8px 12px; font-size: 14px; font-weight: 600;
  color: var(--text-display); background: var(--surface);
  border: 1px solid var(--border); border-radius: 8px;
}
.dims-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; margin-top: 14px; }
.link-button {
  display: inline-block; background: none; border: none; padding: 0; margin-top: 14px;
  font-size: 14px; font-weight: 600; color: var(--text-muted);
  text-decoration: underline; cursor: pointer; font-family: inherit;
}
.link-button.danger { color: var(--danger); }

/* The row wraps at every width, not only on a phone: NB-350 put four
   buttons on the "What's going" step, and at full card width "Back",
   "+ Another delivery from here", "+ Pick up from somewhere else" and
   "Review" measure more than the card, so a row that could not wrap
   painted "Review" past the card's right edge. The main button takes
   whatever is left of its line instead of a fixed 100%, which on its
   own would push it onto a line of its own on every step. */
.wizard-nav { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 20px; }
.wizard-nav .button { margin: 0; flex: 1 1 auto; width: auto; }
.wizard-nav .button.secondary { flex: 0 0 auto; padding-left: 20px; padding-right: 20px; white-space: nowrap; }

.summary-box { border: 1px solid var(--border); border-radius: var(--radius-md); overflow: hidden; }
.summary-row {
  display: flex; justify-content: space-between; gap: 16px; align-items: flex-start;
  padding: 12px 16px; border-bottom: 1px solid var(--border);
  width: 100%; text-align: left; background: var(--surface); font-family: inherit;
}
.summary-row:last-child { border-bottom: none; }
.summary-key { font-size: 14px; color: var(--text-muted); flex-shrink: 0; width: 96px; padding-top: 2px; }
.summary-val { flex: 1; min-width: 0; font-size: 16px; color: var(--text-display); overflow-wrap: break-word; }
.summary-sub { font-size: 14px; color: var(--text-muted); margin-top: 2px; }
.summary-missing { color: var(--danger); font-weight: 600; }
.summary-link { border: none; cursor: pointer; }
.summary-link:hover { background: var(--surface-subtle); }
.summary-link:focus-visible { outline: 3px solid var(--accent-soft); outline-offset: -3px; }
.summary-change { font-size: 14px; font-weight: 600; color: var(--accent); flex-shrink: 0; padding-top: 2px; }
.summary-row-actions { padding: 0 16px 10px; border-bottom: 1px solid var(--border); background: var(--surface); }
.summary-row-actions .link-button { margin-top: 0; font-size: 14px; }

.sent-tick {
  width: 64px; height: 64px; margin: 0 auto; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: #10B981; color: #fff;
}

/* NB-351 — the way out of an added delivery or pickup, on its own screen. */
.wizard-step-actions { margin-top: 14px; }
.wizard-step-actions .link-button { margin-top: 0; font-size: 16px; }

/* NB-350 — one section per booking on Check and send when there are two
   or more; the date stays one shared row above them. */
.review-booking { margin-top: 20px; }
.review-booking-head {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  margin-bottom: 8px;
}
.review-booking-label { font-size: 16px; font-weight: 700; color: var(--text-display); }
.review-booking-head .link-button { margin-top: 0; font-size: 16px; }

/* NB-348 — "Your usual places", above the Business box on a delivery. */
.usual-places { margin-bottom: 16px; }
.usual-places-label { font-size: 14px; font-weight: 600; color: var(--text-muted); margin-bottom: 8px; }
.usual-places-list { display: flex; flex-wrap: wrap; gap: 8px; }
.usual-place {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  max-width: 100%; padding: 10px 14px; min-height: 44px;
  border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--surface); font-family: inherit; text-align: left; cursor: pointer;
}
.usual-place:hover { background: var(--surface-subtle); }
.usual-place:focus-visible { outline: 3px solid var(--accent-soft); outline-offset: 1px; }
.usual-place-name { font-size: 16px; font-weight: 600; color: var(--text-display); overflow-wrap: anywhere; }
.usual-place-sub { font-size: 14px; color: var(--text-muted); }

/* NB-350 / NB-351 — one box per booking on the pages after a send of two
   or more: green "Sent", amber "Not sent". */
.send-result {
  margin-top: 16px; padding: 14px 16px; border-radius: var(--radius-md);
  border: 1.5px solid var(--border);
}
.send-result[data-result="sent"] { border-color: rgba(16, 185, 129, 0.55); background: rgba(16, 185, 129, 0.06); }
.send-result[data-result="not-sent"] { border-color: rgba(217, 119, 6, 0.6); background: rgba(217, 119, 6, 0.08); }
.send-result-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px; font-size: 16px; color: var(--text-display); }
.send-result-label {
  display: inline-block; padding: 2px 10px; border-radius: 999px;
  font-size: 14px; font-weight: 700; color: #fff;
}
.send-result[data-result="sent"] .send-result-label { background: #0E8A63; }
.send-result[data-result="sent"] .send-result-label::before { content: "\2713\00a0"; }
.send-result[data-result="not-sent"] .send-result-label { background: #B45309; }
.send-result-reason { margin: 10px 0 0; font-size: 16px; color: var(--text-body); line-height: 1.5; }

.portal-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 24px; }

@media (max-width: 560px) {
  .portal-tiles { grid-template-columns: 1fr; }
  .dims-grid { grid-template-columns: 1fr; }
  .wizard-nav { flex-wrap: wrap; }
  .wizard-nav .button { flex: 1 1 100%; }
  .wizard-topbar { align-items: flex-start; flex-wrap: wrap; }
  /* NB-218 — label | value | "Change", three across, does not fit a
     phone. At 390 px the fixed label and the Change link left the value
     about 110 px wide, so a street address broke one word per line
     ("45 / Numurkah / Rd,") and a phone number split in two — on the one
     screen that asks the customer to check their own details.
     Below 560 px the label and Change share the first line and the value
     takes the full width underneath. Nothing changes at 561 px and up,
     which is every tablet and desktop. The rows on "Request sent"
     (booking/views.py _BOOKING_SENT_BODY) carry the same classes and get
     the same stacking; it is one rule, not two. */
  .summary-row { flex-wrap: wrap; row-gap: 4px; }
  .summary-key { width: auto; }
  .summary-change { order: 2; }
  .summary-val { order: 3; flex: 1 0 100%; }
}
