/* ===========================================================================
   WE Safari Africa Shuttle & Tours — Design System
   ---------------------------------------------------------------------------
   One stylesheet for the whole product: the staff app, the public booking page
   and the printed quote/invoice/voucher. Custom properties in :root are the
   only place a colour is decided.

   Built mobile-first in intent even though the rules read desktop-first: the
   people using this are standing at a counter in Marloth Park with a phone, or
   sitting with a tablet, far more often than they are at a desk. Every
   breakpoint below exists because something was uncomfortable to tap.

   Palette is the bushveld: deep acacia green for structure, a sunset amber for
   anything the user acts on, savanna gold for accents.
   =========================================================================== */
:root{
  --bush-900:#10231a; --bush-800:#16301f; --bush-700:#1f4430; --bush-600:#2c5c41;
  --brand:#b8621b; --brand-dark:#8f4a12; --brand-soft:#fdf0e3;
  --gold:#d4a03c; --teal:#0f7d72;
  --amber:#c2760a; --red:#c0392b; --green:#1f7a44; --purple:#6d43b8;
  --ink:#14201c; --muted:#5f6f68; --line:#e2e8e3; --line-2:#eef2ef;
  --bg:#f6f7f4; --card:#ffffff; --field:#ffffff;
  --shadow-sm:0 1px 2px rgba(16,35,26,.06),0 1px 3px rgba(16,35,26,.08);
  --shadow-md:0 4px 12px rgba(16,35,26,.09),0 2px 4px rgba(16,35,26,.04);
  --shadow-lg:0 20px 40px -12px rgba(16,35,26,.28);
  --radius:14px; --radius-sm:9px; --radius-lg:20px;
  --font:"Inter","Segoe UI",system-ui,-apple-system,Roboto,Arial,sans-serif;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0;-webkit-text-size-adjust:100%;text-size-adjust:100%}
body{
  font-family:var(--font); color:var(--ink); background:var(--bg);
  font-size:15px; line-height:1.5; -webkit-font-smoothing:antialiased;
}
h1,h2,h3,h4{margin:0;font-weight:700;letter-spacing:-.01em;color:var(--ink)}
a{color:var(--brand-dark);text-decoration:none}
button{font-family:inherit}
img,svg,canvas,video{max-width:100%}
/* Nothing may force the page wider than the screen. min-width:0 lets grid and
   flex children shrink so their text wraps instead of pushing the layout out. */
.grid>*,.form-grid>*,.wizard>*,.tour-grid>*,.pipeline>*{min-width:0}
p,li,td,th{overflow-wrap:break-word}
.hidden{display:none !important}
.mono{font-variant-numeric:tabular-nums;font-feature-settings:"tnum"}
.muted{color:var(--muted)}
.right{text-align:right}
.nowrap{white-space:nowrap}

/* ---------- Buttons ---------- */
.btn{display:inline-flex;align-items:center;gap:.5rem;justify-content:center;
  border:1px solid transparent;border-radius:var(--radius-sm);padding:.62rem 1.05rem;
  font-weight:600;font-size:.92rem;cursor:pointer;transition:.15s;white-space:nowrap;
  background:var(--brand);color:#fff;text-decoration:none}
.btn:hover{background:var(--brand-dark)}
.btn:active{transform:translateY(1px)}
.btn:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.btn.secondary{background:#fff;color:var(--ink);border-color:var(--line)}
.btn.secondary:hover{background:#f4f7f4;border-color:#cbd6ce}
.btn.ghost{background:transparent;color:var(--muted)}
.btn.ghost:hover{background:var(--line-2);color:var(--ink)}
.btn.danger{background:var(--red)}
.btn.danger:hover{background:#96271c}
.btn.success{background:var(--green)}
.btn.success:hover{background:#155f34}
.btn.dark{background:var(--bush-800)}
.btn.dark:hover{background:var(--bush-900)}
.btn.lg{padding:.85rem 1.4rem;font-size:1rem}
.btn.sm{padding:.4rem .75rem;font-size:.82rem}
/* For actions that sit INSIDE a row or card rather than on the page — the
   Change and Delete pair on a booking. Small enough not to compete with the
   guest's name for attention; the coarse-pointer block below still gives it a
   real target on the tablet. */
.btn.xs{padding:.28rem .55rem;font-size:.72rem;border-radius:8px;gap:.25rem}
.btn.block{width:100%}
.btn:disabled{opacity:.5;cursor:not-allowed}
.btn-row{display:flex;gap:.6rem;flex-wrap:wrap}
.btn-row.end{justify-content:flex-end}

/* ---------- Badges / pills ---------- */
.badge{display:inline-flex;align-items:center;gap:.35rem;font-size:.72rem;font-weight:700;
  padding:.22rem .6rem;border-radius:999px;letter-spacing:.02em;text-transform:uppercase;
  background:var(--line-2);color:var(--muted);white-space:nowrap}
.badge.enquiry{background:#eef2f7;color:#4a5b6e}
.badge.quoted{background:#fff4e2;color:var(--amber)}
.badge.confirmed{background:var(--brand-soft);color:var(--brand-dark)}
.badge.paid{background:#e6f5ec;color:var(--green)}
.badge.completed{background:#e4f0ec;color:var(--teal)}
.badge.cancelled{background:#fdecea;color:var(--red)}
.badge.tour{background:#eef6ee;color:var(--green)}
.badge.transfer{background:#eaf1fa;color:#2b5f9e}
.badge.shared_shuttle{background:#f4eefb;color:var(--purple)}
.badge.warn{background:#fdecea;color:var(--red)}
.badge.ok{background:#e6f5ec;color:var(--green)}
.badge.gold{background:#fbf3df;color:#8a6212}

/* ---------- App shell ---------- */
/* 300px, not 252. The menu is three levels deep in places and its labels are
   deliberately the words the office says out loud — "Lodge & agency bookings",
   "Settings & who can sign in", "Admin — who can sign in". Both .lbl rules
   ellipsise on overflow, so a narrow rail silently truncated exactly the long,
   specific names that make the menu readable. Widen the rail rather than
   shorten the words. */
.app{display:grid;grid-template-columns:1fr;min-height:100vh}
/* The rail is a hamburger-triggered drawer at every width now (see .hamburger
   and openSidebar()/closeSidebar() in app.js), not just on tablets — so this
   is unconditional rather than sitting behind a max-width media query. */
.sidebar{background:linear-gradient(180deg,var(--bush-900),var(--bush-700));
  color:#cfdcd3;display:flex;flex-direction:column;
  position:fixed;left:0;top:0;bottom:0;width:min(300px,86vw);z-index:120;
  transform:translateX(-100%);transition:transform .25s ease;box-shadow:var(--shadow-lg)}
.sidebar.open{transform:none}
.brand{display:flex;align-items:center;gap:.7rem;padding:1.15rem 1.25rem;
  border-bottom:1px solid rgba(255,255,255,.08)}
.brand b{color:#fff;font-size:.98rem;display:block;line-height:1.15}
.brand span{font-size:.71rem;color:#93ac9c;display:block;line-height:1.3}
/* ---- Brand mark ----
   The official logo is SQUARE with a white background, so every slot below is a
   1:1 box sized on height, with `object-fit:contain` as a safety net if a
   differently-proportioned file is ever dropped in. On the dark green sidebar
   and the dark document band it sits on a white rounded chip — a white-
   background JPEG floating on dark green reads as a printing error.

   The mark already contains the words "WE Safari Africa", but they are
   illegible below roughly 90px, which is why the sidebar and the documents
   still set the name in live text beside it. */
.brand .logo-img{height:46px;width:46px;object-fit:contain;
  background:#fff;border-radius:10px;padding:3px;flex:none;box-shadow:var(--shadow-sm)}
/* The wordmark wraps rather than truncating — "WE Safari A…" is worse than two
   tidy lines, and the sidebar has the vertical room. */
.brand > div{min-width:0}
.brand b{line-height:1.2}
.topbar-logo{display:block;height:34px;width:34px;object-fit:contain;flex:none;
  border-radius:8px}
/* Big enough on the login card for the mark's own lettering to be readable —
   below about 140px the "WE Safari Africa" inside the artwork turns to mush.
   ⚠ 140 IS THE FLOOR, and it is sitting on it. Trimmed from 156 when the card
     went two-column (see .login-card) — the left rail is 250px wide, so this
     is as large as it can be and still leave the wordmark under it room to
     breathe. Anything smaller has to lose the artwork, not shrink it. */
.login-logo{height:140px;width:140px;object-fit:contain;display:block;border-radius:16px}
/* THE TOPBAR BACK/FORWARD ARROWS (6 Sep 2026) — one class, both buttons,
   right where a title sits in a native app, because that's the one spot a
   "go back" (and, once that landed well, "go forward") control reads as
   obvious without a label next to it. Disabled rather than removed on the
   screen where either has nowhere to go: a button that vanishes would
   shift everything next to it sideways every time the app decides there's
   nothing behind (or ahead of) you. */
.topbar-back{display:flex;align-items:center;justify-content:center;flex:none;
  width:34px;height:34px;border-radius:50%;border:1px solid var(--line);
  background:#fff;color:var(--ink);cursor:pointer;transition:.15s}
.topbar-back:hover{background:var(--fill-1);border-color:var(--line-2)}
.topbar-back:active{transform:scale(.94)}
.topbar-back svg{width:20px;height:20px}
.topbar-back.is-disabled{opacity:.35;cursor:default;pointer-events:none}
.nav{padding:.75rem .6rem;flex:1;overflow-y:auto;
  scrollbar-width:thin;scrollbar-color:var(--brand) rgba(255,255,255,.08)}
.nav::-webkit-scrollbar{width:8px}
.nav::-webkit-scrollbar-track{background:rgba(255,255,255,.06)}
.nav::-webkit-scrollbar-thumb{background:var(--brand);border-radius:8px}
.nav::-webkit-scrollbar-thumb:hover{background:var(--brand-dark)}
.nav a{display:flex;align-items:center;gap:.75rem;color:#aec1b5;padding:.62rem .8rem;
  border-radius:10px;font-weight:600;font-size:.9rem;margin-bottom:2px;cursor:pointer}
.nav a svg{width:18px;height:18px;flex:none;opacity:.85}
.nav a:hover{background:rgba(255,255,255,.07);color:#fff}
.nav a.active{background:var(--brand);color:#fff}
.nav a .count{margin-left:auto;background:rgba(255,255,255,.14);color:#fff;font-size:.68rem;
  font-weight:800;padding:.1rem .42rem;border-radius:999px;min-width:20px;text-align:center}
.nav a.active .count{background:rgba(0,0,0,.22)}
.nav a .lbl{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nav .section{font-size:.67rem;text-transform:uppercase;letter-spacing:.08em;color:#77907f;
  padding:.9rem .9rem .4rem}

/* ---- Collapsible menu groups (see the navigation note in app.js) ----
   Thirty links at once is not a menu, it is a wall. Only the group being
   worked in is open; the rest fold to a single row each. A folded group shows
   the sum of its children's badges so nothing urgent can hide inside one. */
.nav-pin{padding-bottom:.5rem;margin-bottom:.35rem;border-bottom:1px solid rgba(255,255,255,.08)}
.nav-pin a{font-size:.93rem}
.grp{margin-bottom:1px}
.grp-head{display:flex;align-items:center;gap:.75rem;width:100%;
  padding:.58rem .8rem;border:none;background:none;cursor:pointer;
  color:#8fa79a;font:inherit;font-size:.73rem;font-weight:800;
  text-transform:uppercase;letter-spacing:.06em;border-radius:10px;text-align:left}
.grp-head:hover{background:rgba(255,255,255,.05);color:#dcebe2}
.grp-head:focus-visible{outline:2px solid var(--gold);outline-offset:-2px}
.grp-head svg{width:17px;height:17px;flex:none;opacity:.7}
.grp-head .lbl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.grp-head .chev{display:flex;transition:transform .16s;opacity:.6}
.grp.open>.grp-head{color:#eaf3ee}
.grp.open>.grp-head .chev{transform:rotate(180deg)}
/* The roll-up badge. Amber rather than the neutral grey of a single item's
   pill: it is standing in for something you cannot currently see. */
.grp-count{background:var(--brand);color:#fff;font-size:.68rem;font-weight:800;
  padding:.1rem .42rem;border-radius:999px;min-width:20px;text-align:center;flex:none}
.grp-items{display:none;padding:.15rem 0 .45rem .55rem;margin-left:.5rem;
  border-left:1px solid rgba(255,255,255,.09)}
.grp.open>.grp-items{display:block;animation:navdrop .16s ease-out}
.grp-items a{font-size:.87rem;padding:.5rem .7rem}
/* Third level. NOTHING USES IT TODAY — the Bookings menu did ("Bookings →
   Lodge & agency bookings → Tours") until the office said a three-deep menu
   was confusing and those filters moved onto the screen itself. The rules stay
   because app.js still renders a sub-group wherever one is declared, and a
   depth-2 group with no styling would come out looking like a broken link
   rather than a heading. Same behaviour as the level above it on purpose: a
   menu that works differently at depth 2 is one somebody has to learn twice. */
.grp.sub{margin:.15rem 0}
.grp.sub>.grp-head{font-size:.79rem;text-transform:none;letter-spacing:0;color:#a9c0b2;
  padding:.5rem .65rem}
.grp.sub>.grp-head .lbl{font-weight:700}
.grp.sub>.grp-items{padding-left:.5rem;margin-left:.45rem}
.grp.sub>.grp-items a{font-size:.845rem;padding:.44rem .65rem}
.grp.sub>.grp-head:hover{background:rgba(255,255,255,.06)}

/* "You are looking at a slice of something bigger" — the bar that names the
   slice and offers the way out of it. Used by the flight reader; the bookings
   list grew its own filter bar below. */
.scope-bar{display:flex;align-items:center;gap:.75rem;flex-wrap:wrap;margin-bottom:1rem;
  padding:.75rem 1rem;background:var(--card);border:1px solid var(--line);
  border-left:4px solid var(--brand);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm)}
.scope-bar svg{width:18px;height:18px;flex:none;color:var(--brand-dark)}
.scope-bar b{font-size:.95rem}
.scope-sub{font-size:.79rem;color:var(--muted);margin-top:.1rem}

/* ---- The bookings filter bar (views.js §BOOKINGS) ----
   The two questions the office asks about a booking — who sent it, and what
   kind of trip — as two rows of chips with their counts. It replaced fifteen
   sidebar entries for one screen, so it has to read as CHOICES ON A SCREEN
   rather than as another menu: each row is labelled with its question, the
   selected chip is filled with the brand colour, and every chip carries the
   number of bookings behind it so nothing has to be clicked to be counted. */
.filter-bar{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow-sm);padding:.65rem .9rem;margin-bottom:1rem}
.fb-row{display:flex;align-items:center;gap:.75rem;flex-wrap:wrap;padding:.3rem 0}
.fb-row+.fb-row{border-top:1px dashed var(--line-2);margin-top:.15rem;padding-top:.55rem}
.fb-lbl{font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);
  font-weight:800;width:6.4rem;flex:none}
.fb-chips{display:flex;gap:.4rem;flex-wrap:wrap;flex:1;min-width:0}
.fbchip{display:inline-flex;align-items:center;gap:.42rem;cursor:pointer;font:inherit;
  border:1px solid var(--line);background:#fff;border-radius:999px;padding:.38rem .75rem;
  font-size:.84rem;font-weight:600;color:#33473d;transition:.12s}
.fbchip:hover{border-color:var(--brand);background:var(--brand-soft)}
.fbchip:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.fbchip .ico{display:inline-flex;flex:none}
.fbchip svg{width:15px;height:15px}
/* The count reads as a quiet pill inside the chip, never as a second button. */
.fbchip .n{background:#f0f3ef;border-radius:999px;padding:.03rem .42rem;font-size:.72rem;
  font-weight:700;font-variant-numeric:tabular-nums;color:var(--muted)}
.fbchip.on{background:var(--brand);border-color:var(--brand);color:#fff}
.fbchip.on .n{background:rgba(255,255,255,.22);color:#fff}
.fb-foot{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;
  border-top:1px solid var(--line-2);margin-top:.5rem;padding-top:.55rem}
.fb-foot .spacer{flex:1}
.fb-sum{font-size:.79rem;color:var(--muted)}
/* On a phone the question labels sit above their own row of chips rather than
   stealing a third of the width from them. */
@media (max-width:720px){
  .fb-row{gap:.35rem}
  .fb-lbl{width:100%}
}
@keyframes navdrop{from{opacity:0;transform:translateY(-3px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){
  .grp.open>.grp-items{animation:none}
  .grp-head .chev{transition:none}
}

/* ---- Topbar mode switch ----
   Worded as a sentence ("I'm doing: Bookings & guests") rather than a bare
   dropdown of nouns, so it reads as a choice the user is making and can undo
   rather than a filter somebody else left switched on. */
.mode-switch{position:relative;flex:none}
.mode-btn{display:inline-flex;align-items:center;gap:.45rem;cursor:pointer;
  border:1px solid var(--line);background:#fff;border-radius:var(--radius-sm);
  padding:.5rem .7rem;font:inherit;font-size:.86rem;color:var(--ink);
  max-width:270px;transition:.15s}
.mode-btn:hover{background:#f4f7f4;border-color:#cbd6ce}
.mode-btn:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.mode-btn svg{width:16px;height:16px;flex:none;opacity:.7}
.mode-btn .mode-cap{color:var(--muted);font-size:.78rem;white-space:nowrap}
.mode-btn b{font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mode-btn .chev{display:flex;opacity:.55;margin-left:.1rem}
.mode-menu{min-width:296px;max-height:min(70vh,560px);overflow-y:auto;
  scrollbar-width:thin;scrollbar-color:var(--brand) var(--line-2)}
.mode-menu::-webkit-scrollbar{width:8px}
.mode-menu::-webkit-scrollbar-track{background:var(--line-2);border-radius:8px}
.mode-menu::-webkit-scrollbar-thumb{background:var(--brand);border-radius:8px}
.mode-menu::-webkit-scrollbar-thumb:hover{background:var(--brand-dark)}
.mode-menu button{align-items:flex-start;gap:.6rem}
.mode-menu button svg{width:16px;height:16px;flex:none;margin-top:.15rem;opacity:.65}
.mode-menu button.on{background:var(--brand-soft)}
.mode-menu button.on svg{opacity:1;color:var(--brand-dark)}
.mode-opt{flex:1;min-width:0}
.mode-opt b{display:block;font-size:.86rem;font-weight:700}
.mode-opt span{display:block;font-size:.74rem;color:var(--muted);line-height:1.35;
  white-space:normal}
/* Below this there is no room to narrow a menu on a screen that shows one
   thing at a time — and the full menu is one tap away behind the hamburger. */
@media (max-width:430px){ .mode-switch{display:none} }
.mode-btn.previewing{background:#fbf3df;border-color:var(--gold);color:#6d4d09}
.mode-sep{margin-top:.5rem;padding-top:.6rem;border-top:1px solid var(--line-2);
  border-bottom:1px solid var(--line-2)}

/* ---- "Viewing as" banner ----
   Part of the shell, not the page, so it survives every navigation until it is
   switched off. Somebody who forgets they are previewing a driver and wonders
   where the bookings went is the one failure this feature could introduce, so
   it is loud and it always carries its own way out. */
.viewas-banner{display:none}
.viewas-banner.on{display:flex;align-items:center;gap:.7rem;flex-wrap:wrap;
  padding:.6rem 1.6rem;background:linear-gradient(90deg,#fbf3df,#fdf0e3);
  border-bottom:1px solid var(--gold);color:#6d4d09;font-size:.86rem;
  position:sticky;top:0;z-index:19}
.viewas-banner svg{width:17px;height:17px;flex:none}
.viewas-banner .vb-txt{flex:1;min-width:0}
.viewas-banner .vb-txt b{font-weight:800}
.viewas-banner .btn{background:var(--bush-800)}
.viewas-banner .btn:hover{background:var(--bush-900)}

/* Same banner slot, different reason: this one says what's on screen may not
   be current, not that you're previewing somebody else. Its own colour so the
   two never read as the same message when both happen to be on at once. */
.offline-banner.on{background:linear-gradient(90deg,#fde8e8,#fdeeee);
  border-bottom:1px solid var(--red);color:#7a1f1f}
.offline-banner .btn{background:var(--red)}
.offline-banner .btn:hover{filter:brightness(.92)}

/* The framed front-door previews (external_book.html / checklist.html / waiver.html). */
.viewas-frame{border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;
  background:var(--card);box-shadow:var(--shadow-sm)}
.viewas-bar{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;
  padding:.6rem .9rem;background:#fafbf9;border-bottom:1px solid var(--line);
  font-size:.79rem;color:var(--muted)}
.viewas-bar .spacer{flex:1}
.viewas-frame iframe{display:block;width:100%;height:min(78vh,900px);border:0;background:#fff}

/* ---------- My schedule (myday.js) — a driver's own screen ----------
   Built for one hand, outside, on a phone. Bigger tap targets and a bigger
   clock than anything else in the app. */
.myday-head{display:flex;align-items:flex-start;gap:1rem;flex-wrap:wrap;margin-bottom:.4rem}
.myday-head h2{font-size:1.35rem}
.myday-head p{color:var(--muted);margin:.25rem 0 0}
.myday-list{display:grid;gap:.7rem}
.myday-trip{display:flex;gap:.9rem;align-items:flex-start;background:var(--card);
  border:1px solid var(--line);border-radius:var(--radius);padding:.9rem 1rem;
  box-shadow:var(--shadow-sm)}
.myday-trip.is-today{border-left:4px solid var(--brand)}
.myday-when{flex:none;width:4.4rem;text-align:center}
.myday-when b{display:block;font-size:1.32rem;font-weight:800;line-height:1.1;
  font-variant-numeric:tabular-nums}
.myday-when span{display:block;font-size:.7rem;color:var(--muted);text-transform:uppercase;
  letter-spacing:.05em;font-weight:700;margin-top:.15rem}
.myday-core{flex:1;min-width:0}
.myday-core>b{display:block;font-size:1rem;font-weight:700}
.myday-guest{font-size:.87rem;margin-top:.15rem}
.myday-where{display:flex;gap:.35rem;align-items:flex-start;font-size:.82rem;
  color:var(--muted);margin-top:.3rem}
.myday-where svg{width:14px;height:14px;flex:none;margin-top:.2em}
/* A flight that lands on a different day to the trip — the one line on this
   card a driver must not skim past. See the flight block in myday.js. */
.myday-where.warn{color:#8a5a12;font-weight:700}
.myday-note{font-size:.82rem;margin-top:.35rem;padding:.4rem .6rem;background:#fafbf9;
  border-left:2px solid var(--line);border-radius:4px}
.myday-tags{display:flex;gap:.35rem;align-items:center;flex-wrap:wrap;margin-top:.5rem}
.myday-ref{font-size:.72rem;color:var(--muted);font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.myday-do{flex:none;display:flex;flex-direction:column;gap:.35rem;align-items:stretch}
.myday-nophone{font-size:.76rem;color:var(--muted);white-space:nowrap}
.myday-dayhead{font-size:.86rem;font-weight:800;margin-bottom:.5rem;color:var(--ink)}
.myday-leave{display:flex;align-items:center;gap:.8rem;padding:.8rem 1.1rem;
  border-bottom:1px solid var(--line-2)}
.myday-leave:last-child{border-bottom:none}
@media (max-width:600px){
  .myday-trip{flex-wrap:wrap;gap:.6rem}
  .myday-when{width:auto;text-align:left;display:flex;align-items:baseline;gap:.4rem}
  .myday-when span{margin-top:0}
  .myday-do{flex-direction:row;flex-wrap:wrap;width:100%}
  .myday-do .btn{flex:1 1 auto}
  .viewas-banner.on{padding:.6rem 1rem}
  .viewas-frame iframe{height:70vh}
}
/* ===========================================================================
   THE RAIL CALENDAR (sidecal.js)

   A month inside a 300px dark green rail. Everything here is a consequence of
   that: seven columns of about 34px, so a cell holds a number and one hairline
   of load and nothing else; and a colour scheme that has to work on the
   sidebar gradient rather than on white, which is why none of the calendar
   colours further down this file are reused.

   The load hairline is FOUR steps, not a gradient. At 34px a continuous scale
   is a lie — the honest claim is quiet / normal / busy / full.
   =========================================================================== */
.sidecal{flex:none;border-top:1px solid rgba(255,255,255,.08)}
.sidecal.role-hidden{display:none}          /* driver logins — see syncSideCal() */
.sc-toggle{display:flex;align-items:center;gap:.75rem;width:100%;padding:.58rem 1.05rem;
  border:none;background:none;cursor:pointer;color:#8fa79a;font:inherit;font-size:.73rem;
  font-weight:800;text-transform:uppercase;letter-spacing:.06em;text-align:left}
.sc-toggle:hover{color:#fff}
.sc-toggle .ico{display:inline-flex;flex:none;opacity:.85}
.sc-toggle .lbl{flex:1;min-width:0}
.sc-toggle .chev{display:inline-flex;transition:transform .16s;opacity:.7}
.sidecal.open .sc-toggle .chev{transform:rotate(180deg)}
.sc-body{display:none;padding:0 .7rem .8rem}
.sidecal.open .sc-body{display:block}

.sc-head{display:flex;align-items:center;gap:.25rem;margin-bottom:.4rem}
.sc-nav{flex:none;width:26px;height:26px;display:grid;place-items:center;border:none;
  border-radius:7px;background:rgba(255,255,255,.06);color:#cfdcd3;cursor:pointer;padding:0}
.sc-nav:hover{background:rgba(255,255,255,.14);color:#fff}
.sc-month{flex:1;border:none;background:none;color:#fff;font:inherit;font-size:.82rem;
  font-weight:700;cursor:pointer;padding:.2rem;border-radius:7px}
.sc-month:hover{background:rgba(255,255,255,.08)}

.sc-grid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:2px}
.sc-dow{text-align:center;font-size:.6rem;font-weight:800;color:#6f8878;padding:.15rem 0 .25rem;
  text-transform:uppercase}
.sc-dow.we{color:#8a6a4a}
/* The cell. `position:relative` because the holiday mark and the load hairline
   are both absolutely placed — a flex column at this size ends up with the
   number bouncing by a pixel between a day that has trips and one that does
   not, and a calendar whose numbers do not line up looks broken. */
.sc-day{position:relative;aspect-ratio:1;min-height:30px;border:none;padding:0;
  border-radius:7px;background:rgba(255,255,255,.04);color:#cfdcd3;cursor:pointer;
  font:inherit;font-size:.74rem;font-weight:600;font-variant-numeric:tabular-nums;
  display:grid;place-items:center;transition:.1s}
.sc-day:hover{background:rgba(255,255,255,.16);color:#fff}
.sc-day:focus-visible{outline:2px solid var(--gold);outline-offset:1px}
.sc-day.we{background:rgba(255,255,255,.02)}
.sc-day.out{opacity:.32}
.sc-day .d{line-height:1}
.sc-day.today{box-shadow:inset 0 0 0 1.5px var(--gold);color:#fff;font-weight:800}
.sc-day.sel{background:var(--brand);color:#fff;font-weight:800}
.sc-day.sel:hover{background:var(--brand-dark)}
/* Public holiday: a gold pip in the top-right corner. Not a background wash —
   a washed cell competes with the "selected" and "today" states, and those are
   about where you are, which must always win. */
.sc-day .hm{position:absolute;top:3px;right:3px;width:4px;height:4px;border-radius:50%;
  background:var(--gold);box-shadow:0 0 0 1px rgba(0,0,0,.25)}
.sc-day.sel .hm{background:#fff}
/* The load hairline along the bottom. */
.sc-day .bar{position:absolute;left:5px;right:5px;bottom:3px;height:2px;border-radius:2px;
  background:#5f8a6d}
.sc-day.l1 .bar{background:#5f8a6d}
.sc-day.l2 .bar{background:#8fbf78}
.sc-day.l3 .bar{background:var(--gold)}
.sc-day.l4 .bar{background:#e07a4f}
.sc-day.sel .bar{background:#fff;opacity:.9}

.sc-sum{margin-top:.55rem}
.sc-note{font-size:.72rem;color:#8fa79a;padding:.4rem .1rem}
.sc-hol{display:flex;align-items:center;gap:.35rem;font-size:.7rem;font-weight:700;
  color:var(--gold);margin-bottom:.3rem;min-width:0}
.sc-hol .ico{display:inline-flex;flex:none}
.sc-hol span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The whole summary block is the button into the day panel — see the note in
   sidecal.js about not putting a third small target in a 270px rail. */
.sc-open{display:block;width:100%;text-align:left;border:1px solid rgba(255,255,255,.1);
  background:rgba(255,255,255,.05);border-radius:9px;padding:.5rem .6rem;cursor:pointer;
  color:#cfdcd3;font:inherit;transition:.12s}
.sc-open:hover{background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.24)}
.sc-open-head{display:flex;align-items:center;gap:.4rem;min-width:0}
.sc-open-head b{color:#fff;font-size:.78rem;font-weight:700;white-space:nowrap}
.sc-open-head .rel{flex:1;min-width:0;font-size:.66rem;color:#8fa79a;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sc-open-head .go{display:inline-flex;flex:none;opacity:.6}
.sc-open:hover .go{opacity:1}
.sc-figs{display:flex;flex-wrap:wrap;gap:.15rem .6rem;margin-top:.25rem;
  font-size:.68rem;color:#8fa79a}
.sc-figs b{color:#fff;font-weight:800;font-variant-numeric:tabular-nums}
.sc-figs.quiet{color:#7d9488;font-style:italic}
.sc-figs.month{padding:.1rem .1rem .2rem}
.sc-warn{margin-top:.3rem;font-size:.66rem;font-weight:700;color:#f0c14b}
.sc-next{display:flex;align-items:center;gap:.35rem;width:100%;margin-top:.45rem;
  border:none;background:rgba(240,193,75,.1);border-radius:8px;padding:.3rem .45rem;
  cursor:pointer;color:var(--gold);font:inherit;font-size:.68rem;font-weight:700;text-align:left}
.sc-next:hover{background:rgba(240,193,75,.2)}
.sc-next .ico{display:inline-flex;flex:none}
.sc-next .nm{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sc-next .when{flex:none;opacity:.75;font-weight:600}
.sc-full{display:flex;align-items:center;gap:.3rem;width:100%;margin-top:.5rem;border:none;
  background:none;color:#8fa79a;font:inherit;font-size:.7rem;font-weight:700;cursor:pointer;
  padding:.25rem .1rem;border-radius:7px}
.sc-full:hover{color:#fff}
.sc-full .go{display:inline-flex;opacity:.7}

.sidebar .foot{padding:.9rem 1rem;border-top:1px solid rgba(255,255,255,.08);font-size:.78rem}
.sidebar .foot .env{display:inline-block;margin-top:.3rem;font-size:.66rem;padding:.15rem .5rem;border-radius:6px}
.env.demo{background:#3a2c05;color:#f0c14b}
.env.live{background:#06301c;color:#37d18c}

.signout,.install-link{display:flex;align-items:center;justify-content:center;gap:.5rem;width:100%;
  margin-top:.7rem;padding:.55rem .7rem;border-radius:9px;cursor:pointer;
  font:inherit;font-size:.78rem;font-weight:600;transition:.16s}
.signout{background:rgba(255,255,255,.05);color:#c7d6cc;border:1px solid rgba(255,255,255,.13)}
.signout:hover{background:rgba(192,57,43,.2);border-color:rgba(232,120,110,.5);color:#fbd5d0}
.signout:active{transform:translateY(1px)}
.install-link{display:none;color:#8fd9b4;background:rgba(55,209,140,.1);border:1px solid rgba(55,209,140,.3)}
.install-link:hover{background:rgba(55,209,140,.18)}
.topbar .avatar-btn{width:36px;height:36px;flex:none;border-radius:50%;border:1px solid var(--line);
  background:linear-gradient(135deg,var(--brand),var(--gold));color:#fff;
  font-weight:700;font-size:.76rem;cursor:pointer;display:grid;place-items:center;
  box-shadow:var(--shadow-sm);transition:.16s}
.topbar .avatar-btn:hover{filter:brightness(1.08)}
.acct-menu{position:absolute;right:0;top:calc(100% + .5rem);z-index:130;min-width:214px;
  background:#fff;border:1px solid var(--line);border-radius:12px;box-shadow:var(--shadow-lg);
  padding:.5rem;animation:fade .12s}
.acct-menu .hd{padding:.5rem .6rem .6rem;border-bottom:1px solid var(--line-2);margin-bottom:.4rem}
.acct-menu .hd b{display:block;font-size:.84rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.acct-menu .hd span{display:block;font-size:.72rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.acct-menu button{display:flex;align-items:center;gap:.55rem;width:100%;padding:.5rem .6rem;
  border:none;background:none;border-radius:8px;cursor:pointer;font:inherit;font-size:.82rem;
  color:var(--ink);text-align:left}
.acct-menu button:hover{background:var(--line-2)}
.acct-menu button.danger{color:var(--red)}
.acct-menu button.danger:hover{background:#fdecea}

.main{min-width:0;display:flex;flex-direction:column}
.topbar{position:sticky;top:0;z-index:20;background:rgba(255,255,255,.92);backdrop-filter:blur(8px);
  border-bottom:1px solid var(--line);padding:.85rem 1.6rem;display:flex;align-items:center;gap:1rem}
.topbar h1{font-size:1.15rem}
.topbar .sub{color:var(--muted);font-size:.83rem;margin-top:1px}
.topbar .spacer{flex:1}
.hamburger{display:flex;align-items:center;justify-content:center;background:none;border:none;
  cursor:pointer;padding:.3rem;color:var(--ink)}
.content{padding:clamp(1rem,1.8vw,2rem);max-width:min(2200px,100%);width:100%;min-width:0;margin:0 auto}
/* Uncapped — schedule ('wide:true' in app.js's ROUTES) is currently the
   ONLY route using this class, so widening it has no effect anywhere else.
   Was max-width:1760px, which still left real white space on a big
   monitor; the calendar's own 7-column grid (.cal-grid, minmax(0,1fr))
   already shares whatever width it's given evenly, so there is no upper
   bound past which it stops looking right.

   The base cap above was raised from 1280px to 1600px on 6 Sep 2026 (the
   dashboard's stat tiles were leaving wide margins on a big monitor). Tried
   making the dashboard itself 'wide' first, but its trip-row list
   (.trip: fixed-width text on the left, actions pinned to the right) just
   stretches into a dead gap in the middle at full bleed — unlike the
   calendar's grid, it has nothing that grows to fill unbounded width. A
   capped-but-wider number was the fix that helps the tiles without
   breaking the rows. */
.content.content-wide{max-width:none}

/* ---------- Cards & grids ---------- */
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow-sm)}
.card.pad{padding:1.4rem}
.card-head{display:flex;align-items:center;gap:.8rem;flex-wrap:wrap;
  padding:1.1rem 1.4rem;border-bottom:1px solid var(--line-2)}
.card-head h3{font-size:1.14rem;font-weight:800}
.card-head .spacer{flex:1}
.card-body{padding:1.4rem}
.card-foot{padding:1rem 1.4rem;border-top:1px solid var(--line-2);display:flex;gap:.6rem;flex-wrap:wrap}
.grid{display:grid;gap:1.2rem}
.grid.cols-2{grid-template-columns:repeat(2,1fr)}
.grid.cols-3{grid-template-columns:repeat(3,1fr)}
.grid.cols-4{grid-template-columns:repeat(4,1fr)}
.grid.tight{gap:.8rem}
.section-title{font-size:1.05rem;letter-spacing:normal;color:var(--ink);
  font-weight:800;margin:1.7rem 0 .8rem;display:flex;align-items:center;gap:.5rem}
.section-title .spacer{flex:1}

/* ---------- "Where everything is" (start.js) ----------
   Four cards, one per front door. Each card is a signpost, not a screen: the
   only job here is that somebody who is lost can see the shape of the whole
   product at once and press through to the right part of it. */
.hub-intro{margin-bottom:1.4rem;max-width:70ch}
.hub-intro h2{font-size:1.4rem}
.hub-intro p{color:var(--muted);margin:.4rem 0 0}
.hub-grid{display:grid;gap:1.2rem;grid-template-columns:repeat(2,1fr)}
.hub-card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow-sm);padding:1.3rem;display:flex;flex-direction:column;
  border-top:4px solid var(--line);min-width:0}
.hub-card.office {border-top-color:var(--bush-600)}
.hub-card.guest  {border-top-color:var(--brand)}
.hub-card.partner{border-top-color:var(--teal)}
.hub-card.driver {border-top-color:var(--gold)}
.hub-head{display:flex;align-items:flex-start;gap:.8rem;margin-bottom:.7rem}
.hub-ico{display:grid;place-items:center;width:42px;height:42px;flex:none;border-radius:11px;
  background:var(--line-2);color:var(--ink)}
.hub-card.office  .hub-ico{background:#e7efe9;color:var(--bush-700)}
.hub-card.guest   .hub-ico{background:var(--brand-soft);color:var(--brand-dark)}
.hub-card.partner .hub-ico{background:#e2f2f0;color:var(--teal)}
.hub-card.driver  .hub-ico{background:#fbf3df;color:#8a6212}
.hub-id{min-width:0;flex:1}
.hub-id b{display:block;font-size:1.06rem;font-weight:800;line-height:1.25}
.hub-id span{display:block;font-size:.8rem;color:var(--muted);margin-top:.15rem}
.hub-here{flex:none;font-size:.66rem;font-weight:800;text-transform:uppercase;letter-spacing:.05em;
  background:var(--bush-700);color:#fff;padding:.2rem .5rem;border-radius:999px;white-space:nowrap}
.hub-what{margin:0 0 .8rem;font-size:.9rem;line-height:1.5}
.hub-facts{margin:0 0 .9rem;padding:0;list-style:none;font-size:.83rem;color:var(--muted)}
.hub-facts li{position:relative;padding-left:1.05rem;margin-bottom:.32rem;line-height:1.45}
.hub-facts li::before{content:"";position:absolute;left:.2rem;top:.55em;width:5px;height:5px;
  border-radius:50%;background:var(--line);outline:1px solid #cbd6ce}
.hub-url{display:flex;gap:.5rem;align-items:center;background:#fafbf9;border:1px solid var(--line);
  border-radius:var(--radius-sm);padding:.5rem .65rem;margin-bottom:.9rem}
.hub-url span{flex:1;min-width:0;font-size:.78rem;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  word-break:break-all}
.hub-actions{margin-top:auto;padding-top:.2rem}

/* ---------- The driver's front door (drive.js) ----------
   The same idea as the four cards above, sized for a different hand. This is
   read on a phone, outdoors, often in direct sun and often one-handed while
   the other hand is holding a key or a clipboard — so the tiles are big, the
   tap target is the WHOLE tile rather than a button inside it, and the colour
   block does the work a photograph would do at a fraction of the bytes on a
   signal that comes and goes.

   ⚠ TWO COLUMNS EVEN ON THE NARROWEST PHONE, deliberately. A single column
     puts the sixth door below the fold, and a door you have to scroll to find
     is the problem this page was written to fix. Six tiles, three rows, all of
     it on one screen. */
.dh-head{display:flex;align-items:flex-start;gap:1rem;margin-bottom:1.1rem}
.dh-head h2{font-size:1.35rem}
.dh-head p{color:var(--muted);margin:.25rem 0 0;font-size:.88rem}
.dh-hours{flex:none;text-align:right;line-height:1.15}
.dh-hours b{display:block;font-size:1.15rem;font-variant-numeric:tabular-nums;color:var(--green)}
.dh-hours span{font-size:.72rem;color:var(--muted)}
.dh-hours.is-near b{color:var(--amber)}
.dh-hours.is-over b{color:var(--red)}

.dh-next{margin-bottom:.4rem}
.dh-next-lbl{display:flex;align-items:center;gap:.4rem;font-size:.74rem;font-weight:800;
  text-transform:uppercase;letter-spacing:.06em;color:var(--muted);margin-bottom:.55rem}
.dh-next-lbl svg{opacity:.7}

.dh-grid{display:grid;gap:.9rem;grid-template-columns:repeat(2,1fr)}
.dh-tile{position:relative;display:flex;flex-direction:column;text-align:left;
  background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow-sm);overflow:hidden;padding:0;cursor:pointer;
  font:inherit;color:var(--ink);min-width:0;
  transition:transform .12s ease, box-shadow .12s ease}
.dh-tile:hover{box-shadow:var(--shadow-md);transform:translateY(-2px)}
.dh-tile:active{transform:translateY(0)}
.dh-tile:focus-visible{outline:3px solid var(--brand);outline-offset:2px}

/* The colour block, or the photograph when there is one. The background-color
   is painted underneath the image on purpose: a photo named in drive.js that
   is missing from the folder simply never covers it, so a half-finished set of
   pictures degrades one tile at a time instead of leaving holes. */
.dh-art{position:relative;height:88px;display:grid;place-items:center;flex:none;
  background-size:cover;background-position:center;background-repeat:no-repeat}
.dh-art-ico{display:grid;place-items:center;color:#fff;opacity:.95;
  filter:drop-shadow(0 1px 2px rgba(0,0,0,.28))}
.dh-art.tone-bush  {background-color:var(--bush-700)}
.dh-art.tone-gold  {background-color:#b8862a}
.dh-art.tone-teal  {background-color:var(--teal)}
.dh-art.tone-brand {background-color:var(--brand)}
.dh-art.tone-purple{background-color:var(--purple)}
.dh-art.tone-ink   {background-color:var(--bush-900)}
/* A photograph needs a scrim or a white icon lands on a white sky. */
.dh-art.has-photo::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(16,35,26,.15),rgba(16,35,26,.55))}
.dh-art.has-photo .dh-art-ico{position:relative;z-index:1}

.dh-body{padding:.75rem .8rem .85rem;display:flex;flex-direction:column;gap:.15rem;min-width:0}
.dh-body b{font-size:.98rem;font-weight:800;line-height:1.25}
.dh-hint{font-size:.76rem;color:var(--muted);line-height:1.4}
.dh-note{margin-top:.3rem;font-size:.75rem;font-weight:700;color:var(--bush-700)}
.dh-out{position:absolute;top:.5rem;right:.5rem;display:grid;place-items:center;
  width:24px;height:24px;border-radius:50%;background:rgba(255,255,255,.9);color:var(--ink)}

.dh-office{display:flex;align-items:center;justify-content:center;gap:.6rem;flex-wrap:wrap;
  margin-top:1.4rem;padding-top:1.1rem;border-top:1px solid var(--line);
  font-size:.83rem;color:var(--muted)}

/* Roomier once there is width for it — the tablet on the dashboard mount and
   the owner previewing a driver from her desk. */
@media (min-width:760px){
  .dh-grid{grid-template-columns:repeat(3,1fr);gap:1.1rem}
  .dh-art{height:104px}
}

/* The "find any screen" box — the plain answer to "where is X" for somebody
   who knows the word but not which group it lives in. */
.hub-find-results{margin-top:.8rem}
.hub-find-hint{font-size:.84rem;color:var(--muted);padding:.6rem .2rem}
.hub-find-row{display:flex;align-items:center;gap:.7rem;width:100%;text-align:left;
  padding:.6rem .7rem;border:1px solid transparent;border-radius:var(--radius-sm);
  background:none;cursor:pointer;font:inherit;color:var(--ink)}
.hub-find-row:hover{background:var(--line-2);border-color:var(--line)}
.hub-find-row:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}
.hub-find-row svg{width:17px;height:17px;flex:none;opacity:.6}
.hub-find-txt{flex:1;min-width:0}
.hub-find-txt b{display:block;font-size:.9rem;font-weight:700}
.hub-find-txt span{display:block;font-size:.78rem;color:var(--muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hub-find-grp{flex:none;font-size:.7rem;font-weight:700;color:var(--muted);
  background:var(--line-2);border-radius:999px;padding:.18rem .55rem;white-space:nowrap}
@media (max-width:900px){
  .hub-grid{grid-template-columns:1fr}
  .hub-find-grp{display:none}
}

/* ---------- Stat tiles ----------

   CENTRED, asked for on 23 August 2026: "centre all the figures in the cards."
   It is set on .stat itself rather than on the dashboard, because this is ONE
   tile used on nine screens — the dashboard, the calendar, the roster, log
   books, trip grouping, compliance, documents and the two money screens (see
   the note over finance.js statTile(), which exists to keep those matching).
   Centring only the dashboard would have made the same tile look like two
   different components depending on which screen you were standing on.

   .ic needs the auto margins of its own: it is a fixed 34px box, and
   text-align does not move a block that has a width. */
.stat{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  padding:1.15rem 1.25rem;box-shadow:var(--shadow-sm);position:relative;overflow:hidden;
  text-align:center}
.stat .k{font-size:.74rem;color:var(--muted);font-weight:700;text-transform:uppercase;letter-spacing:.04em}
.stat .v{font-size:1.95rem;font-weight:800;margin-top:.25rem;line-height:1.05;font-variant-numeric:tabular-nums}
.stat .d{font-size:.79rem;color:var(--muted);margin-top:.35rem}
.stat .ic{width:34px;height:34px;border-radius:10px;margin:0 auto .6rem;
  display:grid;place-items:center;background:var(--brand-soft);color:var(--brand)}
.stat.green .v{color:var(--green)} .stat.green .ic{background:#e6f5ec;color:var(--green)}
.stat.red .v{color:var(--red)}     .stat.red .ic{background:#fdecea;color:var(--red)}
.stat.amber .v{color:var(--amber)} .stat.amber .ic{background:#fff4e2;color:var(--amber)}
.stat.teal .v{color:var(--teal)}   .stat.teal .ic{background:#e4f0ec;color:var(--teal)}
/* TINTED TILE BACKGROUNDS — opts.tint in views.js's stat(), asked for on the
   dashboard 6 Sep 2026: "give some life to the dashboard." A class of its
   own rather than colouring .stat itself, because .stat is the one tile
   shared by nine screens (see the note above) and a tinted dashboard was
   never asked to also recolour the roster or the compliance screen. The
   icon chip flips to a plain white circle here — on a tinted tile the same
   colour behind the icon as behind the whole card would make it disappear,
   the one thing an icon in a coloured corner is for. Still overridden
   instantly by a saved custom colour (bgInput above sets root.style.
   background directly), same as the tile was before tinting existed.

   TURNED UP ONCE ALREADY, 6 Sep 2026, same day: the first pass used the
   same pale tints as the little icon chips (~95% lightness) and read as
   "barely off-white" rather than the "life" that was actually asked for.
   These sit around 80% lightness instead — noticeably a colour at a glance,
   while .v staying var(--teal) etc. (a much darker shade of the same hue)
   keeps the big number readable against it. */
.stat-tint.green{background:#b3e6c4} .stat-tint.green .ic{background:#fff}
.stat-tint.red{background:#f6c2ba}   .stat-tint.red .ic{background:#fff}
.stat-tint.amber{background:#f6d3a0} .stat-tint.amber .ic{background:#fff}
.stat-tint.teal{background:#a9e2da}  .stat-tint.teal .ic{background:#fff}
.stat.clickable{cursor:pointer;transition:.13s}
.stat.clickable:hover{border-color:var(--brand);box-shadow:0 0 0 2px rgba(184,98,27,.15);transform:translateY(-2px)}

/* ---------- Forms ---------- */
.field{margin-bottom:1rem}
.field > label{display:block;font-weight:600;font-size:.83rem;margin-bottom:.35rem;color:#2c3b34}
.field .hint{font-size:.76rem;color:var(--muted);margin-top:.3rem}
input,select,textarea{width:100%;min-width:0;max-width:100%;padding:.6rem .75rem;
  border:1px solid var(--line);border-radius:var(--radius-sm);font-size:.92rem;font-family:inherit;
  background:var(--field);color:var(--ink);transition:.15s}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--brand);
  box-shadow:0 0 0 3px rgba(184,98,27,.14)}
textarea{resize:vertical;min-height:90px}
input[type=checkbox],input[type=radio]{width:1.05rem;height:1.05rem;min-width:0;max-width:none;
  padding:0;border:none;background:none;box-shadow:none;flex:0 0 auto;accent-color:var(--brand);cursor:pointer}
input[type=checkbox]:focus,input[type=radio]:focus{box-shadow:0 0 0 3px rgba(184,98,27,.2)}
.form-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:0 1.2rem}
.form-grid .full{grid-column:1/-1}
.form-grid.cols-3{grid-template-columns:repeat(3,1fr)}
.req::after{content:" *";color:var(--red)}
.checkline{display:flex;align-items:flex-start;gap:.6rem;font-size:.88rem;cursor:pointer;
  padding:.4rem 0;color:#2c3b34}
.checkline span{flex:1}

/* Disclaimer clauses — each is its own tickbox in its own row, deliberately:
   a risk waiver has to draw attention to each point individually, not bury
   them under one blanket "I agree". */
.waiver-clauses{display:flex;flex-direction:column;gap:.15rem}
.waiver-clause{padding:.65rem .7rem;border-radius:var(--radius-sm);border:1px solid transparent;
  line-height:1.5;transition:.12s}
.waiver-clause:hover{background:#f6f7f4}
.waiver-clause input:checked ~ span{color:var(--ink)}
.waiver-clause:has(input:checked){background:var(--brand-soft);border-color:var(--brand)}

/* number stepper — pax counts get set with a thumb far more than a keyboard */
.stepper{display:inline-flex;align-items:stretch;border:1px solid var(--line);
  border-radius:var(--radius-sm);overflow:hidden;background:#fff}
.stepper-b{border:none;background:#f6f7f4;color:var(--ink);cursor:pointer;padding:0 .8rem;
  display:grid;place-items:center;transition:.12s}
.stepper-b:hover{background:var(--brand-soft);color:var(--brand-dark)}
.stepper-v{border:none;border-radius:0;text-align:center;width:3.4rem;padding:.55rem .2rem;
  font-weight:700;font-variant-numeric:tabular-nums;-moz-appearance:textfield}
.stepper-v:focus{box-shadow:none}
.stepper-v::-webkit-outer-spin-button,.stepper-v::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}

/* choice chips */
.chips{display:flex;flex-wrap:wrap;gap:.45rem}
.chip{border:1px solid var(--line);background:#fff;border-radius:999px;padding:.42rem .9rem;
  font-size:.85rem;font-weight:600;cursor:pointer;color:#33473d;transition:.12s;user-select:none}
.chip:hover{border-color:#c2cfc6}
.chip:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.chip.on{background:var(--brand);border-color:var(--brand);color:#fff}
.chip.on.green{background:var(--green);border-color:var(--green)}
.chip.on.dark{background:var(--bush-700);border-color:var(--bush-700)}

/* ---------- Lodge file import (import.js) ----------
   The drop zone is deliberately large and is the first thing on the screen:
   the whole point of the feature is that the file goes in without being read
   by a person, so the target for it must be impossible to miss. */
.dropzone{border:2px dashed #c8d5cc;border-radius:var(--radius);background:#fbfdfb;
  padding:1.6rem 1.2rem;text-align:center;cursor:pointer;transition:.12s}
.dropzone:hover{border-color:var(--brand);background:var(--brand-soft)}
.dropzone.over{border-color:var(--brand);background:var(--brand-soft);transform:scale(1.005)}
.dropzone .dz-icon{color:var(--brand);margin-bottom:.5rem}
.dropzone b{display:block;font-size:.96rem;margin-bottom:.2rem}
.dropzone span{display:block;font-size:.8rem;color:var(--muted)}
.hr{height:1px;background:var(--line-2);margin:1rem 0 1.2rem}

.imp-summary{display:flex;gap:.7rem;flex-wrap:wrap;margin-bottom:1.2rem}
.imp-tile{flex:1;min-width:104px;border:1px solid var(--line);border-radius:var(--radius-sm);
  padding:.7rem .9rem;background:#fff}
.imp-tile b{display:block;font-size:1.5rem;font-weight:800;line-height:1.1}
.imp-tile span{display:block;font-size:.74rem;color:var(--muted);text-transform:uppercase;
  letter-spacing:.04em;font-weight:700;margin-top:.15rem}
.imp-tile.confirmed{background:var(--brand-soft);border-color:#cfe3d6}
.imp-tile.confirmed b{color:var(--brand-dark)}
.imp-tile.quoted{background:#fff4e2;border-color:#f2e0b8}
.imp-tile.quoted b{color:var(--amber)}
.imp-tile.completed b{color:var(--teal)}
.imp-tile.cancelled b{color:var(--muted)}
.imp-tile.warn{background:#fdecea;border-color:#f3ccc7}
.imp-tile.warn b{color:var(--red)}

/* The preview is a working list, not a navigation list — its rows are read and
   ticked, never clicked through, so they must not pretend to be links. */
table.imp-table{min-width:940px}
table.imp-table tbody tr{cursor:default}
table.imp-table tbody tr:hover{background:transparent}
table.imp-table tr.imp-bad{background:#fdf5f4}
/* A trip that cannot run as written still imports, so it must not look like a
   dead row — it looks like a row with something to do about it. */
table.imp-table tr.imp-blocked{background:#fffaf4;box-shadow:inset 3px 0 0 var(--red)}
table.imp-table input[type=checkbox]{width:17px;height:17px;accent-color:var(--brand)}

/* ---------- Tables ---------- */
.table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain;
  border:1px solid var(--line);border-radius:var(--radius);background:#fff}
table.data{width:100%;border-collapse:collapse;font-size:.86rem;min-width:640px}
table.data th{text-align:left;font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;
  color:var(--muted);padding:.7rem .9rem;border-bottom:1px solid var(--line);
  background:#fafbf9;white-space:nowrap;font-weight:800}
table.data td{padding:.7rem .9rem;border-bottom:1px solid var(--line-2);vertical-align:top}
table.data tr:last-child td{border-bottom:none}
table.data tbody tr{cursor:pointer;transition:.1s}
table.data tbody tr:hover{background:#f7faf7}
/* ⚠ FIGURES ARE CENTRED, ASKED FOR 1 SEP 2026 ("all figures in tables must be
   centred to look smarter"). `.r` is the marker every numeric column in the app
   already carries — 222 cells across the screens — so this one rule moves all
   of them and there is nothing to hunt down cell by cell.

   ⚠ tabular-nums IS NOT OPTIONAL HERE, it is what makes centring work. In the
     default proportional figures a 1 is narrower than a 0, so a centred column
     of prices sits at a slightly different offset on every row and reads as
     sloppy — the exact opposite of the intent. Tabular figures are all one
     width, so the column holds a straight edge down both sides.

   The class is still called `.r` (it was "right") rather than renamed, because
   renaming it means touching 222 call sites in fourteen files to change nothing
   anybody can see. If the alignment is ever revisited, this line is the only
   place it lives. */
table.data th.r,table.data td.r{text-align:center;
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum"}
.cell-name{font-weight:600}
.cell-sub{color:var(--muted);font-size:.78rem}

/* ---------- Toolbar / filters ---------- */
.toolbar{display:flex;gap:.6rem;flex-wrap:wrap;align-items:center;margin-bottom:1.2rem}
.toolbar .search{flex:1;min-width:190px}
.toolbar select,.toolbar input{max-width:230px}
.toolbar .spacer{flex:1}

/* ---------- Pipeline board ----------
   One column per APP_CONFIG.STATUSES entry (6, Cancelled included) — kept as
   an explicit count rather than an auto-fit track so the board always reads
   as one fixed row of stages, never a wrapping grid that reflows mid-scan. */
.pipeline{display:grid;grid-template-columns:repeat(6,1fr);gap:.85rem;align-items:start}
.pipe-col{background:#f1f4f0;border:1px solid var(--line);border-radius:var(--radius);
  padding:.7rem;min-height:120px}
/* The heading is the stat: count, stage, money — stacked and CENTRED in the
   column, so the number is the first thing read rather than a small badge
   pushed into the corner. The tick box comes out of the flow so it cannot pull
   that stack off centre; the side padding keeps the text clear of it. */
.pipe-col > h4{position:relative;display:flex;flex-direction:column;align-items:center;
  gap:.12rem;text-align:center;margin-bottom:.7rem;padding:.15rem 1.5rem .6rem;
  border-bottom:1px solid var(--line)}
.pipe-col > h4 .n{font-size:1.6rem;font-weight:800;line-height:1.05;color:var(--ink);
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum"}
/* An empty stage should look calm, not alarming — a grey 0, not a black one. */
.pipe-col > h4 .n.zero{color:var(--muted);opacity:.5;font-weight:700}
.pipe-col > h4 .k{font-size:.72rem;text-transform:uppercase;letter-spacing:.05em;
  color:var(--muted);font-weight:700}
.pipe-col > h4 .v{font-size:.75rem;font-weight:700;color:var(--brand-dark)}
.pipe-card{background:#fff;border:1px solid var(--line);border-radius:10px;padding:.65rem .75rem;
  margin-bottom:.5rem;cursor:pointer;transition:.12s;box-shadow:var(--shadow-sm)}
.pipe-card:hover{border-color:var(--brand);transform:translateY(-1px);box-shadow:var(--shadow-md)}
/* overflow-wrap so a long unbroken name or product title (no spaces to break
   on) still wraps inside the card instead of forcing it wider than its
   column — a real risk now that six stages share the board instead of five. */
.pipe-card b{display:block;font-size:.85rem;line-height:1.3;overflow-wrap:break-word}
.pipe-card .l2{font-size:.75rem;color:var(--muted);margin-top:.15rem;overflow-wrap:break-word}
.pipe-card .l3{display:flex;align-items:center;gap:.4rem;margin-top:.45rem;
  font-size:.74rem;font-weight:700;color:var(--brand-dark)}
.pipe-card .l3 .spacer{flex:1;min-width:.4rem}
.pipe-card .l3>span{min-width:0}
/* Says what the stage means instead of a bare dash, so five empty columns read
   as an explanation of the board rather than five broken boxes. */
.pipe-empty{font-size:.76rem;color:#9aa8a0;text-align:center;padding:.7rem .35rem 1rem;
  line-height:1.4}
/* The stage's own hint, so an empty board TEACHES the pipeline instead of
   leaving the office guessing what belongs in the column. */
.pipe-empty .hint{font-size:.7rem;margin-top:.25rem;opacity:.85;font-style:italic}

/* ---------- Bookings with no column on the board ----------
   Sits UNDER the board, divided off by a rule and tinted amber, so it reads as
   "these exist and you cannot see them above" rather than as a sixth stage.
   The cards inside are ordinary .pipe-card, so everything you can do to a
   booking on the board you can do to one down here.
   Not a grid of five: there are usually one or two groups, and stretching them
   across five columns makes an exception look like the main event. */
.off-board{margin-top:1.1rem;padding-top:1rem;border-top:2px dashed var(--line)}
.off-head{background:#fff8e9;border:1px solid #e6d3a8;border-radius:var(--radius);
  padding:.7rem .9rem;margin-bottom:.85rem}
.off-head b{display:block;font-size:.86rem}
.off-head > div{font-size:.76rem;color:var(--muted);margin-top:.25rem;line-height:1.45}
.off-cols{display:flex;flex-wrap:wrap;gap:.85rem;align-items:start}
.off-cols > .pipe-col{flex:0 1 260px;min-width:0}

/* ---------- Picking several bookings at once ----------
   The tick boxes are permanent, so the box and the card are two SEPARATE
   targets: the box ticks, everything else opens the booking. That means the box
   must be genuinely hittable — it was previously `pointer-events:none` decoration
   for a mode where the whole card ticked. 18px is small for a thumb, so the card
   box carries an invisible pad around it via a wider positioned hit area, and
   the table's whole pick cell is padded for the same reason. */
.pipe-card{position:relative}
.pipe-card .pick,.data .pickcol .pick,.pipe-col h4 .pick{accent-color:var(--brand);
  width:18px;height:18px;margin:0;cursor:pointer;flex:none}
/* Padding grows the hit area to ~34px while the box itself stays 18px, so the
   target is thumb-sized on the tablet without the tick looking oversized. The
   box sits inset from the card's own corner — top:0;right:0 used to put the
   padded hit area flush against the card's border, which read as the tick
   sitting ON the edge rather than inside the card. */
.pipe-card .pick{position:absolute;top:.4rem;right:.4rem;padding:8px;box-sizing:content-box}
/* Same trick on the column box, now that the heading centres its stat: out of
   the flow at the top-left corner, with the pad giving it a thumb-sized target. */
.pipe-col > h4 .pick{position:absolute;top:-2px;left:-6px;padding:8px;box-sizing:content-box}
.pipe-card:has(.pick){padding-right:2.5rem}
.pipe-card.picked{border-color:var(--brand);box-shadow:0 0 0 2px var(--brand-soft,rgba(93,124,90,.25))}
.data th.pickcol,.data td.pickcol{width:38px;padding-left:.9rem;padding-right:0;text-align:center}
.data td.pickcol .pick{cursor:pointer}

/* ---------- Confirm / Change / Delete on a single booking ----------
   Divided off from the booking's own details by a rule, so it reads as
   something you DO to the card rather than more information about the trip.
   Confirm — a status change — gets its own full-width line above Change and
   Delete, which are stacked one under the other below it rather than sharing
   a row: side by side, a narrow card squeezed both labels down to ellipsis
   ("Cha…" / "Del…"), which is unreadable on exactly the device — a phone or
   the office tablet — this card is drawn for. */
.pipe-act-group{margin-top:.55rem;padding-top:.5rem;border-top:1px solid var(--line)}
.pipe-confirm{margin-bottom:.3rem}
.pipe-confirm .btn{width:100%}
.pipe-act{display:flex;flex-direction:column;gap:.3rem}
.pipe-act .btn{width:100%;justify-content:center}
.pipe-act .btn .lbl{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.data th.actcol,.data td.actcol{width:1%;white-space:nowrap;text-align:right;padding-right:.9rem}
.data td.actcol .btn + .btn{margin-left:.3rem}
table.data tbody tr.picked,table.data tbody tr.picked:hover{background:#eaf2e8}
/* A row that is off the list — a retired transfer route. Faded, not hidden:
   the only place it can be switched back on is the list it was taken off. The
   tick box, the status badge and the buttons are left at full contrast — they
   are what still works on the row, and opacity on a cell cannot be undone by
   the child, so those cells are excluded rather than re-lit. */
table.data tbody tr.inactive td:not(.pickcol):not(.actcol):not(.statuscol){opacity:.55}
.bulk-bar{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;
  background:#fff;border:1px solid var(--brand);border-radius:var(--radius);
  padding:.7rem .9rem;margin-bottom:1rem;box-shadow:var(--shadow-sm)}
.bulk-bar .ico{display:inline-flex;color:var(--brand)}
.bulk-bar b{font-size:.88rem}
.bulk-bar .bulk-sub{font-size:.76rem;color:var(--muted);margin-top:.1rem}
.bulk-bar select{max-width:230px}
@media (max-width:700px){
  .bulk-bar{gap:.45rem}
  .bulk-bar select{max-width:100%;flex:1 1 100%}
}

/* ---------- Tour / route catalogue cards ---------- */
.tour-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(290px,1fr));gap:1.1rem}
.tour-card{background:#fff;border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow-sm);overflow:hidden;display:flex;flex-direction:column;transition:.14s}
.tour-card:hover{box-shadow:var(--shadow-md);border-color:#cbd6ce;transform:translateY(-2px)}
.tour-card .banner{height:88px;display:flex;align-items:flex-end;justify-content:space-between;
  padding:.7rem .9rem;color:#fff;gap:.5rem;
  background:linear-gradient(135deg,var(--bush-700),var(--bush-900))}
.tour-card .banner.t1{background:linear-gradient(135deg,#2c5c41,#10231a)}
.tour-card .banner.t2{background:linear-gradient(135deg,#b8621b,#7a3d0d)}
.tour-card .banner.t3{background:linear-gradient(135deg,#0f7d72,#0a4a44)}
.tour-card .banner.t4{background:linear-gradient(135deg,#d4a03c,#8a6212)}
.tour-card .banner.t5{background:linear-gradient(135deg,#6d43b8,#3f2570)}
.tour-card .banner .ico{opacity:.9}
.tour-card .banner .dur{font-size:.7rem;font-weight:700;background:rgba(0,0,0,.28);
  padding:.15rem .5rem;border-radius:999px;white-space:nowrap}
.tour-card .body{padding:.95rem 1rem;flex:1;display:flex;flex-direction:column}
.tour-card h4{font-size:1rem;line-height:1.3;margin-bottom:.3rem}
.tour-card .desc{font-size:.82rem;color:var(--muted);line-height:1.45;flex:1}
.tour-card .meta{display:flex;flex-wrap:wrap;gap:.3rem;margin:.6rem 0 .1rem}
.tour-card .price{display:flex;align-items:baseline;gap:.4rem;margin-top:.8rem;
  padding-top:.75rem;border-top:1px solid var(--line-2)}
.tour-card .price .from{font-size:.7rem;color:var(--muted);text-transform:uppercase;
  letter-spacing:.05em;font-weight:700}
.tour-card .price .amt{font-size:1.22rem;font-weight:800;color:var(--brand-dark)}
.tour-card .price .pp{font-size:.74rem;color:var(--muted)}
.tour-card .price .spacer{flex:1}

/* rate card table (published tier ladder) */
table.rates{width:100%;border-collapse:collapse;font-size:.85rem}
table.rates th{text-align:left;font-size:.68rem;text-transform:uppercase;letter-spacing:.05em;
  color:var(--muted);padding:.45rem .6rem;border-bottom:1px solid var(--line);font-weight:800}
table.rates td{padding:.45rem .6rem;border-bottom:1px solid var(--line-2);font-variant-numeric:tabular-nums}
table.rates tr:last-child td{border-bottom:none}
/* Same change as table.data above, same reason. The .rates tables already set
   tabular-nums on every td, so only the alignment moves here. */
table.rates td.r,table.rates th.r{text-align:center}
table.rates tr.hit td{background:var(--brand-soft);font-weight:700}

/* ---------- Quote panel (wizard + detail + public page) ---------- */
.quote-box{background:#fff;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}
.quote-box .qh{background:var(--bush-800);color:#fff;padding:.7rem 1rem;
  font-size:.72rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;
  display:flex;align-items:center;gap:.5rem}
.quote-box .qh .spacer{flex:1}
.qline{display:flex;gap:.8rem;padding:.6rem 1rem;border-bottom:1px solid var(--line-2);font-size:.86rem}
.qline .l{flex:1;min-width:0}
.qline .l b{font-weight:600;display:block}
.qline .l small{color:var(--muted);font-size:.75rem}
.qline .q{color:var(--muted);font-size:.8rem;white-space:nowrap;font-variant-numeric:tabular-nums}
.qline .a{font-weight:700;white-space:nowrap;font-variant-numeric:tabular-nums;min-width:5.5rem;text-align:right}
.qline.discount .a{color:var(--green)}
.qline.discount .l b{color:var(--green)}
.qtotals{padding:.8rem 1rem;background:#fafbf9}
.qtotals .row{display:flex;justify-content:space-between;gap:1.2rem;padding:.24rem 0;font-size:.88rem}
.qtotals .row span{color:var(--muted)}
.qtotals .row b{font-variant-numeric:tabular-nums}
.qtotals .row.grand{border-top:2px solid var(--bush-800);margin-top:.45rem;padding-top:.55rem;font-size:1.16rem}
.qtotals .row.grand span{color:var(--ink);font-weight:800}
.qtotals .row.grand b{font-weight:800;color:var(--brand-dark)}
.qtotals .row.sub{font-size:.8rem}
.qtotals .row.sub b{font-weight:600}
.qwarn{display:flex;gap:.55rem;padding:.6rem 1rem;background:#fff8e8;border-top:1px solid #f2e0b8;
  font-size:.8rem;color:#7a5410;line-height:1.45}
.qwarn svg{flex:none;margin-top:.1rem;color:var(--amber)}

/* ---------- Map panel (places.js) ----------
   The preview is an OpenStreetMap iframe; the buttons are plain links that open
   the phone's own navigation app. The frame keeps a fixed aspect so the layout
   does not jump while the tiles load, and the actions stay usable even if the
   iframe never loads at all (no signal, blocked embed) — which is the whole
   reason the buttons are siblings of the map rather than overlaid on it. */
.map-panel{border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;background:#fff}
.map-frame{position:relative;aspect-ratio:16/9;background:#e8eee9;
  background-image:linear-gradient(135deg,#e8eee9 25%,#dfe7e1 25%,#dfe7e1 50%,
    #e8eee9 50%,#e8eee9 75%,#dfe7e1 75%);background-size:16px 16px}
.map-frame.compact{aspect-ratio:2/1}
.map-frame iframe{position:absolute;inset:0;width:100%;height:100%;border:0;display:block}
.map-badge{position:absolute;left:.6rem;bottom:.6rem;z-index:2;
  background:rgba(16,35,26,.88);color:#fff;font-size:.74rem;font-weight:700;
  padding:.28rem .65rem;border-radius:999px;backdrop-filter:blur(3px);
  max-width:calc(100% - 1.2rem);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  pointer-events:none}
/* Shown over the frame when the tiles never arrive — see places.js. */
.map-stall{position:absolute;inset:0;z-index:1;display:flex;align-items:center;justify-content:center;
  gap:.7rem;padding:1rem;text-align:left;color:var(--muted);background:#eef2ef}
.map-stall svg{flex:none;opacity:.5}
.map-stall b{display:block;font-size:.85rem;color:var(--ink)}
.map-stall span{font-size:.76rem;line-height:1.4}
.map-actions{display:flex;flex-wrap:wrap;gap:.45rem;padding:.75rem;
  border-top:1px solid var(--line-2);align-items:center}
.map-actions .map-go{flex:1 1 auto;min-width:200px}
.map-note{padding:0 .8rem .75rem;font-size:.76rem;color:var(--muted);line-height:1.5}
.map-note.warn{color:#7a5410}

/* ---------- Notes / callouts ---------- */
.note{display:flex;gap:.7rem;padding:.85rem 1rem;border-radius:var(--radius-sm);
  font-size:.85rem;line-height:1.5;border:1px solid var(--line);background:#fafbf9;color:#334a41}
.note svg{flex:none;margin-top:.1rem}
.note.info{background:#eef5fb;border-color:#cfe0f0;color:#20496f}
.note.info svg{color:#2b5f9e}
.note.warn{background:#fff8e8;border-color:#f2e0b8;color:#7a5410}
.note.warn svg{color:var(--amber)}
.note.danger{background:#fdecea;border-color:#f3ccc7;color:#8c281c}
.note.danger svg{color:var(--red)}
.note.ok{background:#e9f6ee;border-color:#c5e5d1;color:#155f34}
.note.ok svg{color:var(--green)}
.note b{font-weight:700}

/* ---------- Dashboard alert cards (admin_alerts) ---------- */
/* A slow colour pulse, not an opacity strobe — it has to hold attention
   across a room without being an eyesore or an accessibility problem while
   the card sits there for hours until somebody resolves it. */
@keyframes alertflash{0%,100%{background:#fdecea;border-color:#f3ccc7}
  50%{background:#f7c2ba;border-color:#e2695c}}
.alert-card{animation:alertflash 2s ease-in-out infinite}
/* Every alert row (flashing or resolved) stacks title/details/notes as their
   own lines — b and small are inline by default, which ran a resolved card's
   title, timestamp and notes together as one unbroken line. */
.alert-row .note b{display:block;margin-bottom:.15rem}
.alert-row .note small{display:block;color:inherit;opacity:.8;font-weight:400;margin-top:.15rem}
.alert-row .note .alert-notes{font-style:italic}

/* ---------- Key/value detail blocks ---------- */
.kv{display:grid;grid-template-columns:auto 1fr;gap:.42rem 1rem;font-size:.87rem;align-items:baseline}
.kv dt{color:var(--muted);font-weight:600;white-space:nowrap}
.kv dd{margin:0;font-weight:600}
.kv dd.plain{font-weight:400}

/* ---------- Day sheet / schedule ---------- */
.daysheet{display:flex;flex-direction:column;gap:1.1rem}
.day-block{border:1px solid var(--line);border-radius:var(--radius);background:#fff;overflow:hidden;
  box-shadow:var(--shadow-sm)}
.day-block > .dh{display:flex;align-items:center;gap:.7rem;padding:.75rem 1.1rem;
  background:linear-gradient(135deg,var(--bush-800),var(--bush-700));color:#fff}
.day-block > .dh b{font-size:.95rem}
.day-block > .dh .rel{font-size:.74rem;color:#a9c2b3;text-transform:uppercase;letter-spacing:.05em;font-weight:700}
.day-block > .dh .spacer{flex:1}
.day-block > .dh .tot{font-size:.78rem;background:rgba(255,255,255,.14);padding:.2rem .6rem;border-radius:999px}
.day-block.today > .dh{background:linear-gradient(135deg,var(--brand),var(--brand-dark))}
.trip{display:flex;gap:.9rem;padding:.85rem 1.1rem;border-bottom:1px solid var(--line-2);cursor:pointer;
  transition:.12s;align-items:flex-start}
.trip:last-child{border-bottom:none}
.trip:hover{background:#f7faf7}
/* The bulk-select tick-box (opts.picked in tripRow) — only present when a
   caller opts in, so this costs nothing on the day sheet or a driver's own
   screen. Top-aligned with .time beside it rather than centred on the
   whole row, which is taller once a trip has badges under it. */
.trip .pickcol{flex:none;padding-top:.1rem}
.trip.picked{background:var(--brand-soft)}
/* The date, stacked above the pick-up time — a lodge can have several trips
   on the same product across the next 30 days, and until this the only way
   to tell them apart was the time, which repeats across different days
   (asked for 11 Sep 2026, off the dashboard's "Next trips out" list). */
.trip .time{flex:none;width:3.6rem}
.trip .time .time-date{font-size:.66rem;font-weight:700;color:var(--muted);line-height:1.2}
.trip .time .time-hm{font-weight:800;font-size:.9rem;line-height:1.3}
.trip .core{flex:1;min-width:0}
.trip .core b{display:block;font-size:.9rem;line-height:1.3}
.trip .core .l2{font-size:.79rem;color:var(--muted);margin-top:.15rem}
.trip .core .l3{display:flex;flex-wrap:wrap;gap:.3rem;margin-top:.45rem}
.trip .core .l4{font-size:.79rem;color:var(--muted);margin-top:.3rem}
.trip .side{flex:none;text-align:right}
.trip .side .amt{font-weight:800;font-variant-numeric:tabular-nums;font-size:.9rem}
.trip .side .pax{font-size:.75rem;color:var(--muted);margin-top:.15rem}

/* The row's fill is the TOUR's own colour, set inline per booking (see
   TripColours in calendar.js and tripRow() in views.js) — asked for so
   Panorama and Eswatini read apart on the schedule the same way they do on
   the calendar. Status already has its own badge inside the row (see
   UI.statusBadge) rather than a second, competing background. Only a
   cancelled trip is still marked here, by fading the whole row — that is a
   statement about the row, not one more colour to learn. */
.trip.s-cancelled{opacity:.55}
.trip.unassigned{border-left:3px solid var(--amber)}
.trip.unassigned .core b::after{content:" · needs a vehicle";color:var(--amber);font-weight:700;font-size:.76rem}

/* The dashboard's "group by lodge" list (Views.dashboard → tripRow +
   actionRow in views.js): each trip and its Details/Price/Status toolbar as
   ONE card, not a coloured row with a second, unbordered row of buttons
   floating underneath it. */
.trip-card{border:1px solid var(--line);border-radius:var(--radius-sm);
  overflow:hidden;background:var(--card);box-shadow:var(--shadow-sm)}
.trip-card .trip{border-bottom:1px solid var(--line)}
.trip-card-actions{display:flex;flex-wrap:wrap;gap:.4rem;
  padding:.55rem .7rem;background:var(--line-2)}
.trip-card-actions .btn{flex:1 1 auto;border-radius:8px}

/* ===========================================================================
   CALENDAR (calendar.js)
   Month grid, week columns and the vehicle-utilisation matrix. Chips are
   colour-coded by what the entry IS (departure / tour / transfer / shuttle)
   and gain an amber left edge when nothing is allocated to them.
   =========================================================================== */
.cal-bar{display:flex;align-items:center;gap:.7rem;flex-wrap:wrap;margin-bottom:1rem}
.cal-bar .spacer{flex:1}
.cal-nav{display:flex;gap:.35rem;align-items:center}
.cal-period{font-size:1.15rem;font-weight:800;letter-spacing:-.01em;min-width:9rem}
.cal-modes .chip{padding:.38rem .85rem;font-size:.83rem}

/* The single left/right bar sits after the grid and sticks to the bottom of the
   window, so it is on screen whenever any of the calendar is. The grid's own
   bar is hidden — this one drives it. */
/* The calendar scrolls inside its own window-height box, so ITS left/right bar
   is the one at the bottom of the screen — not the page's. */
.cal-scroll-top{display:block;position:sticky;bottom:0;z-index:30;overflow-x:scroll;overflow-y:hidden;
  height:18px;margin-bottom:.5rem;scrollbar-width:auto;background:#fff;border-top:1px solid var(--line);
  box-shadow:0 -3px 8px rgba(0,0,0,.08)}
.cal-scroll-top-inner{height:1px}
/* The calendar scrolls inside its own box, which fills the screen below the
   toolbar, so its left/right bar is always at the bottom of the window. */
.cal-grid-wrapper{overflow-x:auto;overflow-y:hidden;margin-top:1rem;margin-bottom:0;
  position:relative;scrollbar-width:none}
.cal-grid-wrapper::-webkit-scrollbar{display:none}
.cal-grid-wrapper .cal-grid{overflow:visible;min-width:1680px}
.today-tag{display:inline-block;margin-right:auto;background:#f6a5a5;color:#7a1414;
  border-radius:999px;padding:.12rem .55rem;font-size:.7rem;font-weight:900;letter-spacing:.08em;line-height:1.3;
  box-shadow:0 1px 3px rgba(190,60,60,.35)}
.wk-head .today-tag,table.vh-grid thead th .today-tag{display:inline-block;margin:.25rem auto 0;font-size:.66rem;
  text-transform:none}
.cal-cell.today{background:#fff1f1;box-shadow:inset 0 0 0 2px #f08a8a}
.cal-cell.today:hover{background:#ffe8e8}
.wk-col.today{box-shadow:inset 0 0 0 2px #f08a8a;background:#fff6f6}
/* ⚠ WAS minmax(calc(22vw),1fr) — 7 columns × 22vw is 154% of the viewport,
   so the grid ALWAYS overflowed sideways on a normal desktop window,
   showing 2-3 days and a horizontal scrollbar no matter how wide the
   browser was. Every responsive rule below this one already keeps the
   month grid at 7 equal columns and shrinks cell HEIGHT instead of
   triggering scroll — this was the one rule fighting that. minmax(0,1fr)
   lets all 7 columns actually share the full width, same as they do on
   every screen size below 1024px already. */
/* Grid lines darkened on request — the ordinary --line (#e2e8e3) is barely
   visible at the 1px this grid-gap trick draws it at, and a calendar you
   can't see the day boundaries on doesn't read as a calendar. This colour
   is local to .cal-grid, not a change to --line itself, which borders cards
   and inputs everywhere else in the app and must stay light. */
/* ⚠ A FLOOR ON EACH COLUMN, not just 1fr. `minmax(0,1fr)` let seven columns
   shrink to whatever fit the window, and a trip's own description — "SS ·
   OR Tambo → Marloth" plus its time and its "×10 ·21" — needs more than that
   squeezed to a seventh of even a wide screen. Below the floor the grid now
   scrolls horizontally (.cal-grid-wrapper already sets overflow-x:auto, with
   the synced .cal-scroll-top bars above the grid for reaching it without
   scrolling all the way down first) instead of truncating every chip's text
   down to an ellipsis.
     6 Sep 2026: "make blocks wider to fit" — raised the floor from 0 to
     150px, which fit inside most desktop windows and so never actually
     triggered the scrollbar.
     Same day, follow-up: "make all blocks width allot bigger and put in a
     scroll bar left to right" — 150px still fit. 240px does not fit a typical
     desktop content width at 7 columns, so the horizontal scroll (and the
     scroll bars above the grid) are now genuinely in play, and each day has
     real room for its longest lines. */
.cal-grid{display:grid;grid-template-columns:repeat(7,minmax(240px,1fr));gap:1px;
  background:#333;border:1px solid #333;border-radius:var(--radius);
  overflow-x:auto;overflow-y:visible}
.cal-dow{background:#fafbf9;padding:.5rem .6rem;font-size:.68rem;font-weight:800;
  text-transform:uppercase;letter-spacing:.06em;color:var(--muted);text-align:center}
/* Doubled from 118px, 7 Sep 2026 — "make the blocks height double what it is
   now" — each day now has real room for eight chips before "+more" kicks in,
   rather than the same height it had when three chips were the cut. */
.cal-cell{background:#fff;min-height:236px;padding:.4rem .4rem .5rem;cursor:pointer;
  display:flex;flex-direction:column;gap:.25rem;transition:.12s;position:relative}
.cal-cell:hover{background:#f7faf7}
.cal-cell.out{background:#fbfcfb}
.cal-cell.out .cal-daynum{color:#b6c2ba}
.cal-cell.weekend:not(.out){background:#fcfdfb}
.cal-cell.weekend:not(.out):hover{background:#f5f9f4}
.cal-cell.today{box-shadow:inset 0 0 0 2px var(--brand)}
.cal-daynum{display:flex;align-items:center;justify-content:space-between;gap:.3rem;
  font-size:.82rem;font-weight:700;color:var(--ink);padding:.05rem .1rem .1rem}
.cal-cell.today .cal-daynum > span:first-child{background:var(--brand);color:#fff;
  border-radius:50%;width:22px;height:22px;display:grid;place-items:center;font-size:.76rem}
.cal-daynum .cnt{font-size:.66rem;font-weight:800;color:var(--muted);
  background:var(--line-2);border-radius:999px;padding:.05rem .38rem}
.cal-chips{display:flex;flex-direction:column;gap:2px;min-width:0}
/* Reads as a way into the day, not as a label — the cell it sits in already
   opens that day. Asked for 6 Sep 2026 along with raising the chip cut from
   three to eight; see the note over that cut in calendar.js. */
.cal-more{font-size:.68rem;color:var(--brand);font-weight:700;padding-left:.15rem;
  cursor:pointer;text-decoration:underline;text-underline-offset:2px}
.cal-more:hover{color:var(--brand-dark)}

/* the chip itself — shared by month, week and the vehicle matrix.
   Vertical padding doubled alongside .cal-cell, 7 Sep 2026, for the same
   reason — each block reads as a real block now, not a squeezed-in line. */
.cal-chip{display:flex;align-items:center;gap:.3rem;width:100%;text-align:left;
  border:none;border-left:3px solid var(--muted);border-radius:4px;
  background:var(--line-2);color:var(--ink);cursor:pointer;
  padding:.32rem .3rem;font:inherit;font-size:.7rem;line-height:1.3;
  overflow:hidden;transition:.1s}
.cal-chip:hover{filter:brightness(.96);transform:translateX(1px)}
.cal-chip .t{font-weight:800;font-variant-numeric:tabular-nums;flex:none;opacity:.75}
.cal-chip .n{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The guest count — "×7 ·12" — is the number an operator actually scans the
   month for, so it reads at full size and contrast while the rest of the
   chip stays compact (asked for 12 Oct 2026: "see people for each trip
   bigger"). */
.cal-chip .s{flex:none;font-weight:800;font-variant-numeric:tabular-nums;
  opacity:1;font-size:.86rem}
/* Several parties on the same tour, collapsed into one chip — see
   collapseSameProduct() in calendar.js. A doubled left edge, because it stands
   for more than one booking and should not read as a single trip. */
.cal-chip.grouped{border-left-width:6px;border-left-style:double;font-weight:600}
/* Left edge = WHAT KIND of trip (tour/transfer/shuttle) — same rule for a
   plain booking chip and a scheduled-departure chip. The FILL is set inline
   per product name by TripColours (calendar.js), so Panorama and Eswatini
   read apart even though both are "tour" — see chip() there. */
.cal-chip.tour, .cal-chip.departure.t-tour{border-left-color:#1b5e20}
.cal-chip.transfer, .cal-chip.departure.t-transfer{border-left-color:#0d47a1}
.cal-chip.lodge_transfer, .cal-chip.departure.t-lodge_transfer{border-left-color:#6a4c93}
.cal-chip.shared_shuttle, .cal-chip.departure.t-shared_shuttle{border-left-color:#4a148c}
.cal-chip.departure{border-left-color:var(--gold)}
/* Seat state = an inset ring on top of the activity colour, so both questions
   ("what is it" and "is it full/at risk") can be answered from one chip. */
.cal-chip.departure.st-full{box-shadow:inset 0 0 0 2px #1b5e20}
.cal-chip.departure.st-low{box-shadow:inset 0 0 0 2px #d98b6a}
.cal-chip.departure.st-over{box-shadow:inset 0 0 0 2px var(--red)}
.cal-chip.warn{border-left-color:var(--amber);box-shadow:inset 2px 0 0 var(--amber)}

.cal-totals{display:flex;flex-wrap:wrap;gap:.4rem 1.6rem;margin-top:.9rem;padding:.75rem 1rem;
  background:#fff;border:1px solid var(--line);border-radius:var(--radius);
  font-size:.83rem;color:var(--muted)}
.cal-totals b{color:var(--ink);font-weight:800;font-variant-numeric:tabular-nums}

/* An author `display:flex` outranks the browser's own [hidden] rule, so the
   Day view could not hide this by setting .hidden without saying so here. */
.cal-legend[hidden]{display:none}

/* The dashboard's service sliders (views.js tripsBlock) — same trap, same
   fix: the open drawer is a grid, and an inline display:grid would win over
   [hidden]. Found 16 Sep 2026 in the headless probe: hidden=true, 1157px tall. */
.slider-body{display:grid;gap:1rem;padding:1rem .9rem .9rem}
.slider-body[hidden]{display:none}
.cal-legend{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem 1.1rem;margin-top:1rem;
  font-size:.76rem;color:var(--muted)}
.cal-legend .spacer{flex:1}
.cal-legend span{display:inline-flex;align-items:center;gap:.4rem}
.cal-legend .k{width:12px;height:12px;border-radius:3px;display:inline-block;flex:none}
.cal-legend .k.departure{background:#fbf1dc;border-left:3px solid var(--gold)}
.cal-legend .k.tour{background:#eaf3ea;border-left:3px solid var(--green)}
.cal-legend .k.transfer{background:#e9eff8;border-left:3px solid #2b5f9e}
.cal-legend .k.lodge_transfer{background:#f0ecf5;border-left:3px solid #6a4c93}
.cal-legend .k.shared_shuttle{background:#f2ecfa;border-left:3px solid var(--purple)}
.cal-legend .k.warn{background:#fff4e2;border-left:3px solid var(--amber)}
.cal-legend .k.hol{background:#fdf6e3;border-left:3px solid var(--gold)}

/* ---- public holidays (holidays.js) ----
   A gold left edge on the cell and the name on a ribbon under the date. The
   tint is deliberately weak: a holiday is CONTEXT for the day's trips, not a
   thing competing with them, and a strongly coloured cell in a month grid
   reads as "something is wrong here". */
.cal-cell.holiday:not(.out){background:#fffdf6;box-shadow:inset 3px 0 0 var(--gold)}
.cal-cell.holiday:not(.out):hover{background:#fdf8ea}
.cal-cell.today.holiday{box-shadow:inset 0 0 0 2px var(--brand),inset 3px 0 0 var(--gold)}
.cal-hol{display:flex;align-items:center;gap:.25rem;min-width:0;font-size:.66rem;
  font-weight:800;color:#8a6a12;line-height:1.25}
.cal-hol .dot{flex:none;width:5px;height:5px;border-radius:50%;background:var(--gold)}
.cal-hol .nm{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cal-hol.k-company{color:var(--muted)}
.cal-hol.k-company .dot{background:var(--muted)}
.cal-cell.out .cal-hol{opacity:.45}

.cal-totals .hol b{color:#8a6a12}
/* Every holiday in the month, named and clickable, under the totals. The
   ribbon in a cell truncates at about fourteen characters — "Day of
   Reconciliation" does not fit anywhere in a month grid — so the full names
   live here, and each one opens that day. */
.cal-hol-list{display:flex;flex-wrap:wrap;gap:.4rem;margin-top:.6rem}
.cal-hol-pill{display:inline-flex;align-items:center;gap:.4rem;border:1px solid #eadfc0;
  background:#fffdf6;border-radius:999px;padding:.25rem .7rem;font:inherit;font-size:.75rem;
  color:#7a5f10;cursor:pointer;transition:.12s}
.cal-hol-pill:hover{border-color:var(--gold);background:#fdf6e3}
.cal-hol-pill .d{font-weight:800;font-variant-numeric:tabular-nums}
.cal-hol-pill.k-company{border-color:var(--line);background:#fafbf9;color:var(--muted)}

.cal-hol-banner{display:flex;align-items:center;gap:.65rem;padding:.6rem .85rem;
  background:#fffdf6;border:1px solid #eadfc0;border-left:4px solid var(--gold);
  border-radius:var(--radius-sm);margin-bottom:1rem}
.cal-hol-banner .ico{display:inline-flex;flex:none;color:#b08b1c}
.cal-hol-banner b{display:block;font-size:.9rem;color:#6d5410}
.cal-hol-banner span{display:block;font-size:.76rem;color:#8a7534;margin-top:.1rem}
.cal-hol-banner.k-company{border-left-color:var(--muted);background:#fafbf9;border-color:var(--line)}

/* week + vehicle matrix */
.wk-col.holiday .wk-head{background:#fffaec}
.wk-col.holiday > .cal-hol{padding:.3rem .5rem;justify-content:center;
  border-bottom:1px solid var(--line-2);background:#fffdf6}
table.vh-grid thead th.holiday{background:#fffaec;color:#8a6a12}
table.vh-grid thead th.holiday .hol-mark{display:block;font-size:.62rem;font-weight:800;
  color:#8a6a12;margin-top:.15rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
table.vh-grid td.holiday{background:#fffdf6}
table.vh-grid td.holiday:hover{background:#fdf8ea}
table.vh-grid td.today.holiday{background:#fff7ea}

/* ===========================================================================
   CHECKS & NOTES REPORT (checkreport.js)

   A reading screen, so it is typeset like something meant to be read rather
   than scanned: the note itself is the largest thing in its row, and everything
   around it — where it came from, who wrote it, what record it belongs to — is
   deliberately quieter than the sentence a driver typed at the vehicle.
   =========================================================================== */
.note-card{transition:.12s}
.note-card:hover{border-color:var(--brand);box-shadow:var(--shadow-sm);transform:translateY(-1px)}
/* A reported fault, a failed inspection, a declined leave request, a driver out
   of pocket at a pump. One mark, four meanings, all of them "somebody has to do
   something about this". */
.note-card.flag{border-left:3px solid var(--red)}

.note-src{display:inline-flex;align-items:center;gap:.3rem;flex:none;
  font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.04em;
  color:var(--muted);background:#f4f6f4;border-radius:20px;padding:.15rem .5rem}
.note-src svg{opacity:.7}
.note-text{font-size:.92rem;line-height:1.5;white-space:pre-wrap;word-break:break-word}
.note-ctx{margin-top:.35rem;font-size:.75rem;color:var(--muted)}

/* The lines printed under a checklist row — the whole reason this screen is not
   just Completed checks with a date filter. */
.note-lines{margin-top:.6rem;padding-top:.55rem;border-top:1px solid var(--line-2);
  display:flex;flex-direction:column;gap:.3rem}
.note-line{display:flex;gap:.6rem;align-items:baseline;font-size:.83rem;line-height:1.45}
.note-line .f{flex:none;min-width:6.5rem;font-weight:800;font-size:.7rem;
  text-transform:uppercase;letter-spacing:.04em;color:var(--muted);padding-top:.1rem}
.note-line .t{flex:1;min-width:0;white-space:pre-wrap;word-break:break-word}
.note-line.warn .f{color:var(--red)}
.note-line.warn .t{color:#8c281c;font-weight:600}

@media (max-width:640px){
  .note-line{flex-direction:column;gap:.1rem}
  .note-line .f{min-width:0}
}

@media print{
  /* Printed, this is a filed document — the toolbar and the filters are not
     part of it, and a card that lifts on hover should sit flat on paper. */
  .note-card{break-inside:avoid;box-shadow:none;transform:none}
}

/* ===========================================================================
   DRIVER AVAILABILITY CALENDAR (roster.js → calendarView)

   The booking calendar above shows WHAT is going out. This one shows WHO can
   take it, and the difference drives every decision below: a day cell here is
   not a list of trips that happens to be short, it is a roll-call of the whole
   driver list, every day, whether or not there is anything on. So the cell has
   no "+3 more" — hiding the sixth driver would hide exactly the person the
   office is looking for — and it grows a scrollbar instead once a business has
   more drivers than a cell can hold.

   COLOUR IS THE ANSWER, THE WORD IS THE PROOF. Green reads "free" down a
   column at a glance; the status word is printed next to the name anyway,
   because a calendar that can only be read by people who see colour the way we
   do is not a calendar for a duty roster.
   =========================================================================== */
.av-grid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:1px;
  background:var(--line);border:1px solid var(--line);border-radius:var(--radius);
  overflow:hidden}
.av-dow{background:#fafbf9;padding:.5rem .6rem;font-size:.68rem;font-weight:800;
  text-transform:uppercase;letter-spacing:.05em;color:var(--muted);text-align:center}
.av-cell{background:#fff;min-height:132px;padding:.4rem .4rem .5rem;
  display:flex;flex-direction:column;gap:.25rem}
.av-cell.out{background:#fbfcfb}
.av-cell.out .av-daynum{color:#b6c2ba}
.av-cell.out .av-names{opacity:.5}
.av-cell.weekend:not(.out){background:#fcfdfb}
.av-cell.today{box-shadow:inset 0 0 0 2px var(--brand)}
.av-cell.holiday:not(.out){background:#fffdf6;box-shadow:inset 3px 0 0 var(--gold)}
.av-cell.today.holiday{box-shadow:inset 0 0 0 2px var(--brand),inset 3px 0 0 var(--gold)}
/* A day this business cannot take another booking on. Marked on the cell, not
   only in the count, because it is the one thing on this screen worth
   interrupting somebody about. */
.av-cell.none-free:not(.out){background:#fef7f6;box-shadow:inset 0 0 0 1px #f3d4cf}
.av-cell.today.none-free{box-shadow:inset 0 0 0 2px var(--brand)}
.av-daynum{display:flex;align-items:center;justify-content:space-between;gap:.3rem;
  font-size:.82rem;font-weight:800;line-height:1}
.av-cell.today .av-daynum > span:first-child{background:var(--brand);color:#fff;
  border-radius:50%;width:20px;height:20px;display:grid;place-items:center;font-size:.72rem}
.av-daynum .cnt{font-size:.63rem;font-weight:800;color:var(--green);
  background:#e6f5ec;border-radius:20px;padding:.1rem .4rem}
.av-daynum .cnt.zero{color:var(--red);background:#fdecea}
.av-hol{display:flex;align-items:center;gap:.25rem;min-width:0;font-size:.64rem;
  font-weight:700;color:#8a6a12}
.av-hol .dot{flex:none;width:5px;height:5px;border-radius:50%;background:var(--gold)}
.av-hol .nm{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.av-names{display:flex;flex-direction:column;gap:2px;min-width:0;
  max-height:190px;overflow-y:auto}
.av-name{display:flex;align-items:center;gap:.3rem;width:100%;text-align:left;
  border:none;border-left:3px solid transparent;border-radius:4px;
  padding:.16rem .3rem;font:inherit;font-size:.7rem;cursor:pointer;transition:.12s}
.av-name:hover{filter:brightness(.96);transform:translateX(1px)}
.av-name:focus-visible{outline:2px solid var(--brand);outline-offset:1px}
.av-name .n{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-weight:700}
.av-name .s{flex:none;font-weight:700;opacity:.8;font-size:.63rem}

/* ONE PALETTE, THREE PLACES — the name pill in a month cell, the square in the
   by-driver grid and the legend key. Every selector is scoped to this screen on
   purpose: `.s-off` on its own is exactly the kind of class name that would
   quietly repaint something in another module a year from now. */
.av-name.s-free,   .av-sq.s-free,   .av-legend .k.s-free
  {background:#e6f5ec;border-left-color:var(--green);color:#14532d}
.av-name.s-trip,   .av-sq.s-trip,   .av-legend .k.s-trip
  {background:var(--brand-soft);border-left-color:var(--brand);color:var(--brand-dark)}
.av-name.s-off,    .av-sq.s-off,    .av-legend .k.s-off
  {background:#eef2f7;border-left-color:#94a3b3;color:#4a5b6e}
.av-name.s-standby,.av-sq.s-standby,.av-legend .k.s-standby
  {background:#fff4e2;border-left-color:var(--amber);color:#8a5a0c}
.av-name.s-admin,  .av-sq.s-admin,  .av-legend .k.s-admin
  {background:#f4eefb;border-left-color:var(--purple);color:#432a75}
.av-name.s-leave,  .av-sq.s-leave,  .av-legend .k.s-leave
  {background:#fbf3df;border-left-color:var(--gold);color:#8a6212}
.av-name.s-sick,   .av-sq.s-sick,   .av-legend .k.s-sick
  {background:#fdecea;border-left-color:var(--red);color:#8c281c}
.av-name.s-other,  .av-sq.s-other,  .av-legend .k.s-other
  {background:#eef2f7;border-left-color:#94a3b3;color:#4a5b6e}
.av-legend .k{border-left-width:3px;border-left-style:solid}

/* ---- by driver: rows are people, columns are the days of the month ---- */
table.av-drv{width:100%;border-collapse:collapse;font-size:.84rem;background:#fff;
  min-width:1100px}
table.av-drv th{text-align:left;padding:.5rem .6rem;border-bottom:1px solid var(--line);
  background:#fafbf9;vertical-align:top}
table.av-drv thead th{text-align:center;padding:.4rem .1rem;font-size:.66rem;
  color:var(--muted);text-transform:uppercase;letter-spacing:.03em}
table.av-drv thead th b{display:block;color:var(--ink);font-size:.76rem}
table.av-drv thead th span{display:block;font-weight:600;font-size:.6rem;margin-top:.1rem}
table.av-drv thead th.weekend{background:#f4f6f4}
table.av-drv thead th.today{background:var(--brand-soft);color:var(--brand-dark)}
table.av-drv thead th.holiday{background:#fffaec;color:#8a6a12}
table.av-drv tbody th{width:180px;border-right:1px solid var(--line);position:sticky;left:0;
  z-index:1}
table.av-drv tbody th b{display:block;font-size:.86rem}
table.av-drv tbody th span{display:block;font-size:.7rem;color:var(--muted);margin-top:.1rem}
table.av-drv tbody th .util{display:inline-block;margin-top:.3rem;font-size:.64rem;font-weight:800;
  padding:.1rem .4rem;border-radius:20px;background:#e6f5ec;color:var(--green)}
table.av-drv tbody th .util.warm{background:#fff4e2;color:var(--amber)}
table.av-drv tbody th .util.hot{background:#fdecea;color:var(--red)}
.av-sq{border-bottom:1px solid var(--line-2);border-right:1px solid var(--line-2);
  text-align:center;padding:0;cursor:pointer;transition:.1s}
.av-sq span{display:block;padding:.5rem .1rem;font-size:.66rem;font-weight:800}
.av-sq:hover{filter:brightness(.94)}
.av-sq.today span{box-shadow:inset 0 0 0 2px var(--brand)}
tr.av-tally-row th{background:#fff;border-top:2px solid var(--line)}
.av-tally{text-align:center;border-right:1px solid var(--line-2);border-top:2px solid var(--line);
  background:#fafbf9}
.av-tally span{display:block;padding:.45rem .1rem;font-size:.72rem;font-weight:800;
  color:var(--green);font-variant-numeric:tabular-nums}
.av-tally.zero span{color:var(--red);background:#fdecea}
.av-tally.today span{box-shadow:inset 0 0 0 2px var(--brand)}

@media (max-width:760px){
  /* Seven columns of six names each is unreadable on a phone. The grid becomes
     one day per row — still the whole month, still every name on every day,
     which is the point of the screen. */
  .av-grid{grid-template-columns:1fr}
  .av-dow{display:none}
  .av-cell{min-height:0}
  .av-cell.out{display:none}
  .av-names{max-height:none}
  .av-name{font-size:.76rem}
}

/* ===========================================================================
   THE DAY PANEL'S DRILL-THROUGH (calendar.js)
   A departure is one row in the calendar and five bookings underneath it. The
   row stays the summary; the caret opens the parties. Each party is a link
   into its own booking, which is the whole point — "rolled up" must never mean
   "unreachable".
   =========================================================================== */
.dd{border-bottom:1px solid var(--line-2)}
.dd:last-child{border-bottom:none}
.dd .trip{border-bottom:none}
.dd-head.expandable{cursor:pointer}
.dd-acts{display:flex;align-items:center;justify-content:flex-end;gap:.35rem;margin-top:.4rem}
.dd-edit{display:grid;place-items:center;width:26px;height:26px;flex:none;padding:0;
  border:1px solid var(--line);border-radius:7px;background:#fff;color:var(--muted);
  cursor:pointer;transition:.12s}
.dd-edit:hover{border-color:var(--brand);color:var(--brand-dark);background:var(--brand-soft)}
/* ---------- Dashboard: the business at a glance (plain SVG charts) ---------- */
.ins-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1rem}
@media (max-width:860px){.ins-grid{grid-template-columns:minmax(0,1fr)}}
.ins-card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow-sm);padding:1rem 1.1rem .9rem;min-width:0}
.ins-h{font-weight:800;font-size:.98rem;letter-spacing:-.01em}
.ins-sub{font-size:.8rem;color:var(--muted);margin:.15rem 0 .7rem;min-height:1.2em}
.ins-wrap{position:relative}
.ins-svg{display:block;width:100%;height:auto;overflow:visible}
.ins-gl{stroke:var(--line);stroke-width:1}
.ins-base{stroke:#c9d2cc;stroke-width:1.5}
.ins-ax{font:500 11px var(--font);fill:var(--muted)}
.ins-now{fill:var(--ink);font-weight:800}
.ins-val{font:700 11px var(--font);fill:var(--ink)}
.ins-paid{fill:#0d9488}
.ins-owed{fill:var(--brand)}
.ins-bar rect{transition:filter .12s}
.ins-bar:hover rect:not([fill=transparent]),.ins-bar:focus-visible rect:not([fill=transparent]){filter:brightness(1.12)}
.ins-click{cursor:pointer;outline:none}
.ins-bar.now .ins-paid,.ins-bar.now .ins-owed{stroke:var(--ink);stroke-width:1.5}
.ins-legend{display:flex;gap:1rem;flex-wrap:wrap;margin-top:.55rem;font-size:.76rem;color:var(--muted)}
.ins-key{display:inline-flex;align-items:center;gap:.4rem}
.ins-key i{width:11px;height:11px;border-radius:3px;display:inline-block}
.ins-empty{padding:2rem 1rem;text-align:center;color:var(--muted);font-size:.85rem}
.ins-tip{position:absolute;display:none;z-index:5;pointer-events:none;background:var(--bush-900);color:#fff;
  font-size:.76rem;line-height:1.45;padding:.45rem .65rem;border-radius:8px;box-shadow:var(--shadow-md);
  max-width:230px;white-space:normal}
.ins-tip b{font-weight:700}
.ins-warnt{color:#ffd9a8;font-weight:700}
.ins-hrow{display:grid;grid-template-columns:minmax(80px,38%) 1fr auto;align-items:center;gap:.6rem;
  padding:.28rem 0;cursor:default}
.ins-hl{font-size:.8rem;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ins-ht{height:14px;border-radius:4px;background:var(--line-2);overflow:hidden}
.ins-hb{height:100%;background:#0d9488;border-radius:4px;transition:width .35s ease}
.ins-hb.seq{background:#0d9488}
.ins-hv{font:700 .8rem var(--font);font-variant-numeric:tabular-nums;min-width:3.2rem;text-align:right}
.ins-hrow:hover .ins-hb{filter:brightness(1.12)}
.ins-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.ins-spark{display:block;width:100%;height:30px;margin-top:.55rem}
.ins-area{fill:#0d9488;fill-opacity:.13}
.ins-line{stroke:#0d9488;stroke-width:2.5;stroke-linejoin:round;stroke-linecap:round}
.ins-pt{fill:var(--card);stroke:#0d9488;stroke-width:2.5;transition:r .12s}
.ins-dot:hover .ins-pt{r:6.5}
.ins-nowline{stroke:var(--ink);stroke-width:1;stroke-dasharray:3 4;opacity:.45}
/* Entrance: bars rise from the baseline, horizontal bars grow from the left. */
.ins-bar rect:not([fill=transparent]){transform-box:fill-box;transform-origin:50% 100%;
  animation:insRise .65s cubic-bezier(.2,.8,.2,1) both}
.ins-bar:nth-child(2n) rect{animation-delay:.05s}
.ins-bar:nth-child(3n) rect{animation-delay:.1s}
.ins-hb{transform-origin:0 50%;animation:insGrow .7s cubic-bezier(.2,.8,.2,1) both}
.ins-line{stroke-dasharray:1200;animation:insDraw 1.1s ease-out both}
.ins-area{animation:insFade .9s ease-out .2s both}
@keyframes insRise{from{transform:scaleY(0)}to{transform:scaleY(1)}}
@keyframes insGrow{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes insDraw{from{stroke-dashoffset:1200}to{stroke-dashoffset:0}}
@keyframes insFade{from{opacity:0}to{opacity:1}}
.ins-card{transition:box-shadow .15s,transform .15s}
.ins-card:hover{box-shadow:var(--shadow-md)}
@media (prefers-reduced-motion:reduce){
  .ins-hb,.ins-line,.ins-area,.ins-bar rect{animation:none!important;transition:none}
}
/* ---------- Drill-down pop-up: wide, readable, filterable ---------- */
.drill-bar{display:flex;align-items:center;gap:.8rem;flex-wrap:wrap;margin:0 0 .7rem}
.drill-chips{display:flex;gap:.45rem;flex-wrap:wrap}
.drill-chip{background:var(--brand-soft);color:var(--brand-dark);border:1px solid #f1d6a8;border-radius:999px;
  padding:.18rem .75rem;font-size:.8rem;font-weight:600}
.drill-chip b{font-weight:800;font-variant-numeric:tabular-nums}
.drill-filter{width:240px;max-width:100%;padding:.45rem .75rem;border:1px solid var(--line);border-radius:999px;
  font:inherit;font-size:.85rem;background:#fff}
.drill-filter:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 3px rgba(184,98,27,.15)}
.drill-table{max-height:min(68vh,820px);overflow:auto;border:1px solid var(--line);border-radius:var(--radius-sm)}
.drill-table table{margin:0;min-width:100%}
.drill-table th{position:sticky;top:0;z-index:1;background:#f4f6f3;white-space:nowrap}
.drill-table td{white-space:nowrap;padding:.6rem .9rem}
.drill-table th{padding:.6rem .9rem}
.drill-table td.drill-act,.drill-table th.drill-act{width:1%;text-align:right;padding-right:1rem}
.drill-table .btn.sm{padding:.3rem .9rem;font-weight:700}
.drill-table tbody tr:hover td{background:var(--brand-soft)}
.drill-pick{width:1%;padding-left:1rem!important;padding-right:.2rem!important}
.drill-pick input{width:17px;height:17px;cursor:pointer;accent-color:var(--green)}
.drill-bulk{display:flex;align-items:center;gap:.7rem;margin:0 0 .6rem;padding:.55rem .9rem;border-radius:var(--radius-sm);
  background:#e8f5ee;border:1px solid #bfe0cc;color:var(--green);font-size:.88rem;position:sticky;top:0;z-index:2}
.drill-table tfoot td{position:sticky;bottom:0;background:#f4f6f3;border-top:2px solid var(--line)}
.qa-flag{display:inline-block;margin-top:.25rem;padding:.1rem .55rem;border-radius:999px;
  background:#fff4e2;color:var(--amber);border:1px solid #f1d6a8;font-size:.7rem;font-weight:700}
.dd-btns{display:inline-flex;gap:.3rem;flex:none}
.dd-cancel{width:36px;height:36px;border-radius:9px;color:var(--red);border-color:#f3c6c1;background:#fff7f6}
.dd-btns .dd-edit:not(.dd-cancel){width:36px;height:36px;border-radius:9px}
.dd-cancel:hover{border-color:var(--red);color:#fff;background:var(--red)}
.dd-caret{display:inline-flex;color:var(--muted);transition:transform .16s}
.dd-caret.up{transform:rotate(180deg);color:var(--brand-dark)}
.dd-kids{display:none;background:#f8faf8;border-top:1px solid var(--line-2);
  padding:.5rem .9rem .6rem 5.4rem}
.dd-kids.open{display:block}
.dd-kids-head{padding:.15rem 0 .45rem;
  font-size:.72rem;font-weight:800;text-transform:uppercase;letter-spacing:.05em;
  color:var(--muted)}
.dd-party{display:flex;gap:.9rem;align-items:center;padding:.5rem .6rem;cursor:pointer;
  background:#fff;border:1px solid var(--line);border-radius:var(--radius-sm);
  margin-bottom:.35rem;transition:.12s}
.dd-party:last-child{margin-bottom:0}
.dd-party:hover{border-color:var(--brand);box-shadow:var(--shadow-sm);transform:translateX(2px)}
.dd-party .core{flex:1;min-width:0}
.dd-party .core b{display:block;font-size:.85rem;line-height:1.3}
.dd-party .core .l2{font-size:.74rem;color:var(--muted);margin-top:.1rem}
.dd-party .side{flex:none;text-align:right;display:flex;align-items:center;gap:.6rem}
.dd-party .side .amt{font-weight:800;font-variant-numeric:tabular-nums;font-size:.85rem}
/* The same bulk-select tick-box as .trip .pickcol — see the note over
   `picked` in dayBody() (calendar.js) for why Lodge grouping gets one too. */
.dd-party .pickcol{flex:none}
.dd-party.picked{background:var(--brand-soft);border-color:var(--brand)}
.dd-empty{font-size:.78rem;color:var(--muted);font-style:italic;padding:.2rem 0 .3rem}
/* Several trips under one lodge: each one gets its own heading, and the ones
   after the first need air above them or the day reads as a single list of
   names with a stray line in it. */
.dd-kids-head ~ .dd-kids-head{padding-top:.75rem}

/* ---- the same day, grouped by lodge (calendar.js) ----
   The trip grouping answers "what leaves today"; this one answers "who am I
   collecting at Buhala". Same rows, same drill-through, read from the other
   end — so it deliberately borrows the .dd blocks above rather than inventing
   a second look for the same thing. */
.day-group{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;margin:0 0 .9rem}
.day-group .lbl{font-size:.72rem;font-weight:800;text-transform:uppercase;
  letter-spacing:.05em;color:var(--muted)}
.lg-bar{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;padding:0 0 .5rem}
.lg-bar .lbl{font-size:.72rem;font-weight:800;text-transform:uppercase;
  letter-spacing:.05em;color:var(--muted)}
/* The lodge badge — one stable colour per lodge (lodgeColor() in
   calendar.js), a pill rather than plain text so ten lodges on one day are
   ten different colours to skim, not ten identical grey lines. Sits right
   under the trip name (.l2, tightened below) per the order asked for 7 Sep
   2026. */
.lg-badge{display:inline-flex;align-items:center;gap:.3rem;padding:.15rem .55rem .15rem .4rem;
  border-radius:999px;color:#fff;font-size:.72rem;font-weight:700;line-height:1.5}
.lg-badge-ico{display:inline-flex;align-items:center;justify-content:center;opacity:.9}
.lg-badge-ico svg{display:block;width:11px;height:11px}
.lg-head .core .l2{margin-top:.2rem}
/* The guest name(s) — a highlighter-yellow tag, asked for 7 Sep 2026, so the
   one line on this row that says WHO is travelling is the thing the eye
   lands on, not just another line of dark-on-white text. inline-block
   overrides the plain .trip .core .l3 flex rule (fine here — this is always
   one string, namesLine, never several children needing to wrap). */
.lg-head .core .l3{display:inline-block;margin-top:.3rem;padding:.1rem .5rem;
  border-radius:5px;background:#ffd60a;color:#000;font-size:.82rem;font-weight:700}
.lg-head .core .l4{margin-top:.3rem}
.lg-addr{font-size:.72rem;color:var(--muted);margin-top:.15rem}
/* Why this party is filed under this lodge — a quiet line, but never absent:
   "booked by this lodge" does not mean anybody is standing at its gate. */
.lg-why{display:inline-block;margin-top:.2rem;padding:.05rem .35rem;border-radius:5px;
  background:var(--brand-soft);color:var(--brand-dark);
  font-size:.66rem;font-weight:700;text-transform:lowercase}

/* ---- the same day, as a page (calendar.js, Day mode) ----
   The panel above is a modal and gets the modal's card around it. Standing on
   its own it needs one of its own, or the day's trips float on the page
   background with nothing holding them together. */
.cal-dayview{background:var(--card,#fff);border:1px solid var(--line);
  border-radius:var(--radius);box-shadow:var(--shadow-sm);padding:1.1rem 1.2rem 0}
.cal-dayview .dd:last-child,
.cal-dayview .trip:last-child{border-bottom:none}
.cal-day-acts{display:flex;flex-wrap:wrap;gap:.6rem;justify-content:flex-end;
  padding:.9rem 0;margin-top:.4rem;border-top:1px solid var(--line-2)}
/* The relative day sits with the date rather than under it — "Thursday, 3
   September 2026 · tomorrow" is one line of chrome, not two. */
.cal-period-rel{font-size:.78rem;font-weight:700;color:var(--muted);margin-left:.5rem}
@media(max-width:720px){
  .cal-dayview{padding:.8rem .7rem 0;border-radius:var(--radius-sm)}
  .cal-day-acts{justify-content:stretch}
  .cal-day-acts .btn{flex:1}
}

/* ---- the shared trip row's allocate button (Views.tripRow) ----
   Sits under the amount on the right of every trip row, on the dashboard, the
   day sheet and the calendar's day panel. Quiet until hovered: it is on every
   row, and a row full of loud buttons stops being a list. */
.trip-acts{display:flex;align-items:center;justify-content:flex-end;gap:.35rem;margin-top:.4rem}
.trip-alloc,.trip-edit{display:grid;place-items:center;width:26px;height:26px;flex:none;padding:0;
  border:1px solid var(--line);border-radius:7px;background:#fff;color:var(--muted);
  cursor:pointer;transition:.12s}
.trip-alloc:hover,.trip-edit:hover{border-color:var(--brand);color:var(--brand-dark);background:var(--brand-soft)}
.trip-alloc:focus-visible,.trip-edit:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
/* On a row that has nothing allocated the button is the point of the row.
   ⚠ THE AMBER IS ON .trip-alloc ONLY, and deliberately not on .trip-edit
     beside it. Amber here means one thing — "this trip has no vehicle, press
     me" — and painting the pencil the same colour would turn a specific
     instruction into decoration on two buttons that do different jobs. */
.trip.unassigned .trip-alloc{border-color:var(--amber);color:var(--amber);background:#fff8e8}
.trip.unassigned .trip-alloc:hover{background:var(--amber);color:#fff}

/* A badge that is also a button. It has to keep reading as a badge — the row
   is scanned, not read — so the only affordances are a pointer, a hairline
   ring on hover and a focus outline for the keyboard. */
.badge.badge-btn{cursor:pointer;transition:.12s;border:1px solid transparent}
.badge.badge-btn:hover{filter:brightness(.94);border-color:currentColor;
  transform:translateY(-1px)}
.badge.badge-btn:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
/* The unallocated ones ask to be pressed a little louder than the settled ones. */
.badge.warn.badge-btn{box-shadow:0 0 0 1px rgba(194,118,10,.35)}
.badge.warn.badge-btn:hover{background:#fdefd0}

/* The day panel's warning line, with its action on the right. */
.note.warn .spacer{flex:1}
.note.warn .btn.sm{flex:none;align-self:center}
@media(max-width:640px){
  .note.warn{flex-wrap:wrap}
  .note.warn .btn.sm{width:100%;justify-content:center;margin-top:.5rem}
}
@media(max-width:640px){ .dd-kids{padding-left:1rem} }

/* ---- week ---- */
.wk-grid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:1px;
  background:var(--line);border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}
.wk-col{background:#fff;display:flex;flex-direction:column;min-height:220px}
.wk-col.today{box-shadow:inset 0 0 0 2px var(--brand)}
.wk-head{padding:.55rem .5rem;border-bottom:1px solid var(--line-2);background:#fafbf9;
  text-align:center;cursor:pointer;transition:.12s}
.wk-head:hover{background:var(--brand-soft)}
.wk-head b{display:block;font-size:.74rem;text-transform:uppercase;letter-spacing:.05em;
  color:var(--muted)}
.wk-head span{display:block;font-size:.84rem;font-weight:700;margin-top:.1rem}
.wk-head .cnt{display:inline-block;margin-top:.2rem;font-size:.64rem;font-weight:800;
  background:var(--brand);color:#fff;border-radius:999px;padding:.03rem .4rem}
.wk-body{padding:.4rem;display:flex;flex-direction:column;gap:3px;flex:1}
.wk-empty{text-align:center;color:#b6c2ba;font-size:.8rem;padding:.8rem 0}

/* ---- vehicle utilisation matrix ---- */
table.vh-grid{width:100%;border-collapse:collapse;font-size:.84rem;min-width:900px;background:#fff}
table.vh-grid th{text-align:left;padding:.55rem .7rem;border-bottom:1px solid var(--line);
  background:#fafbf9;vertical-align:top}
table.vh-grid thead th{text-align:center;font-size:.7rem;text-transform:uppercase;
  letter-spacing:.05em;color:var(--muted);font-weight:800}
table.vh-grid thead th b{display:block;color:var(--ink);font-size:.72rem}
table.vh-grid thead th span{display:block;font-weight:600;font-size:.7rem;margin-top:.1rem}
table.vh-grid thead th.today{background:var(--brand-soft);color:var(--brand-dark)}
table.vh-grid tbody th{width:170px;border-right:1px solid var(--line)}
table.vh-grid tbody th b{display:block;font-size:.87rem}
table.vh-grid tbody th span{display:block;font-size:.72rem;color:var(--muted);margin-top:.1rem}
table.vh-grid tbody th .util{display:inline-block;margin-top:.35rem;font-size:.66rem;font-weight:800;
  background:var(--line-2);color:var(--muted);border-radius:999px;padding:.1rem .45rem}
table.vh-grid tbody th .util.warm{background:#fff4e2;color:var(--amber)}
table.vh-grid tbody th .util.hot{background:#e6f5ec;color:var(--green)}
table.vh-grid td{border-bottom:1px solid var(--line-2);border-right:1px solid var(--line-2);
  padding:.35rem;vertical-align:top;cursor:pointer;transition:.1s;min-width:105px}
table.vh-grid td:hover{background:#f7faf7}
table.vh-grid td.today{background:#fffaf4}
table.vh-grid td.today:hover{background:#fff3e6}
.vh-free{color:#c4cfc7;font-size:.74rem;text-align:center;padding:.5rem 0;font-style:italic}
tr.vh-orphan th{background:#fff8e8}
tr.vh-orphan th .util.hot{background:#fdecea;color:var(--red)}

/* ---------- Seat gauge (departures.js) ---------- */
.seat-gauge{min-width:150px}
.seat-gauge .bar{position:relative;height:8px;background:var(--line-2);border-radius:99px;
  overflow:hidden}
.seat-gauge .fill{height:100%;border-radius:99px;transition:width .25s}
.seat-gauge .fill.low{background:linear-gradient(90deg,#d98b6a,#c2760a)}
.seat-gauge .fill.ok{background:linear-gradient(90deg,var(--gold),var(--brand))}
.seat-gauge .fill.full{background:linear-gradient(90deg,#2f9e5f,var(--green))}
.seat-gauge .fill.over{background:linear-gradient(90deg,#e06552,var(--red))}
.seat-gauge .notch{position:absolute;top:-2px;bottom:-2px;width:2px;background:var(--ink);
  opacity:.45;border-radius:2px}
.seat-gauge .lbl{font-size:.74rem;margin-top:.28rem;color:var(--muted);line-height:1.3}
.seat-gauge .lbl b{color:var(--ink);font-weight:800;font-variant-numeric:tabular-nums}
.seat-gauge.compact{min-width:120px}
.seat-gauge.compact .bar{height:6px}

/* ---------- Departure rows (departures.js board) ---------- */
.dep-row{border-bottom:1px solid var(--line-2)}
.dep-row:last-child{border-bottom:none}
.dep-main{display:flex;gap:.9rem;padding:.85rem 1.1rem;cursor:pointer;align-items:center;
  transition:.12s}
.dep-main:hover{background:#f7faf7}
.dep-time{font-weight:800;font-size:.9rem;width:3.4rem;flex:none;
  font-variant-numeric:tabular-nums;color:var(--brand-dark)}
.dep-core{flex:1;min-width:0}
.dep-core b{display:block;font-size:.92rem;line-height:1.3}
.dep-core .l2{font-size:.78rem;color:var(--muted);margin-top:.15rem}
.dep-seats{flex:none}
.dep-status{flex:none;width:6.5rem;text-align:right}
/* The status pill is a real <select> now — see the note in departures.js. Styled
   to keep the badge look it had while it was only a label, so a day of runs
   still reads as a board at a glance rather than as a row of form controls. */
.dep-status-sel{border-radius:999px;border:1px solid var(--line);font-weight:600;
  cursor:pointer;background:var(--card,#fff);width:100%}
.dep-status-sel.paid{border-color:#8fbf9f;color:#1f6b3f}
.dep-status-sel.confirmed{border-color:#8fa8d8;color:#28437f}
.dep-status-sel.completed{border-color:var(--line);color:var(--muted)}
.dep-status-sel.cancelled{border-color:#e0a9a1;color:#a33427}
.dep-status-sel:disabled{opacity:.55;cursor:progress}
.dep-parties{display:flex;flex-wrap:wrap;gap:.35rem;padding:0 1.1rem .8rem 5.4rem}
.dep-party{display:inline-flex;align-items:center;gap:.35rem;border:1px solid var(--line);
  background:#fff;border-radius:999px;padding:.22rem .6rem;font:inherit;font-size:.76rem;
  cursor:pointer;color:var(--ink);transition:.12s}
.dep-party:hover{border-color:var(--brand);color:var(--brand-dark)}
.dep-party .n{font-weight:800;color:var(--muted);font-variant-numeric:tabular-nums}

/* ---------- Wizard ---------- */
.wizard{display:grid;grid-template-columns:250px 1fr;gap:1.4rem;align-items:start}
.steps{position:sticky;top:82px}
.steps ol{list-style:none;margin:0;padding:0}
.steps li{display:flex;gap:.7rem;padding:.55rem .6rem;border-radius:10px;cursor:pointer;color:#4e6157;
  font-size:.86rem;font-weight:600;align-items:flex-start}
.steps li:hover{background:var(--line-2)}
.steps li.active{background:var(--brand-soft);color:var(--brand-dark)}
.steps li .n{width:26px;height:26px;border-radius:50%;border:2px solid #ccd8d0;display:grid;
  place-items:center;font-size:.78rem;flex:none;background:#fff;color:#6a7d73;transition:.15s}
.steps li.active .n{border-color:var(--brand);color:var(--brand)}
.steps li.done .n{background:var(--green);border-color:var(--green);color:#fff}
.steps li small{display:block;font-weight:500;color:#93a49a;font-size:.72rem}
.wizard-panel{min-width:0}
.step-head{margin-bottom:1.2rem}
.step-head h2{font-size:1.25rem}
.step-head p{color:var(--muted);margin:.3rem 0 0}
.wizard-foot{display:flex;flex-wrap:wrap;gap:.6rem;margin-top:1.5rem;padding-top:1.2rem;
  border-top:1px solid var(--line-2)}
.wizard-foot .spacer{flex:1}
.progressbar{height:6px;background:var(--line-2);border-radius:99px;overflow:hidden;margin-bottom:1.2rem}
.progressbar > span{display:block;height:100%;background:linear-gradient(90deg,var(--brand),var(--gold));transition:.3s}

/* selectable product tile inside the wizard / public page */
.pick{border:1px solid var(--line);border-radius:var(--radius-sm);padding:.8rem .95rem;cursor:pointer;
  background:#fff;transition:.12s;display:flex;gap:.8rem;align-items:flex-start;margin-bottom:.55rem}
.pick:hover{border-color:#c2cfc6;background:#fafbf9}
.pick.on{border-color:var(--brand);background:var(--brand-soft);box-shadow:0 0 0 1px var(--brand)}
.pick .tick{width:22px;height:22px;border:2px solid #c2cfc6;border-radius:50%;flex:none;
  display:grid;place-items:center;margin-top:1px;color:#fff;transition:.12s}
.pick.on .tick{background:var(--brand);border-color:var(--brand)}
.pick .tick svg{opacity:0}
.pick.on .tick svg{opacity:1}
.pick .txt{flex:1;min-width:0}
.pick .txt b{display:block;font-size:.9rem;line-height:1.3}
.pick .txt small{color:var(--muted);font-size:.78rem;display:block;margin-top:.15rem;line-height:1.4}
.pick .amt{flex:none;font-weight:800;color:var(--brand-dark);font-size:.9rem;white-space:nowrap;
  font-variant-numeric:tabular-nums;text-align:right}
.pick .amt small{display:block;font-weight:500;color:var(--muted);font-size:.7rem}

/* ---------- Guest extras: combined days, and the lunch option ----------
   guest-extras.js, shared by external_book.html and the guest portal.

   It sits INSIDE the "what would you like to book" step rather than getting a
   step of its own, because it is a refinement of a choice already made — a
   guest who does not want a second tour or a lunch should be able to scroll
   past it without deciding anything. Hence a quiet rule above it rather than a
   heading that demands attention. */
.gx{margin-top:1.4rem;padding-top:1.1rem;border-top:1px solid var(--line)}
.gx .section-title{margin-top:1rem}
.gx .section-title:first-child{margin-top:0}
.gx-hint{font-size:.82rem;color:var(--muted);line-height:1.5;
  margin:-.35rem 0 .7rem}

/* What the fare already covers. Green, because it is the answer to "what am I
   actually getting" and the guest is looking for reassurance, not a warning. */
.gx-inc{background:var(--brand-soft,#eef4ee);border:1px solid var(--line);
  border-radius:var(--radius-sm);padding:.7rem .9rem;margin:.6rem 0 0}
.gx-inc-h{display:flex;align-items:center;gap:.4rem;font-size:.78rem;
  font-weight:700;letter-spacing:.03em;text-transform:uppercase;
  color:var(--brand-dark)}
.gx-inc ul{margin:.45rem 0 0;padding-left:1.1rem}
.gx-inc li{font-size:.84rem;color:var(--ink-2,#4b463d);line-height:1.5}
.gx-inc li::marker{color:var(--brand)}

/* ---------- Type cards (UI.cardGroup) ----------
   The first question the wizard asks — tour, private transfer or shared
   shuttle — decides which product list, which price ladder and which set of
   fields the rest of the wizard shows. A row of pill chips gave that decision
   the same visual weight as "one way or return", and operators were choosing a
   tour by habit and then hunting for a route that was never going to be in the
   list. So it gets real estate: an icon, the name, what it means in one line,
   and how many products are behind it.

   Each card carries its own accent in --tc, set inline, so the tint, the ring
   and the tick are one set of rules rather than three. --tc-soft is the wash. */
.typecards{display:grid;grid-template-columns:repeat(auto-fit,minmax(158px,1fr));gap:.65rem}
.typecard{--tc:var(--brand);--tc-soft:var(--brand-soft);
  position:relative;border:1px solid var(--line);border-radius:var(--radius);background:#fff;
  padding:.95rem .9rem .85rem;cursor:pointer;transition:.15s;text-align:left;overflow:hidden;
  display:flex;flex-direction:column;gap:.55rem;user-select:none}
.typecard:hover{border-color:var(--tc);transform:translateY(-1px);box-shadow:var(--shadow-md)}
.typecard:focus-visible{outline:2px solid var(--tc);outline-offset:2px}
.typecard.on{border-color:var(--tc);background:var(--tc-soft);box-shadow:0 0 0 1px var(--tc)}
/* A soft corner glow so the chosen card reads as lit rather than merely tinted.
   Behind the content, and it cannot catch the click. */
.typecard::after{content:"";position:absolute;top:-38px;right:-38px;width:96px;height:96px;
  border-radius:50%;background:var(--tc);opacity:0;transition:.25s;pointer-events:none;filter:blur(18px)}
.typecard.on::after{opacity:.18}
.typecard .tc-ico{width:38px;height:38px;border-radius:11px;display:grid;place-items:center;flex:none;
  background:var(--tc-soft);color:var(--tc);transition:.15s;position:relative;z-index:1}
.typecard.on .tc-ico{background:var(--tc);color:#fff}
.typecard .tc-txt{position:relative;z-index:1}
.typecard .tc-txt b{display:block;font-size:.92rem;line-height:1.25}
.typecard .tc-txt small{display:block;color:var(--muted);font-size:.76rem;line-height:1.35;margin-top:.15rem}
.typecard.on .tc-txt small{color:#4e6157}
.typecard .tc-meta{margin-top:auto;font-size:.7rem;font-weight:700;letter-spacing:.03em;
  text-transform:uppercase;color:var(--muted);position:relative;z-index:1}
.typecard.on .tc-meta{color:var(--tc)}
/* The tick sits over the icon's top-right rather than the card's, so it never
   collides with a two-line title on a narrow phone. */
.typecard .tc-tick{position:absolute;top:.65rem;right:.65rem;width:20px;height:20px;border-radius:50%;
  background:var(--tc);color:#fff;display:grid;place-items:center;z-index:2;
  transform:scale(.5);opacity:0;transition:.18s cubic-bezier(.34,1.56,.64,1)}
.typecard.on .tc-tick{transform:scale(1);opacity:1}
@media (max-width:520px){
  .typecards{grid-template-columns:1fr}
  .typecard{flex-direction:row;align-items:center;gap:.75rem;padding:.8rem .85rem}
  .typecard .tc-txt{flex:1;min-width:0}
  .typecard .tc-meta{margin-top:.2rem}
  /* In the row layout the tick takes its place at the end of the line rather
     than floating over the corner, so it collapses to nothing when unselected
     instead of leaving a hole. */
  .typecard .tc-tick{position:static;flex:none;width:0;transform:scale(0)}
  .typecard.on .tc-tick{width:20px;transform:scale(1)}
}

/* ---------- Drillable (Reports) ----------
   Anything on the Reports screen that opens the bookings behind it: a bar in
   the revenue chart, a row in one of the bar tables. The tiles use
   .stat.clickable and the drill panel's own tables use table.data, both of
   which already carry a hover — this is for the hand-built charts, which
   otherwise look exactly as inert as they used to be.

   role="button" and tabindex are set in views.js rather than here, so the
   keyboard reaches every one of them and :focus-visible has something to
   land on. */
.drill{cursor:pointer;border-radius:9px;transition:.12s}
.drill:hover{background:#f4f7f3}
.drill:focus-visible{outline:2px solid var(--brand);outline-offset:1px;background:#f4f7f3}
/* The bar itself brightens rather than the column merely tinting — behind a
   solid bar the tint is most of what you cannot see. */
.drill .drill-bar{transition:.15s}
.drill:hover .drill-bar,.drill:focus-visible .drill-bar{filter:brightness(1.13)}

/* ---------- Key dials (Reports) ----------
   A whole CARD that is drillable, which the rules above were not written for:
   .drill forces a 9px radius and a flat tint, and on a card both read as a
   rendering fault rather than as a hover. So a dial keeps the card's own
   radius and lifts on hover exactly the way .stat.clickable does — the two
   sit in adjacent rows on the same screen and must feel like one control. */
.card.dial.drill{border-radius:var(--radius)}
.card.dial.drill:hover,.card.dial.drill:focus-visible{background:var(--card);
  border-color:var(--brand);box-shadow:0 0 0 2px rgba(184,98,27,.15);transform:translateY(-2px)}
/* The sweep grows from empty on first paint. It is the one piece of motion on
   the screen and it earns its place: it shows which way the arc is read. */
.dial-arc{transition:stroke-dashoffset .5s ease-out,stroke .2s}
@media (prefers-reduced-motion:reduce){ .dial-arc{transition:none} }

/* ---------- Modal / overlay ---------- */
.overlay{position:fixed;inset:0;background:rgba(16,35,26,.55);backdrop-filter:blur(2px);z-index:100;
  display:grid;place-items:center;padding:1.5rem;animation:fade .15s}
@keyframes fade{from{opacity:0}to{opacity:1}}
.modal{background:#fff;border-radius:var(--radius-lg);box-shadow:var(--shadow-lg);width:100%;
  max-width:580px;max-height:90vh;overflow:auto;animation:pop .18s}
@keyframes pop{from{transform:scale(.96);opacity:0}to{transform:scale(1);opacity:1}}
.modal.wide{max-width:920px}
.modal-head{display:flex;align-items:center;padding:1.1rem 1.4rem;border-bottom:1px solid var(--line-2)}
.modal-head h3{font-size:1.05rem}
.modal-head .x{margin-left:auto;background:none;border:none;cursor:pointer;font-size:1.4rem;
  color:var(--muted);line-height:1}
.modal-body{padding:1.4rem}
.modal-foot{padding:1rem 1.4rem;border-top:1px solid var(--line-2);display:flex;gap:.6rem;
  justify-content:flex-end;flex-wrap:wrap}
.scrim{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:110}

/* ---------- Toast ---------- */
#toasts{position:fixed;bottom:1.2rem;right:1.2rem;z-index:200;display:flex;flex-direction:column;gap:.6rem}
.toast{background:var(--bush-800);color:#fff;padding:.8rem 1.1rem;border-radius:10px;
  box-shadow:var(--shadow-lg);font-size:.88rem;display:flex;gap:.6rem;align-items:center;
  animation:slide .2s;max-width:340px}
@keyframes slide{from{transform:translateX(20px);opacity:0}to{transform:translateX(0);opacity:1}}
.toast.ok{background:#125c33}.toast.err{background:#8c281c}

/* ---------- Login ---------- */
.login-wrap{min-height:100vh;display:grid;place-items:center;padding:1.5rem;
  background:radial-gradient(1100px 620px at 15% -10%,#2c5c41 0,transparent 62%),
             radial-gradient(900px 520px at 100% 100%,#b8621b 0,transparent 58%),var(--bush-900)}
/* ⚠ WIDE AND SHORT, NOT NARROW AND TALL — changed 3 September 2026 on the
   client's report that this screen was "starting to look terrible".

   It was one 420px column carrying eight things stacked on top of each other:
   a 156px logo, the company name, the language picker, a heading, "Who are
   you?", four role buttons in a 2×2, the sign-in form, the demo note and a
   footer link. On a laptop that is a ribbon roughly 900px tall in a 1080px
   window — the sign-in button, the only thing anybody came here to press,
   sat below the fold on a 13" screen while two thirds of the width went
   unused.

   So it splits: identity down the left (logo, name, language), the actual
   business of signing in down the right, and the demo note across the
   bottom where it can use the full width instead of wrapping into a
   ten-line column. Height becomes the TALLER of the two columns rather than
   the sum of everything — about 420px instead of 900.

   ⚠ THE SPLIT IS A MIN-WIDTH MEDIA QUERY, so the phone keeps the single
     column it always had, in the order it always had it: brand, language,
     the form, then the note. The markup is stacked and the grid is applied
     on top, never the other way round — a layout that has to be undone for
     a phone is a layout that will one day be wrong on one. */
/* ⚠ TWO MAX-WIDTHS, AND THE NARROW ONE IS THE DEFAULT. Stacked, the card must
   stay the 420px column it always was — a single column stretched to 700px
   puts a 140px logo above full-width fields with a hand's width of nothing on
   either side of the text, which looks worse than the tall version it
   replaced. The card only earns its width once there are two columns to put
   in it, so the 880 lives in the media query with the grid. */
.login-card{background:#fff;border-radius:var(--radius-lg);box-shadow:var(--shadow-lg);
  width:100%;max-width:420px;padding:2rem 2.2rem;animation:pop .2s}
.login-grid{display:grid;gap:1.4rem}
/* .brand is the SIDEBAR block re-used on a WHITE card, so both its layout and
   its colours must be overridden here — b is white for the green sidebar. */
.login-card .brand{border:none;padding:0;margin:0 0 1rem;
  flex-direction:column;align-items:center;text-align:center;gap:.55rem}
.login-card .brand b{color:var(--ink);font-size:1.05rem}
.login-card .brand span{color:var(--muted);font-size:.79rem}
.login-card h2{font-size:1.3rem;margin-bottom:.25rem;text-align:center}
.login-card .lead{color:var(--muted);font-size:.9rem;margin-bottom:1rem;text-align:center}
/* ⚠ THIS BLOCK MUST STAY BELOW THE THREE RULES ABOVE IT. A media query adds no
     specificity, so `.login-card h2` in here and `.login-card h2` out there are
     an even match and the LAST one written wins. Moved up, the centring above
     would quietly beat the left-alignment in here and the two-column layout
     would render with a centred heading over a left-aligned form for no
     visible reason. */
@media(min-width:780px){
  .login-card{max-width:880px}
  .login-grid{grid-template-columns:250px minmax(0,1fr);column-gap:2.2rem}
  /* ⚠ NO align-items HERE, so both columns stretch to the taller one and the
     rail can centre its contents against the form. With `start` the logo sat
     at the top of 200px of empty white — the card looked wide and unfinished
     rather than wide and deliberate. */
  .login-brandcol{display:flex;flex-direction:column;justify-content:center}
  /* The hairline is the divider, not the gap — a rule between the two halves
     says "these are different jobs" where whitespace alone just reads as a
     wide card. */
  .login-main{border-left:1px solid var(--line);padding-left:2.2rem}
  /* Centred text under a centred logo is right; centred text at the top of a
     left-aligned form is not. */
  .login-card h2,.login-card .lead{text-align:left}
}
/* "Who are you?" on the login card. A 2×2 grid of big targets, because this is
   tapped on a phone in a car park as often as clicked at a desk — 44px minimum
   on a coarse pointer is the rule the rest of the app follows. Choosing Admin
   or Staff selects nothing: both show the same form, and the real role is read
   from the staff record after sign-in. */
.who-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:.5rem;margin-bottom:1.1rem}
.who{display:flex;align-items:center;gap:.5rem;padding:.7rem .75rem;min-height:44px;
  background:#fff;border:1px solid var(--line);border-radius:var(--radius-sm);
  cursor:pointer;transition:.15s;text-align:left;color:var(--ink);font:inherit;
  font-size:.86rem;font-weight:600}
.who:hover{border-color:var(--brand);background:var(--brand-soft)}
.who:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
.who .ico{display:flex;flex:none;opacity:.7}
.who .ico svg{width:18px;height:18px}
.who .lbl{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.who.on{background:var(--brand);border-color:var(--brand);color:#fff}
.who.on .ico{opacity:1}
/* ⚠ min-width:0 ON THE BUTTON ITSELF, not only on the label inside it. A grid
   item's default min-width is `auto`, which is its MIN-CONTENT — and the label
   is white-space:nowrap, so "Staff member" was a floor the column could not go
   under. Two of those plus the card's padding came to more than a 390px phone
   has, and the card overflowed the screen: the right-hand column of buttons
   ran off the edge with no way to scroll to it. The ellipsis on .lbl was
   already there to handle exactly this and could never fire, because the
   button above it refused to shrink. */
.who{min-width:0}
/* One per row on a phone. 380px was too tight a threshold — at 390 (an iPhone
   in portrait, which is what these are actually tapped on) two columns still
   did not fit, and the pair either overflowed or ellipsised into "Staff
   memb…". Full-width targets are the better answer on a phone anyway. */
@media (max-width:440px){
  .who-grid{grid-template-columns:1fr}
  /* …EXCEPT the language pair, which stays two-up on a phone. "English" and
     "Afrikaans" are short enough to fit side by side at 390px with room to
     spare, and stacking them would add 50-odd pixels above the form to no
     purpose. The role buttons are the ones with a long label in them. */
  .who-grid.lang-grid{grid-template-columns:repeat(2,1fr)}
}

.demo-note{margin-top:1.2rem;font-size:.8rem;color:var(--muted);background:#fafbf9;padding:.85rem;
  border-radius:10px;border:1px solid var(--line);line-height:1.5}
.demo-note code{background:#eef2ef;padding:.05rem .3rem;border-radius:4px;font-size:.92em}

/* ---------- Empty / loading ---------- */
.empty{text-align:center;padding:3rem 1.5rem;color:var(--muted)}
.empty svg{width:46px;height:46px;opacity:.32;margin-bottom:.7rem}
.empty p{margin:0 0 1rem}
.spinner{width:34px;height:34px;border:3px solid var(--line);border-top-color:var(--brand);
  border-radius:50%;animation:spin .7s linear infinite;margin:2rem auto}
@keyframes spin{to{transform:rotate(360deg)}}

/* ===========================================================================
   PUBLIC BOOKING PAGE (external_book.html)
   A different animal from the staff app: one column, big targets, no sidebar,
   works on a 360px phone held one-handed in poor signal.
   =========================================================================== */
.pub{max-width:660px;margin:0 auto;padding:0 1rem 4rem}
.pub-hero{background:linear-gradient(150deg,var(--bush-900),var(--bush-700) 55%,var(--brand-dark));
  color:#fff;padding:2.2rem 1.2rem 2.6rem;text-align:center;margin-bottom:-1.6rem}
.pub-hero .logo-img{height:112px;width:112px;object-fit:contain;background:#fff;
  border-radius:18px;padding:6px;margin-bottom:.9rem;box-shadow:var(--shadow-md)}
.pub-hero h1{color:#fff;font-size:1.5rem;line-height:1.25}
.pub-hero p{color:#c3d6c9;margin:.45rem 0 0;font-size:.92rem}
/* The kiosk's own sync-status pill — see the note over renderSyncBadge() in
   checklist.html. Sits inside the dark hero, so it gets a light chip rather
   than the amber/red tones used against a white card elsewhere. */
.sync-badge{display:flex;align-items:center;justify-content:center;gap:.5rem;
  margin:.9rem auto 0;padding:.5rem .9rem;max-width:max-content;border-radius:999px;
  background:rgba(255,255,255,.14);color:#fff;font-size:.8rem}
.sync-badge svg{flex:none}
.sync-badge button{border:1px solid rgba(255,255,255,.5);background:transparent;color:#fff;
  border-radius:999px;padding:.25rem .7rem;font:inherit;font-weight:700;cursor:pointer}
.sync-badge button:hover{background:rgba(255,255,255,.14)}
.pub-card{background:#fff;border:1px solid var(--line);border-radius:var(--radius-lg);
  box-shadow:var(--shadow-md);padding:1.4rem;position:relative}
.pub-tabs{display:flex;gap:.4rem;background:#eef2ef;padding:.32rem;border-radius:999px;margin-bottom:1.4rem}
.pub-tabs button{flex:1;border:none;background:none;padding:.6rem .5rem;border-radius:999px;
  font:inherit;font-size:.87rem;font-weight:700;color:var(--muted);cursor:pointer;transition:.14s}
.pub-tabs button.on{background:#fff;color:var(--brand-dark);box-shadow:var(--shadow-sm)}
.pub-foot{text-align:center;padding:1.6rem 1rem;color:var(--muted);font-size:.82rem;line-height:1.7}
.pub-foot a{font-weight:600}
.pub-contact{display:flex;flex-wrap:wrap;gap:.5rem;justify-content:center;margin-top:.8rem}
.pub-contact a{display:inline-flex;align-items:center;gap:.4rem;border:1px solid var(--line);
  background:#fff;border-radius:999px;padding:.45rem .9rem;font-size:.83rem;font-weight:600;
  color:var(--ink)}
.pub-contact a:hover{border-color:var(--brand);color:var(--brand-dark)}
.pub-done{text-align:center;padding:1rem .5rem}
.pub-done .tick{width:64px;height:64px;border-radius:50%;background:#e6f5ec;color:var(--green);
  display:grid;place-items:center;margin:0 auto 1rem}
.pub-done h2{font-size:1.3rem;margin-bottom:.5rem}
.pub-done p{color:var(--muted);line-height:1.6;margin:0 0 .8rem}
.pub-done .ref{display:inline-block;background:var(--bush-800);color:#fff;font-weight:800;
  letter-spacing:.06em;padding:.5rem 1.1rem;border-radius:10px;font-size:1.05rem;margin:.4rem 0 1rem}

/* ===========================================================================
   THE GUEST PORTAL (portal.html)
   Built on the same .pub shell as the booking page, because it is the same
   animal: one column, big targets, a phone held one-handed in a lodge with
   poor signal. What is added here is the stuff a booking form never needs —
   a signed-in header, an itinerary that reads as a sequence of days, and a
   "what happens next" block large enough to read at 05:45 in the dark.
   =========================================================================== */
.portal-hero{padding-bottom:2.2rem}
.portal-hero h1{font-size:1.42rem}

/* ---------- the gate ---------- */
.portal-gate{text-align:center;padding:.6rem .2rem .4rem}
.portal-gate-mark{width:62px;height:62px;border-radius:50%;background:var(--brand-soft);
  color:var(--brand-dark);display:grid;place-items:center;margin:0 auto 1rem}
.portal-gate-mark.muted{background:var(--line-2);color:var(--muted)}
.portal-gate h2{font-size:1.32rem;margin-bottom:.55rem}
.portal-gate-sub{color:var(--muted);line-height:1.62;margin:0 auto;max-width:38ch;font-size:.92rem}
/* The form inside the gate is left-aligned even though the block around it is
   centred — a centred label above a full-width input reads as decoration and
   makes the field harder to scan down. */
.portal-gate .form-grid,.portal-gate .field{text-align:left}
.portal-gate-help{margin-top:1.6rem;padding-top:1.1rem;border-top:1px solid var(--line-2);
  color:var(--muted);font-size:.83rem;line-height:1.6}
.portal-err{color:var(--red);font-size:.85rem;font-weight:600;min-height:1.2em;
  margin-top:.5rem;text-align:left}

/* ---------- signed-in header ---------- */
.portal-bar{display:flex;align-items:center;gap:.8rem;padding:0 0 1rem;
  border-bottom:1px solid var(--line-2);margin-bottom:1.3rem}
.portal-bar-who{display:flex;align-items:center;gap:.7rem;flex:1;min-width:0}
.portal-bar-who .face{width:38px;height:38px;border-radius:50%;flex:none;display:grid;
  place-items:center;background:var(--bush-700);color:#fff;font-weight:800;font-size:.82rem}
.portal-bar-who b{display:block;font-size:.94rem;line-height:1.25;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.portal-bar-who small{display:block;color:var(--muted);font-size:.74rem}

/* ---------- next up ----------
   The one thing the guest most wants to know, in the largest type on the page.
   Dark, so it reads as a boarding pass rather than as another card. */
.portal-next{background:linear-gradient(145deg,var(--bush-900),var(--bush-700));color:#fff;
  border-radius:var(--radius);padding:1.3rem 1.3rem 1.15rem;margin-bottom:1.3rem;
  box-shadow:var(--shadow-md);-webkit-print-color-adjust:exact;print-color-adjust:exact}
.portal-next-eyebrow{display:flex;align-items:center;gap:.4rem;color:var(--gold);
  font-size:.7rem;font-weight:800;letter-spacing:.09em;text-transform:uppercase;margin-bottom:.5rem}
.portal-next h2{color:#fff;font-size:1.28rem;line-height:1.25;margin:0}
.portal-next-when{color:#c3d6c9;font-size:.9rem;margin-top:.25rem}
.portal-next-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:.9rem;
  margin-top:1.1rem;padding-top:1rem;border-top:1px solid rgba(255,255,255,.15)}
.portal-next-grid .l{display:block;color:#a9c2b3;font-size:.62rem;letter-spacing:.08em;
  text-transform:uppercase;font-weight:800;margin-bottom:.2rem}
.portal-next-grid b{color:#fff;font-size:.92rem;line-height:1.3;display:block}
.portal-next-tip{display:flex;gap:.5rem;align-items:flex-start;margin-top:1rem;
  background:rgba(212,160,60,.16);border:1px solid rgba(212,160,60,.35);border-radius:var(--radius-sm);
  padding:.6rem .75rem;font-size:.82rem;line-height:1.5;color:#f2e4c8}
.portal-next-tip svg{flex:none;margin-top:.12rem}

/* ---------- driver ---------- */
.portal-driver{display:flex;align-items:center;gap:.8rem;background:#f4f7f4;
  border:1px solid var(--line);border-radius:var(--radius-sm);padding:.8rem .9rem;margin-bottom:1.1rem}
.portal-driver-face{width:42px;height:42px;border-radius:50%;flex:none;display:grid;
  place-items:center;background:var(--brand);color:#fff;font-weight:800;font-size:.86rem}
.portal-driver .l{font-size:.64rem;letter-spacing:.08em;text-transform:uppercase;
  font-weight:800;color:var(--muted)}
.portal-driver b{display:block;font-size:.95rem}
.portal-driver .v{font-size:.78rem;color:var(--muted)}
.portal-driver .btn-row{flex:none;gap:.4rem}
.portal-driver .btn.sm{padding:.45rem .6rem}

/* ---------- the itinerary ---------- */
.portal-trips{display:flex;flex-direction:column;gap:.7rem}
.portal-trip{border:1px solid var(--line);border-radius:var(--radius-sm);background:#fff;
  overflow:hidden;transition:box-shadow .15s,border-color .15s}
.portal-trip:hover{box-shadow:var(--shadow-sm)}
/* An amber spine on anything still owing — the same visual grammar the day
   sheet uses for an unallocated trip, so the two screens teach each other. */
.portal-trip.owing{border-left:3px solid var(--amber)}
.portal-trip-head{display:flex;align-items:center;gap:.8rem;width:100%;text-align:left;
  border:none;background:none;font:inherit;padding:.85rem .95rem;cursor:pointer;color:inherit}
.portal-trip-head:hover{background:#fafbfa}
.portal-trip-head:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}
.portal-trip-date{flex:none;width:62px;text-align:center;padding-right:.7rem;
  border-right:1px solid var(--line-2)}
.portal-trip-date b{display:block;font-size:.78rem;line-height:1.3}
.portal-trip-date small{display:block;color:var(--muted);font-size:.72rem;
  font-variant-numeric:tabular-nums}
.portal-trip-main{flex:1;min-width:0}
.portal-trip-name{font-weight:700;font-size:.93rem;line-height:1.3}
.portal-trip-sub{color:var(--muted);font-size:.78rem;margin-top:.12rem}
.portal-trip-right{display:flex;align-items:center;gap:.4rem;flex:none;flex-wrap:wrap;
  justify-content:flex-end}
.portal-trip-chev{flex:none;color:var(--muted);display:grid;place-items:center;transition:transform .18s}
.portal-trip-head.open .portal-trip-chev{transform:rotate(180deg)}
.portal-trip-body{padding:.2rem 1rem 1.1rem;border-top:1px solid var(--line-2)}
.portal-trip-body .section-title:first-child{margin-top:1rem}
.portal-flag{display:inline-flex;align-items:center;gap:.25rem;background:#fdf3e3;
  border:1px solid #f0d9b0;color:var(--amber);border-radius:999px;padding:.15rem .5rem;
  font-size:.68rem;font-weight:800}
.portal-done-chip{display:inline-flex;align-items:center;gap:.35rem;color:var(--green);
  font-size:.8rem;font-weight:700}
.portal-kv{margin-top:1rem}

/* ---------- cards, money, lists ---------- */
.portal-card{margin-top:1.3rem}
.portal-card .card-head h3{display:flex;align-items:center;gap:.45rem}
.portal-money{display:grid;grid-template-columns:repeat(3,1fr);gap:.8rem;text-align:center}
.portal-money>div{background:#f4f7f4;border:1px solid var(--line);border-radius:var(--radius-sm);
  padding:.75rem .5rem}
.portal-money .l{display:block;font-size:.64rem;letter-spacing:.07em;text-transform:uppercase;
  font-weight:800;color:var(--muted);margin-bottom:.25rem}
.portal-money b{font-size:1.02rem;font-variant-numeric:tabular-nums}
.portal-money .owing{background:#fdf3e3;border-color:#f0d9b0}
.portal-money .owing b{color:var(--amber)}
.portal-money-note{color:var(--muted);font-size:.85rem;line-height:1.6;margin:.9rem 0 0}
.portal-bank{margin-top:1rem;border-top:1px solid var(--line-2);padding-top:.9rem}
.portal-bank .l{font-size:.64rem;letter-spacing:.07em;text-transform:uppercase;font-weight:800;
  color:var(--muted);margin-bottom:.5rem}
.portal-bank p{font-size:.8rem;color:var(--muted);margin:.7rem 0 0}
.portal-note{align-items:flex-start;margin-bottom:1.2rem}
.portal-list{margin:0;padding-left:1.15rem;color:#33473d;font-size:.88rem;line-height:1.65}
.portal-list li{margin-bottom:.4rem}
.portal-welcome{background:var(--brand-soft);border:1px solid #f0d9b0;border-radius:var(--radius-sm);
  padding:.9rem 1rem;font-size:.88rem;line-height:1.55;color:#6b3d10}
.portal-welcome b{display:block;margin-bottom:.2rem;font-size:.95rem}
.portal-hint{font-size:.83rem;color:var(--muted);margin:-.4rem 0 .8rem}

/* ===========================================================================
   THE PARTNER PORTAL (partner.html)
   Reuses the whole .pub / .portal shell — same one-column page, same gate, same
   signed-in header — because a lodge receptionist and a guest are both reading
   a phone. Only what a partner does and a guest never does is added here.
   =========================================================================== */

/* A count on a tab. The Tomorrow tab is the reason a lodge opens this page at
   all, and "is there anything for me?" has to be answerable without tapping. */
.pub-tabs button .pill{display:inline-block;margin-left:.35rem;background:var(--red);color:#fff;
  border-radius:999px;font-size:.68rem;font-weight:800;padding:.05rem .38rem;line-height:1.5;
  vertical-align:.05em}
.pub-tabs button.on .pill{background:var(--brand)}

/* ---------- tomorrow, grouped by what is happening ----------
   The visual shape of the WhatsApp message this replaces: a bold heading per
   leg or activity, and the parties under it. */
.day-group{margin-bottom:1.25rem}
.day-head{font-weight:800;font-size:.93rem;color:var(--brand-dark);
  background:var(--brand-soft);border-radius:var(--radius-sm);padding:.5rem .75rem;
  margin-bottom:.15rem}
.day-items{padding:0 .1rem}
.day-item{display:flex;align-items:flex-start;gap:.7rem;padding:.7rem .65rem;
  border-bottom:1px solid var(--line)}
.day-item:last-child{border-bottom:none}
.day-item .txt{flex:1;min-width:0}
.day-item .txt b{display:block;font-size:.92rem;line-height:1.35}
.day-item .txt small{display:block;color:var(--muted);font-size:.78rem;line-height:1.5;
  margin-top:.12rem}
/* An already-confirmed party stays on the list rather than disappearing — the
   lodge is checking a plan for the morning, and a plan with rows silently
   removed from it is not the same plan. Dimmed, not hidden. */
.day-item.done{opacity:.6}
.day-item .tick-done{width:22px;height:22px;flex:none;border-radius:50%;background:#e6f5ec;
  color:var(--green);display:grid;place-items:center;margin-top:.1rem}
.day-item .checkline{margin:0;flex:none;padding-top:.1rem}
.ref-sm{font-size:.7rem;color:var(--muted);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  flex:none;padding-top:.2rem}

/* ---------- a booking or a room, as the partner sees it ---------- */
.p-card{border:1px solid var(--line);border-radius:var(--radius-sm);padding:.9rem 1rem;
  margin-bottom:.8rem;background:#fff}
.p-card-top{display:flex;align-items:flex-start;gap:.7rem;margin-bottom:.6rem}
.p-card-top>div:first-child{flex:1;min-width:0}
.p-card-top b{display:block;font-size:.95rem;line-height:1.3}
.p-card-top small{display:block;color:var(--muted);font-size:.79rem;margin-top:.15rem}
.p-card-body{display:grid;gap:.3rem;font-size:.85rem;border-top:1px solid var(--line);
  padding-top:.6rem}
.p-card-body>div{display:flex;gap:.6rem;align-items:baseline}
.p-card-body .k{flex:none;width:5.6rem;color:var(--muted);font-size:.76rem;font-weight:700;
  text-transform:uppercase;letter-spacing:.04em}

/* A dead-end screen borrows .pub-done's circle but must not read as success. */
.pub-done .tick.muted{background:var(--line-2);color:var(--muted)}

/* An inline spinner for a button that is mid-send. */
.spinner.sm{width:15px;height:15px;border-width:2px;margin:0;display:inline-block;
  vertical-align:-2px}

@media(max-width:520px){
  .p-card-body>div{flex-direction:column;gap:.1rem}
  .p-card-body .k{width:auto}
  .day-item{padding:.7rem .2rem}
}

/* ===========================================================================
   PRINTED DOCUMENTS — quote, invoice, voucher (screen preview + print)
   =========================================================================== */
.doc-sheet{background:#fff;max-width:860px;margin:0 auto;border:1px solid var(--line);
  border-radius:12px;box-shadow:var(--shadow-md);overflow:hidden;font-size:13.5px;color:#1a2620}
.doc-band{background:linear-gradient(120deg,var(--bush-900),var(--bush-700));color:#fff;
  padding:1.5rem 2rem;display:flex;justify-content:space-between;align-items:flex-start;gap:1.4rem;
  -webkit-print-color-adjust:exact;print-color-adjust:exact}
.doc-band .brandline{font-size:.68rem;letter-spacing:.15em;text-transform:uppercase;
  color:#a9c2b3;font-weight:800}
.doc-band h1{font-size:1.6rem;margin:.2rem 0 .5rem;color:#fff}
.doc-band .meta{display:flex;gap:1.5rem;flex-wrap:wrap;font-size:.82rem}
.doc-band .meta .l{color:#a9c2b3;text-transform:uppercase;font-size:.6rem;letter-spacing:.08em;display:block}
.doc-band .meta b{color:#fff;font-variant-numeric:tabular-nums}
.doc-band .logo-img{height:78px;width:78px;object-fit:contain;background:#fff;
  border-radius:10px;padding:4px;flex:none}
.doc-body{padding:1.5rem 2rem 2rem}
.doc-grid{display:grid;grid-template-columns:1fr 1fr;gap:1.2rem;margin-bottom:1.3rem}
.dbox{border:1px solid var(--line);border-radius:10px;overflow:hidden;background:#fff}
.dbox-h{background:#f4f7f4;border-bottom:1px solid var(--line);padding:.5rem .85rem;
  font-size:.66rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--bush-700)}
.dbox-b{padding:.75rem .85rem}
.dbox-b p{margin:.2rem 0}
table.doc-items{width:100%;border-collapse:collapse;margin:.2rem 0 1rem;font-size:.86rem}
table.doc-items th{background:var(--bush-800);color:#fff;text-align:left;padding:.55rem .7rem;
  font-size:.66rem;text-transform:uppercase;letter-spacing:.05em;font-weight:800;
  -webkit-print-color-adjust:exact;print-color-adjust:exact}
/* ⚠ DELIBERATELY STILL RIGHT-ALIGNED, while every table on SCREEN was centred
   on 1 Sep 2026. This is not an oversight and it is not inconsistency.

   table.doc-items is the quotation, the tax invoice and the guest voucher —
   documents that leave this business and are read by a lodge's bookkeeper, an
   accountant, sometimes SARS. A column of money is right-aligned on every
   invoice any of them will ever see, because that is what lets the units line
   up and a column be added down the page. Centring an Amount column on a tax
   invoice does not read as smart, it reads as software that has not issued an
   invoice before — the opposite of the intent behind the change.

   The screens are the office's own and centred figures suit them. This one is
   the company's paperwork. If you want it centred too, this is the line. */
table.doc-items th.r,table.doc-items td.r{text-align:right;
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum"}
table.doc-items td{padding:.55rem .7rem;border-bottom:1px solid var(--line-2);vertical-align:top;
  font-variant-numeric:tabular-nums}
table.doc-items tbody tr:last-child td{border-bottom:1px solid var(--line)}
table.doc-items td small{color:var(--muted);display:block;font-size:.76em}
.doc-totals{width:330px;max-width:100%;margin-left:auto;font-size:.9rem}
.doc-totals .row{display:flex;justify-content:space-between;gap:1.5rem;padding:.3rem 0}
.doc-totals .row span{color:var(--muted)}
.doc-totals .row b{font-variant-numeric:tabular-nums}
.doc-totals .row.grand{border-top:2px solid var(--bush-800);margin-top:.4rem;padding-top:.55rem;font-size:1.15rem}
.doc-totals .row.grand span{color:var(--ink);font-weight:800}
.doc-terms{margin-top:1.6rem;padding-top:1rem;border-top:1px solid var(--line);
  font-size:.78rem;color:var(--muted);line-height:1.6}
.doc-terms h4{font-size:.68rem;text-transform:uppercase;letter-spacing:.06em;color:var(--ink);margin-bottom:.4rem}
.doc-terms ol{margin:.2rem 0;padding-left:1.1rem}
.doc-terms li{margin-bottom:.25rem}

/* The printed/PDF copy of a signed disclaimer — plain wrapped text, not a
   list, because it is a verbatim snapshot of what the guest was shown. */
.waiver-text{white-space:pre-wrap;font:inherit;font-size:.86rem;line-height:1.6;
  background:#fbfbf9;border:1px solid var(--line);border-radius:var(--radius-sm);
  padding:1rem 1.1rem;margin:0}

/* ============================= Sign on glass ============================= */
/* touch-action:none stops the page scrolling under a finger mid-stroke — the
   single most important line here on an actual tablet. */
/* A PROPER BOX, WITH "SIGN HERE" IN THE MIDDLE OF IT (18 Sep 2026: "the
   signature block must have a block border and the words inside — Sign Here —
   centred in the box"). It was a light dashed outline on an off-white panel
   with a small grey hint: on a tablet in daylight the edge all but vanished,
   and drivers were not sure where the signature went. Now a solid border in
   the ink colour, white inside, and the words large and centred. They vanish
   the moment the first stroke lands (has-ink), so they are never signed over.
   Shared by the checklist and the guest consent form — both benefit. */
.sigpad{position:relative;border:2px solid var(--ink);border-radius:var(--radius);
  background:#fff;overflow:hidden}
.sigpad-canvas{display:block;width:100%;touch-action:none;cursor:crosshair}
.sigpad-hint{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  color:var(--muted);font-size:1.5rem;font-weight:700;letter-spacing:.02em;
  pointer-events:none;text-align:center;padding:0 1rem;opacity:.55}
.sigpad.has-ink .sigpad-hint{display:none}

/* ==================== Vehicle checklist (checklist.html) ====================
   Wider than the other public pages (external_book.html / waiver.html) — this form is
   genuinely data-dense (4 tyres × 2 checks, 6 yes/no questions), and the
   shared .pub 660px cap left ~90px of usable width per tyre card once card
   padding was subtracted. .wide opts into a roomier column without touching
   the narrower pages. */
.pub.wide{max-width:900px}

/* A pair of tyre cards per row — this also happens to read as the vehicle's
   own wheel layout (front pair / rear pair) without asserting a left/right
   mapping the paper form never defined. */
.tyre-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:.8rem;margin-top:.6rem}
/* The four per-tyre cards are hidden until "All four tyres OK?" is answered
   No (18 Sep 2026). An author display:grid outranks [hidden] — same trap as
   .slider-body and .cal-legend — so say it here. */
.tyre-grid[hidden]{display:none}
/* The day's kilometres on the evening checklist (checklist.js, 18 Sep 2026):
   morning reading, then km travelled once the closing reading is typed. */
.odo-day{margin-top:.7rem;border:1px solid var(--line);border-radius:var(--radius-sm);background:#fbfbf9;overflow:hidden}
.odo-day[hidden]{display:none}
.odo-day-row{display:flex;align-items:baseline;gap:.6rem;flex-wrap:wrap;padding:.65rem .9rem;font-size:.9rem}
.odo-day-row + .odo-day-row{border-top:1px solid var(--line-2)}
.odo-day-row > span{flex:1;color:var(--muted)}
.odo-day-row b{font-variant-numeric:tabular-nums}
.odo-day-row small{width:100%;color:var(--muted);font-size:.76rem}
.odo-day-row.total{background:var(--brand-soft)}
.odo-day-row.total b{font-size:1.15rem;color:var(--brand-dark)}
.odo-day-row.bad b{color:var(--red)}
.odo-day-row.warn{color:var(--red);font-size:.8rem}
.odo-day-row.muted{color:var(--muted);font-size:.84rem}
/* The searchable vehicle picker on the driver's checklist (checklist.js
   vehiclePicker, 18 Sep 2026). A text box with a list that narrows as you
   type; big rows because it is tapped with a thumb in a dark vehicle. */
.spick{position:relative}
.spick-box{position:relative}
.spick-box input{padding-right:2.6rem}
.spick-clear{position:absolute;right:.45rem;top:50%;transform:translateY(-50%);width:32px;height:32px;
  border:0;border-radius:50%;background:var(--line-2);color:var(--muted);display:grid;place-items:center;cursor:pointer}
.spick-clear[hidden]{display:none}
.spick-list{position:absolute;left:0;right:0;z-index:30;margin-top:.3rem;max-height:46vh;overflow:auto;
  background:#fff;border:1px solid var(--line);border-radius:var(--radius-sm);box-shadow:0 10px 28px rgba(16,35,26,.16)}
.spick-list[hidden]{display:none}
.spick-item{display:block;width:100%;text-align:left;border:0;background:none;padding:.9rem 1rem;font:inherit;
  font-size:1.02rem;font-weight:600;color:var(--ink);border-bottom:1px solid var(--line-2);cursor:pointer}
.spick-item:last-child{border-bottom:0}
.spick-item:hover,.spick-item:focus{background:var(--brand-soft);outline:none}
.spick-other{color:var(--muted);font-weight:500}
.spick-none{padding:.8rem 1rem;color:var(--muted);font-size:.86rem}
.tyre-card{background:#fbfbf9;border:1px solid var(--line);border-radius:var(--radius-sm);padding:1rem}
.tyre-card-title{font-weight:800;font-size:.9rem;margin-bottom:.7rem}
.tyre-card .qcompact{margin-bottom:.55rem}
.tyre-card .qcompact:last-child{margin-bottom:0}

/* Simple yes/no questions two-up, so the page reads as a form instead of a
   long single-column list of dividers. */
.checklist-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:.7rem;margin-top:.5rem}

/* ---------------------------- Checklist photos ----------------------------
   ⚠ THESE RULES WERE MISSING ENTIRELY, which is why the photos filled the
     page. The markup (.photo-strip / .photo-thumb / .photo-slot) shipped with
     the photo feature but nothing ever styled it, so each <img> rendered at its
     natural size — and checklist.js shrinks a camera shot to 1440px on the long
     edge, so five of them made a column metres long on a tablet. A thumbnail
     is not a small photo, it is a photo with a box around it; without the box
     there is no thumbnail.

   FIXED SQUARES, not a percentage. The strip has to hold five of them plus the
   empty slots on a tablet held either way round, and a percentage width makes
   the thumbnails grow as the page does — the opposite of what a contact strip
   is for. 88px is about a thumbnail's worth: big enough to tell a scuffed
   bumper from a clean one, small enough that five plus the slots sit on one or
   two rows and the Submit button stays on screen.

   object-fit:cover, so a portrait shot from a phone and a landscape one from
   the tablet both fill the same square instead of one of them letterboxing into
   a grey slab. The full picture is always one tap away (preview()).
   ------------------------------------------------------------------------- */
.photo-pick{margin-top:.5rem}
.photo-strip{display:flex;flex-wrap:wrap;gap:.5rem;margin:.6rem 0 .5rem}

.photo-thumb,
.photo-slot{width:88px;height:88px;flex:none;border-radius:var(--radius-sm);position:relative}

.photo-thumb{overflow:hidden;border:1px solid var(--line);background:#fbfbf9}
.photo-thumb img{width:100%;height:100%;object-fit:cover;display:block;cursor:pointer}

/* The empty slots. Dashed and quiet — they are an invitation, not a warning,
   and photos are never required (see the note in checklist.js). */
.photo-slot{border:2px dashed var(--line-2);background:#fbfbf9;display:grid;place-items:center;
  color:var(--muted);cursor:pointer}
.photo-slot:hover{border-color:var(--brand);color:var(--brand)}

/* THE WALK-AROUND GRID (checklist.js photoPicker with opts.slots, 18 Sep
   2026) — six named slots, three across, each saying which side to shoot.
   Wider than the numbered strip's 88px squares because the label has to be
   readable on the tile itself; that is the whole point of the change. */
.photo-strip.is-slots{display:grid;grid-template-columns:repeat(3,1fr);gap:.6rem}
.photo-slot-cell{display:flex;flex-direction:column;gap:.3rem;min-width:0}
.photo-slot-cell .photo-thumb,
.photo-slot-cell .photo-slot{width:100%;height:auto;aspect-ratio:4/3}
.photo-slot.labelled{font:inherit;padding:.4rem;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:.35rem;text-align:center}
.photo-slot.labelled:disabled{opacity:.5;cursor:default}
.photo-slot-name{font-size:.82rem;font-weight:700;color:var(--ink);line-height:1.2}
.photo-slot-label{font-size:.76rem;font-weight:700;text-align:center;color:var(--muted)}
.photo-slot-label.done{color:var(--green)}

/* Remove. Deliberately a real 26px target rather than a tiny cross: this is
   pressed with a thumb, outside, and the cost of missing it is re-taking a
   photo of a vehicle that has since been driven away. */
.photo-x{position:absolute;top:3px;right:3px;width:26px;height:26px;border:0;
  border-radius:50%;background:rgba(16,35,26,.72);color:#fff;display:grid;place-items:center;
  cursor:pointer;padding:0;line-height:0}
.photo-x:hover{background:var(--red)}

/* The number, so "photo 3" in a conversation means the same thing to the driver
   and to whoever opens the checklist in the office. */
.photo-n{position:absolute;left:3px;bottom:3px;min-width:18px;height:18px;padding:0 5px;
  border-radius:9px;background:rgba(16,35,26,.72);color:#fff;font-size:.68rem;font-weight:700;
  display:grid;place-items:center;pointer-events:none}

.photo-status{font-size:.8rem;color:var(--muted);line-height:1.45}

/* A photo taken with no signal — queued on the device, not yet in the bucket.
   Dimmed rather than hidden: the driver already saw it and should still be
   able to review or remove it, just knows it hasn't gone anywhere yet. */
.photo-thumb.is-pending img{opacity:.55}
.photo-pending{position:absolute;top:3px;left:3px;width:22px;height:22px;border-radius:50%;
  background:var(--amber);color:#fff;display:grid;place-items:center;pointer-events:none}

/* THE OFFICE'S SIDE OF THE SAME PHOTOS (Checklist.viewScreen). Bigger than the
   tablet's thumbnails on purpose: the driver is confirming they took the right
   picture, the office is trying to see whether the mark on the bumper was
   already there. Inside the printed sheet, so a filed copy carries them. */
.photo-view{display:flex;flex-wrap:wrap;gap:.6rem;margin-top:.6rem}
.photo-view-item{position:relative;width:172px;border:1px solid var(--line);
  border-radius:var(--radius-sm);overflow:hidden;background:#fbfbf9;display:block;
  transition:.12s}
.photo-view-item:hover{border-color:var(--brand);box-shadow:var(--shadow-sm)}
.photo-view-item img{display:block;width:100%;height:124px;object-fit:cover}
@media print{
  .photo-view-item{width:150px;break-inside:avoid;box-shadow:none}
  .photo-view-item img{height:auto}
}

/* On a phone the strip would otherwise be one thumbnail and a lot of air. */
@media (max-width:420px){
  .photo-thumb,
  .photo-slot{width:72px;height:72px}
}

/* One question = one self-contained card: label left, a compact Yes/No
   segmented toggle right — colour-coded (green = Yes, red = No) so a driver
   scanning back over a long form sees at a glance what still needs fixing,
   not just that something was tapped. */
.qcard{border:1px solid var(--line);border-radius:var(--radius-sm);background:#fff;padding:.85rem 1rem}
.qhead{display:flex;align-items:center;justify-content:space-between;gap:.8rem;flex-wrap:wrap}
.qlabel{font-weight:600;font-size:.87rem;line-height:1.35;color:#2c3b34}
.qcard .qfollow{margin-top:.65rem}

/* The compact variant drops the card chrome — used inside a tyre card, which
   already provides the border. */
.qcompact .qhead{display:flex;align-items:center;justify-content:space-between;gap:.6rem}
.qcompact .qlabel{font-weight:600;font-size:.78rem;color:var(--muted)}

/* Segmented control, not a pair of loose pills: a sunken neutral track with
   the active segment raised and solid-filled — green for Yes, red for No —
   with white text/icon on both for maximum contrast against either fill. */
.yn{display:inline-flex;flex:none;align-items:center;gap:2px;background:var(--line-2);
  border:1px solid var(--line);border-radius:10px;padding:2px}
.yn-btn{display:inline-flex;align-items:center;gap:.32rem;border:none;background:transparent;
  border-radius:8px;padding:.42rem .85rem;font-size:.83rem;font-weight:700;
  font-family:inherit;color:var(--muted);cursor:pointer;transition:background .12s,color .12s,box-shadow .12s;
  white-space:nowrap}
.yn-btn svg{flex:none;opacity:.6;transition:opacity .12s}
.yn-btn:hover:not(.on){background:rgba(20,32,28,.06);color:#33473d}
.yn-btn.on{color:#fff;box-shadow:0 1px 2px rgba(16,35,26,.16)}
.yn-btn.on svg{opacity:1}
.yn-btn.yes.on{background:var(--green)}
.yn-btn.no.on{background:var(--red)}
.yn-btn:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.qcompact .yn-btn{padding:.3rem .6rem;font-size:.76rem;gap:.22rem}
.qcompact .yn-btn svg{width:11px;height:11px}

/* ---------- Vehicle log book ----------
   The printed log book runs far wider than an invoice does — a trip line
   carries a date, a driver, two odometer readings and a distance, and a fuel
   line nine columns — so it uses the same document table with tighter type,
   and SCROLLS inside its own box on a phone rather than pushing the page
   sideways. In print there is nothing to scroll, so the box gets out of the
   way and the type tightens again to hold A4. */
.log-table{overflow-x:auto;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain}
.log-table table.doc-items{font-size:.78rem;min-width:620px;margin-bottom:1.2rem}
.log-table table.doc-items th,.log-table table.doc-items td{padding:.4rem .5rem}

/* ============================= Responsive ============================= */

/* Tablets and below: the sidebar drawer is unconditional now (see above), so
   this media query is only the layout collapse — narrower grids, stacked
   headers — that still needs to kick in below the desk-width breakpoint. */
@media (max-width:1024px){
  .content{padding:1.15rem}
  .grid.cols-4{grid-template-columns:repeat(2,1fr)}
  .grid.cols-3{grid-template-columns:repeat(2,1fr)}
  .pipeline{grid-template-columns:repeat(2,1fr)}
  /* The month grid stays seven columns — a calendar that is not a week wide is
     not a calendar — but the cells get shorter and the chips lose their times. */
  .cal-cell{min-height:92px}
  .cal-chip .t{display:none}
  /* Week view stacks: seven readable rows beat seven unreadable columns. */
  .wk-grid{grid-template-columns:1fr}
  .wk-col{min-height:0}
  .wk-head{display:flex;align-items:center;gap:.5rem;text-align:left}
  .wk-head b,.wk-head span{display:inline}
  /* 16px avoids iOS zooming the page on focus */
  input,select,textarea{font-size:16px}
  .toolbar .search{flex:1 1 100%;min-width:0}
  .toolbar select,.toolbar input{max-width:none;flex:1 1 auto}
  /* wizard steps become a horizontal strip above the panel */
  .wizard{grid-template-columns:1fr}
  .steps{position:static}
  .steps ol{display:flex;overflow-x:auto;-webkit-overflow-scrolling:touch;gap:.4rem;padding-bottom:.5rem}
  .steps li{flex:none;flex-direction:column;align-items:center;text-align:center;min-width:84px}
  .steps li small{display:none}
}

/* Small tablets / large phones → documents become one readable column */
@media (max-width:768px){
  .doc-sheet{border-radius:10px}
  .doc-band{flex-direction:column;align-items:flex-start;padding:1.3rem 1.4rem;gap:1rem}
  .doc-band h1{font-size:1.4rem}
  .doc-body{padding:1.3rem 1.4rem 1.6rem}
  .doc-grid{grid-template-columns:1fr;gap:1rem}
  .modal-body{padding:1.1rem}
  .modal-head,.modal-foot{padding:1rem 1.1rem}
}

/* Phones → single column everywhere so nothing is cramped */
@media (max-width:600px){
  .form-grid,.form-grid.cols-3{grid-template-columns:1fr}
  .tyre-grid,.checklist-grid{grid-template-columns:1fr}
  .qhead{justify-content:space-between}
  .grid.cols-2,.grid.cols-3,.grid.cols-4{grid-template-columns:1fr}
  .pipeline{grid-template-columns:1fr}
  .pipe-col{min-height:0}
  .content{padding:.85rem}
  .topbar{padding:.7rem 1rem;gap:.6rem}
  .topbar h1{font-size:1.05rem}
  .topbar .sub{display:none}
  .topbar .btn.secondary{display:none}      /* "Find a screen" is pinned top of the menu on phones */
  /* The mode switch shrinks to its icon and label — the "I'm doing" caption is
     the first thing to go, and the whole control goes below 420px. There is no
     room to narrow a menu on a screen that only shows one thing at a time. */
  .mode-btn .mode-cap{display:none}
  .mode-btn{max-width:150px;padding:.45rem .55rem}
  .card.pad{padding:1.1rem}
  .card-head{padding:.9rem 1.1rem}
  .card-body{padding:1.1rem}
  .card-head h3{font-size:1.04rem}
  .grid{gap:1rem}
  .stat{padding:1rem}
  .stat .v{font-size:1.65rem}
  table.data{font-size:.82rem;min-width:520px}
  table.data th,table.data td{padding:.6rem .7rem}
  .tour-grid{grid-template-columns:1fr}
  /* Phone month grid: the day number and a count dot are all that fit. Tapping
     the day opens the detail panel, which is the real interaction on a phone. */
  .cal-cell{min-height:62px;padding:.25rem}
  .cal-chips{display:none}
  .cal-more{display:none}
  .cal-daynum{justify-content:center;gap:.25rem;font-size:.78rem}
  .cal-dow{padding:.35rem .2rem;font-size:.6rem}
  .cal-period{font-size:1rem;min-width:0}
  .cal-bar{gap:.5rem}
  .cal-totals{gap:.3rem .9rem;font-size:.78rem}
  .dep-main{flex-wrap:wrap;padding:.75rem .9rem;gap:.6rem}
  .dep-status{width:auto;text-align:left}
  .dep-seats{flex:1 1 100%}
  .dep-parties{padding-left:.9rem}
  .trip{padding:.8rem .9rem;gap:.7rem}
  .trip .time{width:3.1rem}
  .trip .time .time-date{font-size:.6rem}
  .trip .time .time-hm{font-size:.8rem}
  /* modals go near-fullscreen for easy thumb use */
  .overlay{padding:0;place-items:stretch}
  .modal,.modal.wide{max-width:none;max-height:100vh;height:100%;border-radius:0}
  .modal-foot{flex-direction:column-reverse;gap:.5rem}
  .modal-foot .btn{width:100%}
  .btn-row .btn{flex:1 1 auto}
  .doc-totals{width:100%}
  .login-card{padding:1.6rem}
  .pub-card{padding:1.1rem;border-radius:var(--radius)}
  .pub-hero{padding:1.7rem 1rem 2.4rem}
  .pub-hero h1{font-size:1.28rem}
  .kv{grid-template-columns:1fr;gap:.1rem .5rem}
  .kv dt{margin-top:.5rem}
}

/* Touch devices: bigger, thumb-friendly hit areas everywhere */
@media (pointer:coarse){
  .btn{min-height:44px;padding:.72rem 1.15rem}
  .btn.sm{min-height:38px;padding:.5rem .9rem}
  .btn.xs{min-height:34px;padding:.4rem .7rem}
  .btn.lg{min-height:52px}
  .nav a{padding:.8rem .85rem}
  .chip{padding:.55rem 1rem;font-size:.9rem}
  .yn-btn{padding:.68rem 1.15rem;font-size:.9rem}
  .qcompact .yn-btn{padding:.48rem .85rem;font-size:.82rem}
  .pick{padding:1rem 1.05rem}
  .pick .tick{width:26px;height:26px}
  input,select,textarea{padding:.72rem .8rem}
  .stepper-b{padding:0 1rem}
  .stepper-v{padding:.7rem .2rem}
  table.data tbody td{padding:.85rem .9rem}
  .modal-head .x{padding:.3rem .5rem;font-size:1.7rem}
  .pub-tabs button{padding:.75rem .5rem}
}

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

/* ============================= Print ============================= */
@media print{
  @page{size:A4;margin:14mm}
  body{background:#fff}
  .sidebar,.topbar,.wizard-foot,.no-print,#toasts,.steps,.toolbar,.pub-tabs{display:none !important}
  .app{display:block}
  .content{padding:0;max-width:none}
  *{-webkit-print-color-adjust:exact;print-color-adjust:exact}
  .doc-sheet{box-shadow:none;border:none;max-width:none;margin:0;border-radius:0}
  .doc-body{padding:0}
  .doc-band{border-radius:0}
  .card{box-shadow:none;break-inside:avoid}
  .day-block{break-inside:avoid}
  /* Nothing scrolls on paper: let the log book's tables use the full width of
     the sheet, and tighten them enough that a nine-column fuel line holds. */
  .log-table{overflow:visible}
  .log-table table.doc-items{font-size:.68rem;min-width:0}
  .log-table table.doc-items th,.log-table table.doc-items td{padding:.28rem .35rem}

  /* ---- the guest portal on paper ----
     A guest printing their itinerary — or saving it as a PDF to have offline in
     the bush, which is the more common case — wants every trip's detail, not a
     row of collapsed headings. The accordions carry an inline display:none, and
     !important in a stylesheet beats an inline declaration without it, so this
     opens all of them for the print without the page having to touch the DOM.
     The map iframes go: a tile that may not have loaded prints as a grey box,
     and the address beside it is what is actually useful on paper. */
  .portal-trip-body{display:block !important;border-top:1px solid var(--line-2)}
  .portal-trip-chev,.portal-bar,.map-frame,.map-actions{display:none !important}
  .portal-trip{break-inside:avoid;border:1px solid var(--line)}
  .portal-next{break-inside:avoid}
  .pub-card{box-shadow:none;border:none;padding:0}
  .pub-hero{margin-bottom:1rem;border-radius:0}

  a[href]::after{content:""}
}

/* ===========================================================================
   ASK WESAFARI  (#/ask — assistant.js + askdata.js)

   WRITTEN 23 AUGUST 2026, AND IT IS THE FIRST CSS THIS SCREEN HAS EVER HAD.
   Every .ask-* class in assistant.js was unstyled: the screen rendered as bare
   stacked divs, which is a large part of why the equivalent page on another
   Agile Automate project looked so much better beside it. The layout below is that page's
   shape — live figures and real questions in a rail, one conversation in the
   middle — in THIS app's colours and tokens, so it belongs to WeSafari rather
   than looking like a page borrowed from somewhere else.
   =========================================================================== */

/* ---------- the screen ---------- */
.ask-hero{display:flex;gap:.9rem;align-items:flex-start;
  background:linear-gradient(135deg,var(--brand),var(--brand-dark));
  color:#fff;border-radius:var(--radius);padding:1.1rem 1.3rem;margin-bottom:1.1rem;
  box-shadow:var(--shadow-md)}
.ask-hero h2{color:#fff;font-size:1.25rem}
.ask-hero p{margin:.35rem 0 0;font-size:.85rem;line-height:1.5;opacity:.93;max-width:76ch}
.ask-hero-ico{flex:none;display:grid;place-items:center;width:42px;height:42px;
  background:rgba(255,255,255,.16);border-radius:12px}

/* Three columns: the rail asks, the middle answers, the right explains. Below
   1200px the two side columns fall under the conversation rather than being
   squeezed — a 160px-wide list of questions is not a list of questions. */
.ask-body{display:grid;grid-template-columns:270px minmax(0,1fr) 300px;
  gap:1rem;align-items:start}
@media(max-width:1200px){
  .ask-body{grid-template-columns:minmax(0,1fr)}
  .ask-rail,.ask-side{display:grid;grid-template-columns:1fr 1fr;gap:1rem;align-content:start}
}
@media(max-width:700px){ .ask-rail,.ask-side{grid-template-columns:1fr} }
.ask-rail,.ask-side{display:flex;flex-direction:column;gap:1rem}

/* ---------- the live figures ---------- */
.ask-kpis{display:grid;grid-template-columns:1fr 1fr;gap:.5rem}
.ask-kpi{background:linear-gradient(135deg,var(--brand),var(--brand-dark));color:#fff;
  border-radius:12px;padding:.6rem .5rem;text-align:center;cursor:pointer;
  box-shadow:var(--shadow-sm);transition:transform .13s,box-shadow .13s}
.ask-kpi:hover{transform:translateY(-2px);box-shadow:var(--shadow-md)}
.ask-kpi:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.ask-kpi.loading{background:var(--line-2);color:var(--muted);cursor:default;box-shadow:none}
.ask-kpi.loading:hover{transform:none}
.ask-kpi-v{font-size:1.12rem;font-weight:800;line-height:1.15;
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum"}
.ask-kpi-k{font-size:.66rem;font-weight:700;text-transform:uppercase;
  letter-spacing:.06em;opacity:.9;margin-top:.2rem}
.ask-kpi-s{font-size:.63rem;opacity:.72;margin-top:.05rem}

/* ---------- the rail of real questions ---------- */
.ask-qgroup+.ask-qgroup{margin-top:.8rem}
.ask-qlbl{font-size:.65rem;font-weight:800;text-transform:uppercase;letter-spacing:.07em;
  color:var(--muted);margin-bottom:.35rem}
.ask-quick{display:block;width:100%;text-align:left;margin-bottom:.3rem;
  padding:.45rem .6rem;background:var(--bg);border:1px solid var(--line);
  border-radius:var(--radius-sm);font:inherit;font-size:.78rem;line-height:1.35;
  color:var(--ink);cursor:pointer;transition:.12s}
.ask-quick:hover{background:var(--brand);border-color:var(--brand);color:#fff}
.ask-quick:focus-visible{outline:2px solid var(--brand);outline-offset:1px}

/* ---------- the conversation ---------- */
.ask-panel{display:flex;flex-direction:column;background:var(--card);
  border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow-sm);
  overflow:hidden;min-height:min(70vh,620px)}
.ask-thread{flex:1;overflow-y:auto;padding:1.1rem;display:flex;flex-direction:column;gap:.9rem}

.ask-turn{display:flex;gap:.6rem;align-items:flex-start;max-width:100%}
.ask-turn.ask-q{flex-direction:row-reverse}
.ask-avatar{flex:none;display:grid;place-items:center;width:28px;height:28px;border-radius:50%;
  background:var(--line-2);color:var(--muted)}
.ask-avatar.ai{background:var(--brand-soft);color:var(--brand-dark)}
.ask-bubble{background:var(--bg);border:1px solid var(--line);border-radius:var(--radius);
  padding:.7rem .9rem;font-size:.87rem;line-height:1.55;min-width:0;max-width:100%}
.ask-turn.ask-q .ask-bubble{background:var(--brand-soft);border-color:#f0dcc6}
/* A numbers answer carries tables and a chart, so it gets the full width and
   drops the tinted panel — a table inside a chat bubble reads as a screenshot. */
.ask-bubble.ask-data{flex:1;background:transparent;border:none;padding:.1rem 0}

.ask-dots{display:flex;gap:4px;padding:.2rem 0}
.ask-dots i{width:6px;height:6px;border-radius:50%;background:var(--muted);opacity:.5;
  animation:askblink 1.2s infinite}
.ask-dots i:nth-child(2){animation-delay:.18s}
.ask-dots i:nth-child(3){animation-delay:.36s}
@keyframes askblink{0%,60%,100%{opacity:.25}30%{opacity:.9}}
@media(prefers-reduced-motion:reduce){ .ask-dots i{animation:none} }

.ask-md p{margin:0 0 .55rem}
.ask-md p:last-child{margin-bottom:0}
.ask-md ul{margin:.2rem 0 .55rem;padding-left:1.1rem}
.ask-md li{margin-bottom:.2rem}

/* ---------- the empty state ---------- */
.ask-empty{text-align:center;padding:1.6rem .8rem;color:var(--muted)}
.ask-empty-ico{display:inline-grid;place-items:center;width:52px;height:52px;
  border-radius:50%;background:var(--brand-soft);color:var(--brand-dark);margin-bottom:.6rem}
.ask-empty p{font-size:.87rem;line-height:1.55;max-width:52ch;margin:0 auto}
.ask-chips-title{font-size:.66rem;font-weight:800;text-transform:uppercase;
  letter-spacing:.07em;margin:1.1rem 0 .5rem}
.ask-chips{display:flex;flex-wrap:wrap;gap:.4rem;justify-content:center}
.ask-chip{padding:.4rem .75rem;background:var(--card);border:1px solid var(--line);
  border-radius:999px;font:inherit;font-size:.78rem;color:var(--ink);cursor:pointer;
  transition:.12s}
.ask-chip:hover{background:var(--brand);border-color:var(--brand);color:#fff}

/* ---------- the box you type in ---------- */
.ask-bar{border-top:1px solid var(--line);background:var(--bg);padding:.7rem .9rem;flex:none}
.ask-bar-in{display:flex;gap:.5rem;align-items:flex-end}
.ask-input{flex:1;min-height:42px;max-height:200px;resize:none;font:inherit;font-size:.88rem;
  padding:.6rem .8rem;border:1.5px solid var(--line);border-radius:var(--radius-sm);
  background:var(--field);color:var(--ink)}
.ask-input:focus{outline:none;border-color:var(--brand)}
.btn.ask-send{flex:none;align-self:stretch}
.ask-note{font-size:.7rem;color:var(--muted);margin-top:.45rem;text-align:center}

/* ---------- the receipts under an answer ---------- */
.ask-src,.ask-flag,.ask-err,.ask-recap{font-size:.76rem;color:var(--muted);
  margin-top:.5rem;line-height:1.5}
.ask-err{color:var(--red)}
.ask-actions{display:flex;flex-wrap:wrap;gap:.4rem;margin-top:.6rem}
.ask-quote{margin-top:.6rem;border:1px solid var(--line);border-radius:var(--radius-sm);
  overflow:hidden;background:var(--card)}
.ask-quote-head{background:var(--brand-soft);color:var(--brand-dark);padding:.45rem .7rem;
  font-size:.72rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em}
.ask-quote-t{display:flex;justify-content:space-between;gap:.8rem;padding:.3rem .7rem;
  font-size:.82rem}
.ask-quote-tot{display:flex;justify-content:space-between;gap:.8rem;padding:.45rem .7rem;
  border-top:1px solid var(--line);font-weight:800}
.ask-quote-dep,.ask-quote-warn{padding:.3rem .7rem .5rem;font-size:.76rem;color:var(--muted)}
.ask-quote-warn{color:var(--amber)}

/* ---------- the two cards on the right ---------- */
.ask-sources{margin:0;padding-left:1.1rem;font-size:.8rem;line-height:1.55;color:var(--muted)}
.ask-sources li{margin-bottom:.55rem}
.ask-sources b{color:var(--ink)}
.ask-limit{font-size:.79rem;line-height:1.5;color:var(--muted);margin:.2rem 0 0;
  padding-top:.6rem;border-top:1px dashed var(--line)}
.ask-limit b{color:var(--ink)}
.ask-misses{list-style:none;margin:0;padding:0;font-size:.8rem}
.ask-misses li{padding:.4rem 0;border-bottom:1px solid var(--line-2);
  display:flex;flex-wrap:wrap;gap:.35rem;align-items:baseline}
.ask-misses li:last-child{border-bottom:none}
.ask-misses .n{font-size:.68rem;font-weight:800;color:var(--amber)}
.ask-misses .when{font-size:.68rem;color:var(--muted);margin-left:auto}

/* ===========================================================================
   A NUMBERS ANSWER  (askdata.js — the .ad-* classes)
   =========================================================================== */
.ad-title{font-size:.95rem;font-weight:800;margin-bottom:.6rem}
.ad-stats{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:.8rem}
.ad-stat{flex:1 1 120px;background:var(--bg);border:1px solid var(--line);
  border-radius:var(--radius-sm);padding:.5rem .7rem;text-align:center}
.ad-stat-v{font-size:1.05rem;font-weight:800;line-height:1.2;color:var(--ink);
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum"}
.ad-stat-k{font-size:.64rem;font-weight:700;text-transform:uppercase;
  letter-spacing:.06em;color:var(--muted);margin-top:.15rem}
.ad-stat.green .ad-stat-v{color:var(--green)}
.ad-stat.red   .ad-stat-v{color:var(--red)}
.ad-stat.amber .ad-stat-v{color:var(--amber)}

.ad-note{font-size:.78rem;color:var(--muted);line-height:1.5;margin:0 0 .7rem}
.ad-empty{font-size:.84rem;color:var(--muted);background:var(--bg);
  border:1px dashed var(--line);border-radius:var(--radius-sm);padding:.7rem .9rem;margin:0}
.ad-more{font-size:.75rem;color:var(--muted);margin:.5rem 0 0}

.ad-tools{display:flex;flex-wrap:wrap;gap:.35rem;margin-top:.7rem}
.ad-links{display:flex;flex-wrap:wrap;gap:.35rem;margin-top:.6rem;
  padding-top:.6rem;border-top:1px dashed var(--line)}
/* Every answer ends with a way OUT of this screen and into the one that owns
   the subject — the same rule the knowledge answers follow. Ask never acts;
   these are how a person gets to where they can. */
.ad-link{padding:.35rem .7rem;background:var(--card);border:1px solid var(--line);
  border-radius:999px;font:inherit;font-size:.75rem;font-weight:600;
  color:var(--brand-dark);cursor:pointer;transition:.12s}
.ad-link:hover{background:var(--brand);border-color:var(--brand);color:#fff}

/* The chart, and the div-bar fallback for a browser that never got Chart.js. */
.ad-chart{margin:.2rem 0 .9rem;max-height:260px}
.ad-bars{display:flex;flex-direction:column;gap:.4rem}
.ad-bar-row{display:flex;align-items:center;gap:.6rem;font-size:.78rem}
.ad-bar-lbl{flex:0 0 34%;color:var(--muted);overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.ad-bar-track{flex:1;height:9px;background:var(--line-2);border-radius:5px;overflow:hidden}
.ad-bar-fill{height:100%;background:var(--brand);border-radius:5px}
.ad-bar-val{flex:none;font-weight:700;font-variant-numeric:tabular-nums;
  font-feature-settings:"tnum"}

@media print{
  /* Printing an answer should print the ANSWER — not the rail of questions
     that produced it, nor the box you would type the next one into. */
  .ask-hero,.ask-rail,.ask-side,.ask-bar,.ask-chips,.ask-actions{display:none !important}
  .ask-body{display:block}
  .ask-panel{border:none;box-shadow:none;min-height:0}
  .ask-thread{overflow:visible;padding:0}
  .ad-chart{max-height:none}
}

/* ===========================================================================
   ASK WESAFARI — POLISH PASS (10 Oct 2026: "improve the CSS ... look and feel
   like the main app and website").
   Sits AFTER the base Ask styles above and only overrides them, so the
   structure assistant.js builds is untouched. Same family as guide.html: forest
   hero over a safari photo, white cards, brand-orange accents. Inter stays (the
   app's own face). Written large -> small at the bottom.
   =========================================================================== */
.ask-screen{max-width:1680px;margin:0 auto}

/* ---------- hero ---------- */
.ask-hero{position:relative;overflow:hidden;align-items:center;gap:1rem;
  background:var(--bush-900) url(K5.jpg) center 30%/cover;
  border-radius:var(--radius-lg);padding:clamp(1.1rem,2.4vw,1.8rem) clamp(1.1rem,3vw,2.2rem);
  margin-bottom:1.25rem;box-shadow:var(--shadow-lg)}
.ask-hero::before{content:"";position:absolute;inset:0;
  background:linear-gradient(100deg,rgba(11,26,19,.94) 0%,rgba(11,26,19,.78) 55%,rgba(11,26,19,.45) 100%)}
.ask-hero>*{position:relative}
.ask-hero h2{font-size:clamp(1.35rem,2.6vw,1.9rem);letter-spacing:-.01em;line-height:1.15}
.ask-hero p{font-size:clamp(.84rem,1.1vw,.95rem);max-width:78ch;opacity:.9}
.ask-hero-ico{width:clamp(42px,4.4vw,56px);height:clamp(42px,4.4vw,56px);border-radius:16px;
  background:var(--brand);box-shadow:0 8px 20px rgba(0,0,0,.35)}

/* ---------- layout: rail | conversation | notes ---------- */
.ask-body{grid-template-columns:clamp(250px,19vw,320px) minmax(0,1fr) clamp(270px,21vw,350px);
  gap:clamp(.9rem,1.6vw,1.4rem)}
.ask-rail,.ask-side{gap:clamp(.9rem,1.4vw,1.2rem)}
.ask-rail .card,.ask-side .card{border-radius:var(--radius);box-shadow:var(--shadow-sm);
  transition:box-shadow .15s}
.ask-rail .card:hover,.ask-side .card:hover{box-shadow:var(--shadow-md)}

/* ---------- live figures ---------- */
.ask-kpi{background:linear-gradient(145deg,var(--bush-800),var(--bush-600));
  border-radius:var(--radius);padding:.8rem .55rem;position:relative;overflow:hidden}
.ask-kpi::after{content:"";position:absolute;left:0;right:0;bottom:0;height:3px;background:var(--brand)}
.ask-kpi-v{font-size:clamp(1.05rem,1.5vw,1.3rem)}
.ask-kpi.loading{background:var(--line-2)}
.ask-kpi.loading::after{display:none}

/* ---------- question list ---------- */
.ask-quick{background:var(--card);border-left:3px solid var(--brand-soft);
  padding:.55rem .7rem;font-size:.8rem;box-shadow:var(--shadow-sm)}
.ask-quick:hover{border-left-color:var(--brand-dark);transform:translateX(2px)}
.ask-qlbl{color:var(--brand-dark)}

/* ---------- the conversation ---------- */
.ask-panel{height:clamp(520px,calc(100vh - 230px),900px);min-height:0;
  border-radius:var(--radius-lg);box-shadow:var(--shadow-md);border-color:var(--line)}
.ask-thread{padding:clamp(1rem,2vw,1.6rem);gap:1.1rem;scroll-behavior:smooth;
  background:linear-gradient(180deg,#fbfcfa,var(--bg))}
.ask-avatar{width:32px;height:32px}
.ask-avatar.ai{background:linear-gradient(145deg,var(--brand),var(--brand-dark));color:#fff}
.ask-bubble{background:var(--card);border-radius:4px var(--radius) var(--radius) var(--radius);
  box-shadow:var(--shadow-sm);padding:.8rem 1rem;font-size:clamp(.87rem,1vw,.95rem);max-width:min(100%,78ch)}
.ask-turn.ask-q .ask-bubble{background:linear-gradient(145deg,var(--brand),var(--brand-dark));
  color:#fff;border-color:transparent;border-radius:var(--radius) 4px var(--radius) var(--radius)}
.ask-turn.ask-q .ask-avatar{background:var(--bush-800);color:#fff}
.ask-bubble.ask-data{background:transparent;box-shadow:none;max-width:100%}

/* ---------- empty state ---------- */
.ask-empty{padding:clamp(1.4rem,4vw,3rem) 1rem}
.ask-empty-ico{width:64px;height:64px;color:#fff;
  background:linear-gradient(145deg,var(--bush-700),var(--bush-900));box-shadow:var(--shadow-md)}
.ask-chip{padding:.5rem .95rem;font-size:.82rem;box-shadow:var(--shadow-sm)}
.ask-chip:hover{transform:translateY(-1px);box-shadow:var(--shadow-md)}

/* ---------- typing box ---------- */
.ask-bar{background:var(--card);padding:.8rem clamp(.8rem,2vw,1.2rem)}
.ask-input{border-radius:var(--radius);padding:.7rem 1rem;background:var(--bg)}
.ask-input:focus{background:var(--field);box-shadow:0 0 0 4px var(--brand-soft)}
.btn.ask-send{border-radius:var(--radius);padding-left:1.3rem;padding-right:1.3rem}

/* ---------- right-hand notes ---------- */
.ask-sources,.ask-limit,.ask-misses{overflow-wrap:anywhere}

/* ---------- wide monitors ---------- */
@media(min-width:1700px){
  .ask-screen{max-width:1900px}
  .ask-panel{height:clamp(640px,calc(100vh - 240px),1000px)}
  .ask-quick{font-size:.86rem}
  .ask-bubble{font-size:1rem}
}

/* ---------- laptops / tablets: conversation first, notes beneath ---------- */
@media(max-width:1200px){
  .ask-body{grid-template-columns:minmax(0,1fr)}
  .ask-mid{order:-1}
  .ask-panel{height:clamp(480px,72vh,760px)}
  .ask-rail,.ask-side{grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr))}
}

/* ---------- phones ---------- */
@media(max-width:700px){
  .ask-hero{align-items:flex-start;border-radius:var(--radius);margin-bottom:.9rem}
  .ask-hero::before{background:linear-gradient(180deg,rgba(11,26,19,.94),rgba(11,26,19,.78))}
  .ask-panel{height:clamp(440px,calc(100dvh - 210px),720px);border-radius:var(--radius)}
  .ask-thread{padding:.9rem}
  .ask-turn{gap:.45rem}
  .ask-avatar{width:28px;height:28px}
  .ask-bubble{padding:.65rem .8rem}
  /* 16px stops iPhones zooming the page the moment the box is tapped. */
  .ask-input{font-size:16px}
  .ask-bar{padding:.6rem .7rem calc(.6rem + env(safe-area-inset-bottom))}
  .btn.ask-send{padding-left:.9rem;padding-right:.9rem}
  .ask-chips{flex-direction:column;align-items:stretch}
  .ask-chip{text-align:left;border-radius:var(--radius-sm)}
  .ask-kpis{grid-template-columns:1fr 1fr}
}
@media(max-width:380px){
  .ask-hero-ico{display:none}
  .ask-kpi-v{font-size:1rem}
}
@media(prefers-reduced-motion:reduce){
  .ask-thread{scroll-behavior:auto}
  .ask-quick,.ask-chip,.ask-kpi{transition:none}
}
@media print{
  .ask-panel{height:auto}
  .ask-hero{background:none;color:#000;box-shadow:none}
  .ask-hero::before{display:none}
  .ask-hero h2,.ask-hero p{color:#000}
}

/* ===========================================================================
   DASHBOARD — WIDE SCREENS (10 Oct 2026: "page must be responsive to screen
   size - wider"). The page used to stop at 1600px and leave blank margins on a
   big monitor. It now runs to 2200px (see .content), and the charts add a third
   column once there is room for three, so eight charts sit as 3+3+2 rather than
   four tall rows of two. Phones and tablets are unchanged.
   =========================================================================== */
.topbar-guide{text-decoration:none}
@media(min-width:1500px){ .ins-grid{grid-template-columns:repeat(3,minmax(0,1fr))} }
@media(min-width:2000px){ .ins-grid{gap:1.3rem} }

/* ===========================================================================
   DRILL-DOWN POP-UP — SUMMARY, SCOPE BUTTONS, GROUPING (10 Oct 2026)
   Service-first columns, a per-service summary, "Already travelled" and
   "Travelled, still owing" buttons, group-by day/week with sub-totals, and R 0
   rows shown quietly (or hidden). Behaviour is in ui.js drillTable().
   =========================================================================== */
.drill-ctl{display:flex;flex-wrap:wrap;align-items:center;gap:.6rem 1rem;margin:0 0 .8rem}
.drill-seg{display:flex;flex-wrap:wrap;gap:.35rem}
.drill-grplbl,.drill-dim{display:inline-flex;align-items:center;gap:.4rem;font-size:.82rem;color:var(--muted);cursor:pointer}
.drill-sel{font:inherit;font-size:.82rem;padding:.3rem .5rem;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--field);color:var(--ink)}
.drill-sum{margin:0 0 .9rem;border:1px solid var(--line);border-radius:var(--radius);background:var(--card);overflow:hidden}
.drill-sum-head{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem .9rem;padding:.55rem .8rem;background:var(--brand-soft);color:var(--brand-dark)}
.drill-sum-head .btn{margin-left:auto}
.drill-sum .table-wrap{max-height:230px;overflow:auto}
.drill-sum-table{width:100%}
.drill-sum-table tfoot td{font-weight:700;background:var(--line-2);position:sticky;bottom:0}
.drill-sum-row{cursor:pointer}
.drill-sum-row:hover td{background:#fbf6ee}
.drill-sum-row.on td{background:var(--brand-soft);font-weight:700}
.drill-sum-row:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}
tr.drill-grp td{background:var(--bush-800,#16301f);color:#fff;font-weight:700;font-size:.82rem;padding:.4rem .6rem;
  position:sticky;top:0;z-index:1}
tr.drill-zero td{color:var(--muted);font-style:italic;background:repeating-linear-gradient(135deg,transparent 0 6px,rgba(0,0,0,.025) 6px 12px)}
@media(max-width:700px){
  .drill-ctl{gap:.5rem}
  .drill-sum .table-wrap{max-height:190px}
}
@media print{ .drill-ctl{display:none} }

/* Sortable pop-up headings (10 Oct 2026) */
th.drill-sortable{cursor:pointer;user-select:none;white-space:nowrap}
th.drill-sortable:hover{background:var(--brand-soft);color:var(--brand-dark)}
th.drill-sortable:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}
th.drill-sortable[aria-sort="ascending"],th.drill-sortable[aria-sort="descending"]{color:var(--brand-dark)}

/* From / To dates in the pop-up (10 Oct 2026) */
.drill-dates{display:flex;flex-wrap:wrap;align-items:center;gap:.4rem .6rem}
.drill-dlbl{display:inline-flex;align-items:center;gap:.35rem;font-size:.82rem;color:var(--muted)}
.drill-date{font:inherit;font-size:.82rem;padding:.28rem .45rem;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--field);color:var(--ink)}
.drill-date:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}

/* ===========================================================================
   PAGE BANNER (11 Oct 2026) — the main pages, left to right, under the top bar
   =========================================================================== */
.quicknav{position:sticky;top:60px;z-index:19;display:flex;align-items:stretch;gap:.25rem;
  background:var(--card);border-bottom:1px solid var(--line);box-shadow:0 3px 10px rgba(16,35,26,.05);
  padding:0 clamp(.6rem,1.8vw,2rem);overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
.quicknav::-webkit-scrollbar{display:none}
.qn-link{flex:0 0 auto;display:inline-flex;align-items:center;gap:.5rem;padding:.8rem .95rem;
  font-size:.9rem;font-weight:600;color:var(--muted);text-decoration:none;white-space:nowrap;
  border-bottom:3px solid transparent;margin-bottom:-1px;transition:color .12s,background .12s,border-color .12s}
.qn-link .qn-ico{display:inline-flex;color:var(--brand-dark);opacity:.85}
.qn-link:hover{color:var(--ink);background:var(--brand-soft)}
.qn-link:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}
.qn-link.active{color:var(--bush-800,#16301f);border-bottom-color:var(--brand);background:linear-gradient(180deg,transparent,var(--brand-soft))}
.qn-link.active .qn-ico{opacity:1}
@media(max-width:700px){
  .quicknav{padding:0 .4rem;-webkit-mask-image:linear-gradient(90deg,#000 92%,transparent);mask-image:linear-gradient(90deg,#000 92%,transparent)}
  .qn-link{padding:.7rem .75rem;font-size:.85rem}
}
@media print{ .quicknav{display:none !important} }
@media(max-width:1250px){ .qn-link{padding:.8rem .7rem;gap:.4rem} }

/* ===========================================================================
   BOOKINGS — LESS BUSY (11 Oct 2026: "this page is awesome but very busy")
   Nothing is removed; things are made smaller, quieter or shared a line.
   =========================================================================== */
/* The two filter questions sit side by side when there is room, one under the
   other when there is not. */
.filter-bar{display:flex;flex-wrap:wrap;gap:.2rem 2.2rem;align-items:flex-start}
.filter-bar>.fb-row{flex:1 1 560px;min-width:0;border-top:0;margin-top:0;padding-top:.3rem;flex-wrap:wrap}
.filter-bar>.fb-row+.fb-row{border-top:0;margin-top:0;padding-top:.3rem}
.filter-bar>.fb-foot{flex:1 1 100%;margin-top:.1rem}
.fb-lbl{min-width:4.6rem}
.fb-sum{font-size:.76rem}
/* Change and Delete share one line on a card: Change keeps its words, Delete is an icon. */
.pipe-act{flex-direction:row;align-items:center;gap:.3rem}
.pipe-act .btn{width:auto;flex:1 1 auto}
.pipe-act .btn.del-icon{flex:0 0 auto;width:2rem;padding-left:0;padding-right:0;color:var(--muted)}
.pipe-act .btn.del-icon:hover,.actcol .btn.del-icon:hover{color:var(--red);background:#fdecea}
.actcol .btn.del-icon{color:var(--muted)}
.pipe-act-group{margin-top:.45rem;padding-top:.4rem}
.pipe-confirm{margin-bottom:.25rem}
.filter-bar>.fb-row{flex:0 1 auto}
.filter-bar .fb-chips{flex:0 1 auto}
.filter-bar .fb-lbl{align-self:center}

/* Refresh button on Bookings (11 Oct 2026): the arrow turns while it works */
@keyframes bkspin{to{transform:rotate(360deg)}}
.btn.spinning .ico svg{animation:bkspin .8s linear infinite}
@media(prefers-reduced-motion:reduce){.btn.spinning .ico svg{animation:none}}

/* ===========================================================================
   BOOKINGS BOARD — DRAG, MOVE, DATE HEADINGS  (11 Oct 2026)
   =========================================================================== */
.pipe-card[draggable="true"]{cursor:grab}
.pipe-card.dragging{opacity:.35;cursor:grabbing;transform:rotate(1.2deg)}
.pipe-col{transition:background .12s,outline-color .12s}
.pipe-col.drop-over{outline:2px dashed var(--brand);outline-offset:-2px;background:var(--brand-soft)}
.pipe-act .btn.move-icon{flex:0 0 auto;width:2rem;padding-left:0;padding-right:0;color:var(--muted)}
.pipe-act .btn.move-icon:hover,.actcol .btn.move-icon:hover{color:var(--brand-dark);background:var(--brand-soft)}
.actcol .btn.move-icon{color:var(--muted)}
.board-hint{display:flex;align-items:center;gap:.5rem;font-size:.78rem;color:var(--muted);margin:-.2rem 0 .7rem}
.board-hint svg{flex:none;opacity:.7}
/* one heading per travel day inside Confirmed / Paid */
.day-sec{margin-bottom:.35rem}
.day-head{display:flex;align-items:baseline;flex-wrap:wrap;gap:.2rem .5rem;padding:.5rem .35rem;cursor:pointer;
  border-top:1px solid var(--line);font-size:.83rem;border-radius:var(--radius-sm)}
.day-head:hover{background:var(--line-2)}
.day-head:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}
.day-head .ch{display:inline-flex;align-self:center;color:var(--muted)}
.day-head b{font-size:.86rem}
.day-head .sub{font-size:.74rem;color:var(--muted)}
.day-head .cnt{margin-left:auto;font-size:.74rem;color:var(--muted);white-space:nowrap}
.day-sec.soon>.day-head{background:var(--brand-soft);border-top-color:#f0d9bd}
.day-sec.soon>.day-head b{color:var(--brand-dark)}
.day-sec.warn>.day-head{background:#fff6e6}
.day-sec.warn>.day-head b{color:#8a5a0a}
.day-body{padding:.35rem 0 .2rem}

/* ---------- TOP BAR + PAGE-LINK ROW ON PHONES AND TABLETS (11 Oct 2026: "menus need to be mobile and
   tablet friendly"). Found by rendering at 390 and 820: the bar was wider than the screen, so "New booking"
   was cut off and the whole page scrolled sideways. Fix: the title may shrink (with …), the two booking
   buttons drop their words and keep their icons, and the less important items step aside as the screen
   narrows. Tap targets stay at least 40px. ---------- */
.topbar > div:not(.spacer):not(.mode-switch){min-width:0}
.topbar h1{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.topbar .btn,.topbar .mode-switch{flex:none}
@media (max-width:1000px){
  .topbar{padding:.6rem 1rem;gap:.55rem}
  .topbar .sub{display:none}
  .topbar-quick span:last-child{display:none}            /* "Quick booking" -> + icon (its tooltip still says what it is) */
  .topbar-quick{padding-left:.7rem;padding-right:.7rem}
  .topbar .spacer{flex:1 1 0;min-width:0}
}
@media (max-width:640px){
  .topbar{gap:.4rem;padding:.55rem .7rem}
  .topbar-logo,.topbar .topbar-back + .topbar-back{display:none}   /* logo and forward arrow: the menu and Back are enough */
  .topbar-new span:last-child{display:none}
  .topbar-new{padding-left:.7rem;padding-right:.7rem}
  .topbar .btn,.topbar .topbar-back,.hamburger{min-height:40px;min-width:40px}
  .quicknav{top:56px}
  .qn-link{min-height:44px}
}
@media (max-width:430px){
  .topbar-quick{display:none}                              /* Quick booking is also on the dashboard and in the menu */
}
html,body{max-width:100%;overflow-x:clip}


/* ---------- QUICK EDIT MENU under every booking pencil (13 Oct 2026) ---------- */
.edit-menu{position:fixed;z-index:300;width:min(300px,calc(100vw - 16px));background:#fff;border:1px solid var(--line);
  border-radius:14px;box-shadow:var(--shadow-lg);padding:.35rem;animation:pop .12s}
.edit-menu-head{display:flex;flex-direction:column;gap:.05rem;padding:.55rem .7rem .45rem;border-bottom:1px solid var(--line-2);margin-bottom:.25rem}
.edit-menu-head b{font-size:.78rem;text-transform:uppercase;letter-spacing:.06em;color:var(--muted)}
.edit-menu-head span{font-weight:700;font-size:.92rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.edit-menu-item{display:flex;align-items:center;gap:.7rem;width:100%;min-height:48px;padding:.5rem .7rem;border:0;border-radius:10px;
  background:none;cursor:pointer;text-align:left;font:inherit;color:var(--ink)}
.edit-menu-item:hover,.edit-menu-item:focus-visible{background:var(--brand-soft);outline:none}
.edit-menu-item .em-ico{display:grid;place-items:center;flex:none;width:34px;height:34px;border-radius:9px;background:var(--line-2);color:var(--brand-dark)}
.edit-menu-item .em-txt{display:flex;flex-direction:column;min-width:0}
.edit-menu-item .em-txt b{font-size:.9rem}
.edit-menu-item .em-txt small{font-size:.76rem;color:var(--muted);line-height:1.3}
.edit-menu-item.all{border-top:1px solid var(--line-2);border-radius:0 0 10px 10px;margin-top:.25rem}

/* PARTY ROWS IN THE DAY POPUP ON A PHONE (13 Oct 2026): the status, amount and the two bigger buttons used to sit
   beside the guest name and squeezed it to one word per line. Below 640px the row wraps: name and details on the
   first line, status / amount / buttons underneath. */
@media (max-width:640px){
  .dd-party{flex-wrap:wrap;row-gap:.45rem}
  .dd-party .core{flex:1 1 calc(100% - 2.6rem);min-width:0}
  .dd-party .pickcol{align-self:flex-start}
  .dd-party .side{flex:1 1 100%;justify-content:space-between;flex-wrap:wrap;text-align:left}
}


/* THE AMOUNT IS A BUTTON (13 Oct 2026): a figure that opens the price dialog now looks like one - a pencil beside it and a
   dashed underline - instead of reading as plain text. */
.amt-btn{cursor:pointer;display:inline-flex;align-items:center;gap:.3rem;padding:.2rem .5rem;border-radius:8px;
  border:1px dashed var(--line);background:#fff;transition:.12s}
.amt-btn::after{content:"";width:13px;height:13px;flex:none;opacity:.6;background:no-repeat center/contain
  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238f4a12' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 20h9'/%3E%3Cpath d='M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z'/%3E%3C/svg%3E")}
.amt-btn:hover,.amt-btn:focus-visible{border-color:var(--brand);background:var(--brand-soft);outline:none}
.amt-btn:hover::after{opacity:1}
.amt-btn.unpriced{border-color:#f1d6a8;background:#fff9ef}


/* RETURN RUNS DIALOG (13 Oct 2026): one row per return run, the vehicle that brings it back on the right. */
.ar-row{display:grid;grid-template-columns:auto minmax(0,1.3fr) auto minmax(0,1fr);gap:.8rem;align-items:center;
  padding:.7rem .8rem;border:1px solid var(--line);border-radius:12px;margin-bottom:.55rem;background:#fff}
.ar-row.ar-none{background:#fff9ef;border-color:#f1d6a8}
.ar-row b{display:block;font-size:.92rem}
.ar-row small{display:block;color:var(--muted);font-size:.78rem;line-height:1.35}
.ar-arrow{font-size:1.2rem;color:var(--brand-dark);font-weight:700}
@media (max-width:640px){
  .ar-row{grid-template-columns:auto minmax(0,1fr)}
  .ar-arrow{display:none}
  .ar-veh{grid-column:1 / -1;padding-left:2rem;border-top:1px dashed var(--line);padding-top:.5rem}
}


/* DRIVER + VEHICLE LINE ON EVERY DAY-POPUP TILE (13 Oct 2026) */
.crew-line{display:flex;flex-wrap:wrap;gap:.35rem;margin-top:.4rem}
.crew-chip{display:inline-flex;align-items:center;gap:.35rem;padding:.2rem .6rem;border-radius:999px;background:#eef6f1;
  color:var(--bush-800);font-size:.78rem;border:1px solid #cfe3d6}
.crew-chip b{font-weight:700}
.crew-chip .dot{opacity:.5}
.crew-chip svg{opacity:.75;flex:none}
.crew-line.none,.crew-chip.none{display:inline-flex;align-items:center;gap:.35rem;color:#8a5a0a;background:#fff6e6;
  border:1px solid #f1d6a8;border-radius:999px;padding:.2rem .6rem;font-size:.78rem;font-weight:600;width:fit-content}
