/* css/core.css — tokens, reset, shared components: everything not module-scoped.
   Loads FIRST, ahead of every css/modules/*.css. */

/* css/core.css: tokens, reset, shared components and everything not module-scoped — split out of css/app.css by #360's sibling stage (#362).
   Load order is core.css, then every modules/*.css alphabetically; rules here
   are in their original relative order, and no selector in this file appears in
   any other (asserted in tests/css-split-check.js). */
/* ==========================================================================
   Northfleet FleetView — Prototype styles
   Brand palette (BrandGuidelines V2.2 2026):
   Blue #2552A4 · Orange #F58A2A · Yellow #FCD911 · Red #F23C00
   White #FFFFFF · Near-black #221F20 · Type: Raleway
   ========================================================================== */
:root{
  --blue:#2552A4; --blue-d:#1b3d7a; --blue-l:#3f6cc0;
  --orange:#F58A2A; --yellow:#FCD911; --red:#F23C00;
  --ink:#221F20; --white:#fff;
  --bg:#f4f6fa; --panel:#ffffff; --panel-2:#fbfcfe;
  --line:#e4e9f2; --muted:#6b7280; --muted-2:#9aa3b2;
  --ok:#1f9d55; --warn:#F58A2A; --danger:#F23C00;
  --radius:14px; --radius-sm:10px;
  --shadow:0 1px 3px rgba(34,31,32,.06),0 6px 24px rgba(34,31,32,.06);
  --sidebar-w:236px;
  --font:'Raleway',-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;
}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;height:100%}
body{font-family:var(--font);background:var(--bg);color:var(--ink);font-weight:500;font-size:15px;-webkit-font-smoothing:antialiased}
h1,h2,h3{font-weight:900;letter-spacing:.2px;margin:0}
a{cursor:pointer;text-decoration:none;color:inherit}
small{font-weight:600}
#app{display:flex;min-height:100vh}
/* ---------- Sidebar ---------- */
/* The whole sidebar scrolls (brand + nav + foot together) so short screens
   aren't stuck with a tiny scrollable nav under a pinned logo. */
/* The background is a barely-there vertical gradient rather than flat --ink: it
   gives the column depth against the light main area without introducing a
   colour that isn't in the brand (both stops are the ink family). */
.sidebar{width:var(--sidebar-w);background:linear-gradient(180deg,#2b2728 0%,var(--ink) 38%,#1a1819 100%);color:#fff;display:flex;flex-direction:column;flex-shrink:0;position:sticky;top:0;height:100vh;height:100dvh;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.22) transparent;border-right:1px solid rgba(255,255,255,.06)}
.sidebar::-webkit-scrollbar{width:8px}
.sidebar::-webkit-scrollbar-track{background:transparent}
.sidebar::-webkit-scrollbar-thumb{background:rgba(255,255,255,.18);border-radius:4px}
.sidebar::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.32)}
.brand{padding:24px 20px 18px;display:flex;flex-direction:column;align-items:center;gap:7px;flex-shrink:0}
.brand__mark{width:92px;height:auto;display:block}
/* The rule under the wordmark is a blue→orange gradient rather than a 4px slab
   of blue — same two brand colours as the logo mark above it, at a weight that
   reads as an accent instead of a border. */
.brand__word{font-family:var(--font);font-weight:900;font-size:25px;letter-spacing:.5px;line-height:1;padding-bottom:8px;position:relative}
.brand__word::after{content:"";position:absolute;left:0;right:0;bottom:0;height:3px;border-radius:2px;background:linear-gradient(90deg,var(--blue),var(--orange))}
.brand__north{color:#fff}
.brand__fleet{color:var(--orange)}
.brand__app{font-weight:700;font-size:10.5px;letter-spacing:2.6px;color:#8f97a6;margin-top:3px}
/* Secondary tag under the app name — smaller and dimmer, so it reads as a
   subtitle to "FleetView" rather than competing with it. */
.brand__demo{font-weight:700;font-size:8.5px;letter-spacing:1.3px;text-transform:uppercase;color:#6f7788;margin-top:3px}
.nav{display:flex;flex-direction:column;gap:2px;padding:6px 12px 10px;flex:0 0 auto}
/* Each group is divided by a hairline so the list reads as four short sections
   rather than one long ladder — the headers alone were doing that job on their
   own and, at 10px in a muted grey, not doing it well. */
.nav__group{margin-top:7px;padding-top:7px;border-top:1px solid rgba(255,255,255,.055)}
.nav__section{font-size:10px;font-weight:800;letter-spacing:1.3px;text-transform:uppercase;color:#7c8494;padding:9px 15px 6px}
.nav__section:first-child{padding-top:4px}
/* Collapsible nav groups. The <button>.nav__toggle is the section header; its
   aria-expanded drives the collapse (via :has, as with the .table scroll rule) so
   JS keeps a single source of truth. See initNavGroups() in js/app.js. */
.nav__toggle{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;background:none;border:0;font-family:inherit;cursor:pointer;text-align:left;border-radius:var(--radius-sm);transition:color .15s}
.nav__toggle:hover{color:#c9cfda}
.nav__toggle:focus-visible{outline:2px solid var(--orange);outline-offset:-2px}
/* A stroked caret drawn from two borders, not the ▾ glyph: a solid triangle at
   the far right of a header reads as a form control. font-size:0 hides the glyph
   in the markup while leaving it there for anything that reads text. */
.nav__chev{font-size:0;line-height:0;width:10px;height:10px;position:relative;color:#69717f;transition:transform .2s ease,color .15s;flex-shrink:0}
.nav__chev::before{content:"";position:absolute;top:1px;left:1px;width:6px;height:6px;border-right:1.6px solid currentColor;border-bottom:1.6px solid currentColor;transform:rotate(45deg)}
.nav__toggle:hover .nav__chev{color:#aab2c2}
.nav__group-items{display:grid;grid-template-rows:1fr;transition:grid-template-rows .22s ease}
.nav__group-inner{display:flex;flex-direction:column;gap:2px;overflow:hidden;min-height:0}
.nav__group:has(>.nav__toggle[aria-expanded="false"]) .nav__group-items{grid-template-rows:0fr}
/* Once folded, take the hidden links out of the tab order (visibility:hidden does
   that); delay it by the fold duration so they stay visible while collapsing. */
.nav__group:has(>.nav__toggle[aria-expanded="false"]) .nav__group-inner{visibility:hidden;transition:visibility 0s .22s}
/* The chevron follows its OWN toggle's state, so one rule serves both levels and a
   collapsed division never rotates the chevrons of the groups nested inside it. */
.nav__toggle[aria-expanded="false"] .nav__chev{transform:rotate(-90deg)}
/* ---- Second level: business divisions (issue #82) ----------------------------
   A .nav__division is the top tier (Transport, Wet Hire, …). It wraps either
   .nav__group blocks (Transport) or leaves directly (every other division), and
   collapses with the same grid-rows trick one tier up. */
.nav__division{margin-top:8px;padding-top:8px;border-top:1px solid rgba(255,255,255,.09)}
.nav__division>.nav__toggle{font-size:10.5px;font-weight:800;letter-spacing:1.4px;text-transform:uppercase;color:#9aa3b2;padding:9px 15px 7px}
.nav__division>.nav__toggle:hover{color:#cfd5df}
.nav__division-items{display:grid;grid-template-rows:1fr;transition:grid-template-rows .22s ease}
.nav__division-inner{display:flex;flex-direction:column;gap:2px;overflow:hidden;min-height:0;padding-left:6px}
.nav__division:has(>.nav__toggle[aria-expanded="false"]) .nav__division-items{grid-template-rows:0fr}
.nav__division:has(>.nav__toggle[aria-expanded="false"]) .nav__division-inner{visibility:hidden;transition:visibility 0s .22s}
/* A group nested in a division is a sub-heading: no second hairline, smaller and
   more muted than the division header above it, and its leaves get the SECOND
   indent level so the tree reads division › group › item (matters most in the
   <820px drawer, where the whole column is only ~236px wide). */
.nav__division .nav__group{margin-top:1px;padding-top:1px;border-top:0}
.nav__division .nav__group>.nav__toggle{font-size:9.5px;letter-spacing:1.2px;color:#727a8a;padding:7px 15px 5px}
.nav__division .nav__group-inner{padding-left:8px}
/* An empty division (Dry Hire, Plant & Equip) names itself rather than folding to
   nothing, which would read as broken. */
.nav__division-empty{margin:0;padding:5px 15px 7px;color:#6b7280;font-weight:700;font-size:11px;font-style:italic}
/* A collapsed group OR division with an unread badge inside gets a dot on its
   header so an alert isn't hidden by the fold (refreshNavCollapsedBadges, js/app.js). */
.nav__toggle.has-alert>span:first-child{position:relative}
.nav__toggle.has-alert>span:first-child::after{content:"";position:absolute;top:0;right:-14px;width:6px;height:6px;border-radius:50%;background:var(--red);box-shadow:0 0 0 3px rgba(242,60,0,.18)}
@media(prefers-reduced-motion:reduce){.nav__group-items,.nav__chev,.nav__group-inner,.nav__division-items,.nav__division-inner{transition:none}}
.nav__item{position:relative;display:flex;align-items:center;gap:11px;padding:9px 14px;border-radius:var(--radius-sm);color:#c9cfda;font-weight:700;font-size:15px;transition:.15s;user-select:none}
.nav__item:hover{background:rgba(255,255,255,.07);color:#fff}
/* Softer than the flat saturated block it replaces: a gradient fill, a rounded
   orange marker and a shadow, so the current screen sits above the list rather
   than being punched into it. The marker is *inset* from the pill's edge and
   fully rounded — flush at left:0 with square corners it reads as a stray tab
   escaping the rounded pill rather than as a "you are here" accent. */
.nav__item.is-active{background:linear-gradient(90deg,rgba(37,82,164,.98),rgba(37,82,164,.74));color:#fff;box-shadow:0 2px 10px rgba(0,0,0,.28)}
.nav__item.is-active::before{content:"";position:absolute;left:5px;top:50%;transform:translateY(-50%);width:3px;height:16px;border-radius:2px;background:var(--orange)}
/* The icons are unicode glyphs, so they arrive at wildly different optical
   weights and widths (⇩ $ ₳ ☰ ⚠ …). Giving each an identical rounded chip
   normalises them: the glyph still varies, the shape the eye tracks does not. */
.nav__ico{font-size:14px;width:26px;height:26px;flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;border-radius:8px;background:rgba(255,255,255,.05);color:#98a2b3;transition:.15s}
.nav__item:hover .nav__ico{background:rgba(255,255,255,.1);color:#fff}
.nav__item.is-active .nav__ico{background:rgba(255,255,255,.17);color:#fff}
.sidebar__foot{padding:16px;border-top:1px solid rgba(255,255,255,.08);font-size:12px;flex-shrink:0;margin-top:auto}
.conn{display:flex;align-items:center;gap:8px;color:#aeb6c4;margin-bottom:8px;font-weight:700}
.conn small{color:#7b8494;font-weight:600;margin-left:2px}
.conn .dot{width:9px;height:9px;border-radius:50%;background:var(--muted-2);box-shadow:0 0 0 0 rgba(31,157,85,.5)}
.conn.is-live .dot{background:var(--ok);animation:pulse 2s infinite}
.conn.is-down .dot{background:var(--red);animation:none}
/* Customer-specific connote reference (Load ID / Job number) */
.cref{margin-top:16px;padding:14px;border:1px solid var(--line);border-radius:12px;background:#f8fafc}
.cref__hd{display:flex;align-items:center;gap:8px;margin-bottom:8px;font-size:15px}
.cref__row{display:flex;gap:8px}
.cref__row input{flex:1;padding:10px;border:1px solid var(--line);border-radius:8px;font-family:var(--font);font-weight:700}
.cref__note{color:var(--red);font-weight:700;font-size:12px;margin-top:6px}
.shake{animation:shake .35s}
@keyframes shake{0%,100%{transform:translateX(0)}25%{transform:translateX(-5px)}75%{transform:translateX(5px)}}
@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(31,157,85,.5)}70%{box-shadow:0 0 0 7px rgba(31,157,85,0)}100%{box-shadow:0 0 0 0 rgba(31,157,85,0)}}
.sidebar__ver{color:#5f6879;margin-top:10px;font-weight:600}
.sidebar__ver-link{color:#8b93a3;text-decoration:underline;text-underline-offset:2px}
.sidebar__ver-link:hover{color:var(--orange,#F58A2A)}
/* ---------- Main / topbar ---------- */
.main{flex:1;display:flex;flex-direction:column;min-width:0}
.topbar{display:flex;align-items:center;gap:16px;padding:0 26px;height:66px;background:var(--panel);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:20}
.topbar__titles{display:flex;flex-direction:column;justify-content:center;min-width:0}
.topbar__title{font-size:22px;line-height:1.1}
.topbar__hint{font-size:12.5px;font-weight:600;color:var(--muted);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:60vw}
@media(max-width:720px){.topbar__hint{display:none}}
.topbar__search{position:relative;margin-left:auto;width:min(340px,32vw)}
.topbar__search input{width:100%;padding:10px 14px;border:1px solid var(--line);border-radius:10px;font-family:var(--font);font-weight:600;font-size:14px;background:#f4f6fa}
.topbar__search input:focus{outline:none;border-color:var(--blue);background:#fff;box-shadow:0 0 0 3px rgba(37,82,164,.12)}
.gsr{display:flex;align-items:center;gap:10px;padding:11px 14px;cursor:pointer;border-bottom:1px solid var(--line)}
.gsr:last-child{border-bottom:none}
.gsr:hover,.gsr.is-active{background:var(--panel-2)}
.gsr__type{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.5px;color:#fff;background:var(--blue);border-radius:5px;padding:2px 7px;flex-shrink:0}
.gsr__type--Quote{background:var(--orange)}
.gsr__type--Client{background:var(--ok)}
.gsr__type--Job{background:var(--muted)}
.gsr__type--Asset{background:var(--yellow);color:var(--ink)}
.gsr__label{font-weight:700;font-size:14px}
.gsr__none{padding:14px;color:var(--muted);font-weight:700;font-size:13px}
@media(max-width:900px){.topbar__search{display:none}}
.req{color:var(--red);font-weight:900}
.topbar__right{display:flex;align-items:center;gap:22px}
.clock{font-weight:800;color:var(--blue);font-variant-numeric:tabular-nums}
.user{display:flex;align-items:center;gap:10px;font-weight:700;background:none;border:none;font-family:var(--font);cursor:pointer;color:var(--ink);padding:4px 6px;border-radius:10px}
.user:hover{background:var(--panel-2)}
.user__caret{color:var(--muted-2);font-size:12px}
.user__avatar{width:34px;height:34px;border-radius:50%;background:var(--blue);color:#fff;display:grid;place-items:center;font-size:13px;font-weight:800}
.hamburger{display:none;background:none;border:none;font-size:22px;color:var(--ink);cursor:pointer;
  min-width:44px;min-height:44px;border-radius:8px}
.hamburger:active{background:var(--panel-2)}
/* Scrim behind the mobile nav drawer. Tapping it closes the drawer, which is
   what everyone reaches for first, and it dims the page so it's obvious the
   drawer — not the content behind it — has the input. */
.scrim{position:fixed;inset:0;z-index:55;background:rgba(20,28,44,.5);border:none;padding:0;
  opacity:0;pointer-events:none;transition:opacity .25s}
.scrim.is-open{opacity:1;pointer-events:auto}
/* Above the drawer breakpoint the sidebar is always on screen, so a scrim left
   over from a rotate/resize would dim the app with nothing to dismiss. */
@media(min-width:821px){.scrim{display:none}}
@media(prefers-reduced-motion:reduce){.scrim{transition:none}}
.view{padding:26px;flex:1;overflow:auto}
/* ---------- Generic UI ---------- */
.btn{font-family:var(--font);font-weight:800;font-size:14px;border:none;border-radius:10px;padding:12px 20px;cursor:pointer;transition:.15s;min-height:44px}
.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
.badge{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:800;padding:5px 11px;border-radius:999px;white-space:nowrap}
.badge::before{content:"";width:7px;height:7px;border-radius:50%;background:currentColor}
/* ---------- Grid helpers ---------- */
.grid{display:grid;gap:18px}
/* auto-fit rather than a fixed count: five screens share this class with
   different numbers of tiles, and hardcoding 4 left the dashboard's fifth tile
   alone on a second row while hardcoding 5 left an empty column on every other
   screen. Empty tracks collapse, so N tiles give N equal columns. */
.kpis{grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}
.kpi{padding:20px}
.kpi__label{font-size:13px;color:var(--muted);font-weight:700;text-transform:uppercase;letter-spacing:.6px}
.kpi__value{font-size:38px;font-weight:900;line-height:1.1;margin-top:6px}
.kpi__sub{font-size:13px;color:var(--muted);font-weight:700;margin-top:4px}
.kpi__value--blue{color:var(--blue)}
.kpi__value--orange{color:var(--orange)}
.kpi__value--green{color:var(--ok)}
.kpi__value--red{color:var(--red)}
/* ---------- Tables ---------- */
/* Tables set their own natural width from their content, so a six-column list
   in a phone-width card used to overflow and get clipped by an ancestor — the
   right-hand columns (status, ETA) were simply unreachable. Any element whose
   direct child is a .table becomes its own horizontal scroll container instead.
   The gradients are a scroll affordance: `background-attachment:local` pins the
   white pair to the content so they slide away, while the shadow pair stays
   fixed to the frame — the shadow on an edge is only visible while there is
   still content off that side. Pure CSS, no per-render-function change. */
.table{width:100%;border-collapse:collapse}
:where(.card,.detail__main,.detail__side,.rep-card,div):has(>.table){
  overflow-x:auto;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain;
  background:
    linear-gradient(to right,var(--panel) 30%,rgba(255,255,255,0)) left center,
    linear-gradient(to left,var(--panel) 30%,rgba(255,255,255,0)) right center,
    linear-gradient(to right,rgba(37,82,164,.16),rgba(37,82,164,0)) left center,
    linear-gradient(to left,rgba(37,82,164,.16),rgba(37,82,164,0)) right center;
  background-repeat:no-repeat;
  background-size:34px 100%,34px 100%,12px 100%,12px 100%;
  background-attachment:local,local,scroll,scroll;
}
/* The wall displays are wide and unscrollable by nature — no affordance needed,
   and a stray scrollbar on a monitor looks like a fault. */
body.wall :where(.card,.detail__main,.detail__side,.rep-card,div):has(>.table){
  background-image:none}
.table th{text-align:left;font-size:12px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted);font-weight:800;padding:12px 16px;border-bottom:2px solid var(--line)}
.table td{padding:14px 16px;border-bottom:1px solid var(--line);font-weight:600}
.table tr{cursor:pointer}
.table tbody tr:hover{background:var(--panel-2)}
.table .mono{font-variant-numeric:tabular-nums;font-weight:800;color:var(--blue)}
/* ---------- Getting started ---------- */
.gs{padding:18px 20px;margin-bottom:18px;position:relative;background:linear-gradient(135deg,#1b3d7a,#2552A4);color:#fff;border:none}
.gs__close{position:absolute;top:12px;right:12px;background:rgba(255,255,255,.15);border:none;color:#fff;width:28px;height:28px;border-radius:50%;cursor:pointer;font-weight:800}
.gs__close:hover{background:rgba(255,255,255,.28)}
.gs__head{font-weight:700;font-size:14px;margin-bottom:14px;max-width:90%}
.gs__head b{font-weight:900}
.gs__steps{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.gs__step{display:flex;align-items:center;gap:8px;background:rgba(255,255,255,.12);border:1px solid rgba(255,255,255,.25);border-radius:10px;padding:9px 13px;font-weight:800;font-size:13px;color:#fff;transition:.15s}
.gs__step:hover{background:rgba(255,255,255,.22)}
.gs__num{width:20px;height:20px;border-radius:50%;background:var(--orange);display:grid;place-items:center;font-size:11px;font-weight:900;flex-shrink:0}
.gs__arrow{color:rgba(255,255,255,.5);font-weight:900}
@media(max-width:820px){.gs__arrow{display:none}}
/* On its own line in a figures column, inline in the prose that explains it.
   `width:fit-content` + auto margin rather than plain `display:block`: a block in
   a right-aligned cell stretches the chip's background across the whole column
   and it reads as a bar rather than a label. */
.num .spd-cert{display:block;width:fit-content;margin:3px 0 0 auto}
/* Tabs: tablist / tab / tabpanel. Selection + arrow keys are wired once in
   initKeyboard(), not per screen. */
.tabs{display:flex;gap:6px;background:#eef1f6;padding:5px;border-radius:12px;flex-wrap:wrap}
.tabs [role=tab]{border:none;background:none;padding:9px 15px;border-radius:8px;font-weight:800;font-size:13px;color:var(--muted);cursor:pointer;font-family:var(--font)}
.tabs [role=tab][aria-selected=true]{background:#fff;color:var(--blue);box-shadow:0 1px 2px rgba(0,0,0,.08)}
.tabs [role=tab]:focus-visible{outline:2px solid var(--blue);outline-offset:2px}
[role=tabpanel][hidden]{display:none}
/* On a wall monitor nobody clicks a tab, so show every panel and drop the
   tablist — the columns simply scroll. */
body.wall .tabs{display:none}
body.wall [role=tabpanel][hidden]{display:block}
/* ---------- Dashboard ---------- */
/* Live-fleet filter bar. Wraps rather than scrolls, so on a phone the two
   selects stack under the label instead of pushing the count off the card. */
.dfil{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:12px 16px;margin-top:18px}
.dfil__lbl{font-weight:900;font-size:13px;letter-spacing:.3px;text-transform:uppercase;color:var(--muted)}
.dfil__sel{border:1px solid var(--line);border-radius:8px;padding:7px 10px;font-family:var(--font);font-weight:700;font-size:14px;background:#fff;color:var(--ink)}
.dfil__count{margin-left:auto;color:var(--muted);font-weight:700;font-size:13px}
/* Full width on its own line: this is the coverage caveat, not a footnote, and
   it must not be squeezed into whatever space the selects leave over. */
.dfil__note{flex-basis:100%;margin:2px 0 0;color:var(--muted);font-weight:700;font-size:13px;line-height:1.5}
.dfil__note[hidden]{display:none}
@media(max-width:520px){.dfil__count{margin-left:0;width:100%}}
/* Live Fleet's side list carries each asset's coordinates when there is no map
   to put a pin on. A div row, not an anchor, so the geoLink can nest. */
.veh__geo{margin-top:2px}
/* Speed sparkline in an asset popup (24 h, expandable to 7 days). The body's
   min-height holds the row open while the history is fetched so the popup
   doesn't jump size as it fills — Leaflet has already positioned and sized the
   bubble by then. */
.worm{margin-top:7px;padding-top:6px;border-top:1px solid #eee}
.worm svg{display:block}
.worm__body{min-height:34px}
.worm__note{display:block;color:var(--muted-2);font-weight:700;font-size:10.5px;margin-top:1px}
/* The 24 h / 7 d toggle. Compact — the popup is ~200px wide — and each button
   fills half the row so the tap target is generous. */
.worm__tabs{display:flex;gap:4px;margin-bottom:5px}
.worm__tabs button{flex:1;border:1px solid #e6e9f0;background:#f6f8fb;color:var(--muted);
  font-family:var(--font);font-weight:800;font-size:10.5px;padding:3px 6px;border-radius:6px;cursor:pointer}
.worm__tabs button.is-active{background:#fff;color:var(--blue);border-color:var(--blue)}
#dash-fleet{max-height:352px;overflow-y:auto;padding-right:4px}
#dash-alerts{max-height:352px}
.act{display:flex;align-items:center;gap:12px;padding:11px 0;border-bottom:1px solid var(--line)}
.act:last-child{border-bottom:none}
.act__ico{width:36px;height:36px;border-radius:9px;display:grid;place-items:center;font-size:15px;flex-shrink:0;color:#fff}
.act__txt{flex:1}
.act__txt b{font-weight:800}
.act__time{color:var(--muted-2);font-size:12px;font-weight:700}
.bar{height:10px;border-radius:6px;background:#eef1f6;overflow:hidden;margin-top:6px}
.bar__fill{height:100%;border-radius:6px}
#map{width:100%;height:100%}
#dash-map{width:100%;height:340px}
/* also used as an <a> on the dashboard's Fleet status list, where each row links
   through to Live Fleet — inherit type and colour so the anchor is invisible */
.veh{display:flex;gap:12px;padding:13px;border-radius:12px;cursor:pointer;border:1px solid transparent;
  color:inherit;text-decoration:none;font:inherit}
.veh:focus-visible{outline:2px solid var(--blue);outline-offset:-2px}
.veh:hover{background:var(--panel-2)}
.veh.is-active{background:#e8effb;border-color:#c9d9f5}
.veh__dot{width:12px;height:12px;border-radius:50%;margin-top:5px;flex-shrink:0}
.veh__b{flex:1;min-width:0}
.veh__name{font-weight:800}
.veh__meta{font-size:12px;color:var(--muted);font-weight:700}
.veh__spd{font-weight:800;color:var(--blue);font-variant-numeric:tabular-nums}
/* ---------- Dispatch board ---------- */
.board{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;align-items:start}
.col{background:#eef1f6;border-radius:var(--radius);padding:12px;min-height:200px}
.col__head{display:flex;align-items:center;justify-content:space-between;padding:4px 8px 12px;font-weight:800;font-size:14px}
.col__count{background:#fff;color:var(--muted);border-radius:999px;padding:2px 10px;font-size:12px;font-weight:800}
.job{background:#fff;border-radius:11px;padding:14px;margin-bottom:10px;box-shadow:0 1px 2px rgba(34,31,32,.06);border-left:4px solid var(--blue);cursor:grab}
.job:active{cursor:grabbing}
.job.drag-over{outline:2px dashed var(--blue)}
.job__ref{font-weight:900;color:var(--blue);font-size:13px}
.job__route{font-weight:800;margin:5px 0 3px}
.job__meta{font-size:12px;color:var(--muted);font-weight:700;display:flex;flex-wrap:wrap;gap:4px 10px;margin-top:6px}
.col.drop-target{outline:2px dashed var(--blue);outline-offset:-6px}
/* ---------- Consignments ---------- */
.toolbar{display:flex;gap:12px;align-items:center;margin-bottom:18px;flex-wrap:wrap}
.search{flex:1;min-width:200px;position:relative}
.search input{width:100%;padding:12px 14px 12px 40px;border:1px solid var(--line);border-radius:10px;font-family:var(--font);font-weight:600;font-size:14px;background:#fff}
.search::before{content:"⌕";position:absolute;left:14px;top:9px;font-size:18px;color:var(--muted-2)}
/* Consignment detail */
.detail{display:grid;grid-template-columns:1fr 340px;gap:18px}
.detail__main{padding:22px}
.detail__side{padding:20px;align-self:start}
.crumb{color:var(--blue);font-weight:800;font-size:13px;margin-bottom:14px;display:inline-block}
.dl{display:grid;grid-template-columns:auto 1fr;gap:10px 20px;margin:16px 0}
.dl dt{color:var(--muted);font-weight:700;font-size:13px}
.dl dd{margin:0;font-weight:700}
.timeline{list-style:none;padding:0;margin:14px 0 0}
.timeline li{position:relative;padding:0 0 18px 26px;font-weight:700;font-size:14px}
.timeline li::before{content:"";position:absolute;left:5px;top:3px;width:11px;height:11px;border-radius:50%;background:var(--blue)}
.timeline li::after{content:"";position:absolute;left:10px;top:14px;bottom:-4px;width:2px;background:var(--line)}
.timeline li:last-child::after{display:none}
.timeline li small{display:block;color:var(--muted);font-weight:600;margin-top:2px}
.timeline li.done::before{background:var(--ok)}
/* ---------- Signature modal ---------- */
/* z-index clears Leaflet: its map panes run 400–700 and its controls (the +/-
   zoom) sit at 1000, so a modal opened over a screen with a background map
   (Geofences, Live Fleet, the dashboard mini-map) must sit above 1000 or the
   map paints over it — the delete-confirm's buttons were hidden behind the
   "All fences" map (#194). This is the systemic fix: every .modal in the app
   shares this rule, so a confirm/dialog always clears a map anywhere. The tour
   overlay is lifted in step (kept above the modal) so the same map bug can't
   bite it and the modal-vs-tour order is unchanged. */
.modal{position:fixed;inset:0;background:rgba(34,31,32,.55);display:none;align-items:center;justify-content:center;z-index:1100;padding:16px}
.modal.is-open{display:flex}
.modal__card{background:#fff;border-radius:18px;width:min(560px,100%);max-height:94vh;overflow:auto;box-shadow:0 20px 60px rgba(0,0,0,.35)}
.modal__head{display:flex;align-items:center;justify-content:space-between;padding:20px 22px;border-bottom:1px solid var(--line)}
.modal__head h2{font-size:20px}
.modal__close{border:none;background:#eef1f6;width:38px;height:38px;border-radius:50%;font-size:16px;cursor:pointer}
.modal__body{padding:20px 22px}
.modal__foot{display:flex;gap:12px;justify-content:flex-end;padding:16px 22px;border-top:1px solid var(--line)}
.fld{display:block;margin-bottom:16px}
.fld span{display:block;font-weight:800;font-size:13px;margin-bottom:6px}
.fld input,.fld select{width:100%;padding:13px 14px;border:1px solid var(--line);border-radius:10px;font-family:var(--font);font-size:16px;font-weight:600;background:#fff;color:var(--ink)}
.fld input:focus,.fld select:focus{outline:none;border-color:var(--blue);box-shadow:0 0 0 3px rgba(37,82,164,.15)}
#nc-items-tbl input{width:100%;padding:9px 10px;border:1px solid var(--line);border-radius:8px;font-family:var(--font);font-weight:600;font-size:14px}
#nc-items-tbl input:focus{outline:none;border-color:var(--blue)}
#nc-items-tbl td{vertical-align:middle}
#nc-items-tbl tfoot td{border-bottom:none;padding-top:14px}
.chk{display:flex;align-items:center;gap:10px;margin-top:16px;font-weight:700;font-size:14px}
.chk input{width:20px;height:20px}
.modal__card.is-fail .modal__head h2{color:var(--red)}
#sig-mode.is-fail-active{background:#fde6df;color:var(--red)}
/* ---------- Alerts panel ---------- */
.alerts{display:flex;flex-direction:column;gap:8px;max-height:280px;overflow:auto}
.barlist{padding:20px}
.barrow{margin-bottom:14px}
.barrow__top{display:flex;justify-content:space-between;font-weight:800;font-size:14px;margin-bottom:5px}
.barrow__val{color:var(--muted)}
.barrow .bar{height:14px}
/* ---------- Login gate ---------- */
/* Login backdrop: the fleet photo, a scrim to keep the card readable over it,
   and the old brand gradient underneath as the bottom layer. Layers paint
   top-first, so if assets/login-bg.jpg is missing or still loading the gradient
   shows through the scrim and the screen looks deliberate rather than broken.
   Positioned at 65% rather than centred: the photo is 3:2 and a landscape window
   is wider, so `cover` crops top and bottom — centred, the crop sliced through
   the logo in the image's top-left. Biasing down takes the whole crop off the
   top, so the logo leaves frame cleanly and the vehicles stay in shot. */
.login{position:fixed;inset:0;
  background:
    linear-gradient(180deg,rgba(10,18,34,.58) 0%,rgba(10,18,34,.34) 42%,rgba(10,18,34,.72) 100%),
    url('../assets/login-bg.jpg') center 65% / cover no-repeat,
    linear-gradient(135deg,#1b3d7a 0%,#221F20 90%);
  background-color:#16203a;
  /* Card sits to the right so the bus and truck in the photo aren't hidden
     behind it. `place-items: center end` = centred vertically, flush to the
     inline end; the right padding keeps it off the edge. Below 900px the
     card is nearly the full width anyway, so it goes back to centred. */
  /* A definite column, so .login__card's `width:min(380px,100%)` resolves its
     100% against the container rather than against a content-sized column.
     Without it the card stayed 380px wide on a 360-390px phone and hung off
     the right edge — the inputs' right side was simply off screen. */
  display:grid;grid-template-columns:minmax(0,1fr);place-items:center end;z-index:500;
  padding:20px clamp(20px,5vw,80px) 40px 20px;
  /* The card is ~650px tall. On a short window (small laptop, phone in
     landscape) it was taller than the viewport and simply clipped — the
     Sign in button and the portal QRs were off screen and unreachable,
     because .login is position:fixed with nothing scrollable. */
  overflow-y:auto;overscroll-behavior:contain}
/* On a phone the photo crops hard and the card fills most of the screen, so
   lean on the scrim rather than the picture. */
@media(max-width:900px){ .login{place-items:center;padding:20px 20px 40px} }
@media(max-width:560px){
  .login{background:
    linear-gradient(180deg,rgba(10,18,34,.72) 0%,rgba(10,18,34,.62) 50%,rgba(10,18,34,.82) 100%),
    url('../assets/login-bg.jpg') center 65% / cover no-repeat,
    linear-gradient(135deg,#1b3d7a 0%,#221F20 90%);
  background-color:#16203a}
}
.login.is-hidden{display:none}
.login__card{background:#fff;border-radius:18px;padding:32px 30px;width:min(380px,100%);display:flex;flex-direction:column;box-shadow:0 30px 80px rgba(0,0,0,.45)}
.login__mark{width:78px;margin:0 auto}
.login__word{text-align:center;font-weight:900;font-size:26px;margin-top:8px}
.login__word b{color:var(--ink)}
.login__word i{color:var(--orange);font-style:normal}
.login__app{text-align:center;font-weight:800;font-size:12px;letter-spacing:2px;color:var(--muted);margin-bottom:3px}
.login__demo{text-align:center;font-weight:700;font-size:10px;letter-spacing:1.4px;text-transform:uppercase;color:var(--muted-2,#9aa2b1);margin-bottom:18px}
.login__lbl{font-weight:800;font-size:13px;margin:12px 0 5px}
.login select,.login input{padding:13px 14px;border:1px solid var(--line);border-radius:10px;font-family:var(--font);font-weight:600;font-size:15px;background:#fff}
.login select:focus,.login input:focus{outline:none;border-color:var(--blue);box-shadow:0 0 0 3px rgba(37,82,164,.15)}
.login .btn{margin-top:18px;width:100%}
.login__err{color:var(--red);font-weight:800;font-size:13px;min-height:16px;margin-top:8px}
.login__hint{color:var(--muted-2);font-weight:700;font-size:12px;margin-top:16px;text-align:center;line-height:1.5}
.login__qr{display:flex;gap:16px;justify-content:center;margin-top:18px;padding-top:16px;border-top:1px solid var(--line)}
.login__qr-item{display:flex;flex-direction:column;align-items:center;gap:6px;border-radius:10px;padding:6px;text-decoration:none;color:var(--ink)}
a.login__qr-item:hover{background:var(--panel-2);outline:1px solid var(--line)}
.login__qr-box{width:104px;height:104px;display:grid;place-items:center}
.login__qr-item small{font-weight:800;font-size:12px;color:var(--ink)}
.login__qr-cap{text-align:center;color:var(--muted-2);font-weight:700;font-size:11px;margin-top:8px}
/* Staff single sign-on (#66). The `sso-` element names sit under .login__ /
   .user-menu__ rather than taking a new prefix, because they are parts of two
   existing components and not a new one.

   The Microsoft mark is drawn in CSS from four conic-gradient quadrants — no
   image request, for the same reason the QR encoder is hand-written (#265): an
   asset that fails to load on the sign-in screen is the one that matters most,
   and this one would fail silently, leaving a button that reads as unbranded
   rather than broken. Colours are Microsoft's published brand values. */
/* display:contents so wrapping the picker in a <form> — done so the Microsoft
   button is not inside it — changes no layout at all. The form still submits;
   display:contents affects boxes, not semantics. */
.login__form{display:contents}
.login__sso{margin-top:4px}
.login__sso-btn{width:100%;display:flex;align-items:center;justify-content:center;gap:10px;
  /* 48px: the same touch target the driver portal holds itself to, because a
     depot machine is as likely to have a touchscreen as a mouse. */
  min-height:48px;font-size:15px}
.login__sso-ms{width:18px;height:18px;flex:0 0 18px;border-radius:1px;
  background:
    linear-gradient(#f25022,#f25022) 0 0/8px 8px no-repeat,
    linear-gradient(#7fba00,#7fba00) 10px 0/8px 8px no-repeat,
    linear-gradient(#00a4ef,#00a4ef) 0 10px/8px 8px no-repeat,
    linear-gradient(#ffb900,#ffb900) 10px 10px/8px 8px no-repeat}
.login__sso-err{color:var(--red);font-weight:700;font-size:12.5px;line-height:1.5;margin-top:10px;min-height:0}
/* The divider is a real "or", not decoration: it is what tells somebody the
   picker below is still available while SSO is being rolled out. */
.login__sso-or{display:flex;align-items:center;gap:10px;margin:18px 0 4px;
  color:var(--muted-2);font-weight:700;font-size:11px;text-transform:uppercase;letter-spacing:.04em}
.login__sso-or::before,.login__sso-or::after{content:"";flex:1;height:1px;background:var(--line)}
/* Build stamp across the foot of the login screen. Absolutely positioned, not a
   grid child: .login is `display:grid;place-items:center`, so a second child
   would take its own row and shove the card off centre. Sits on the darkest
   part of the scrim, with a shadow so it stays legible whatever the photo does
   behind it. The extra bottom padding on .login keeps the card clear of it on
   short viewports. */
.login__ver{position:absolute;left:0;right:0;bottom:14px;text-align:center;
  color:rgba(255,255,255,.78);font-weight:700;font-size:12px;letter-spacing:.2px;
  text-shadow:0 1px 3px rgba(0,0,0,.55)}
/* The version is a link to the changelog, same as the sidebar's. Padded out to
   a usable target without moving the line — the negative margin keeps the
   stamp sitting where it did. */
.login__ver-link{display:inline-block;padding:8px 10px;margin:-8px -10px;
  color:#fff;text-decoration:underline;text-underline-offset:2px}
.login__ver-link:hover{color:var(--yellow,#FCD911)}
/* ---------- Job flow (same as driver portal docket) ---------- */
.jobflow{max-width:560px}
.fstep{display:flex;align-items:center;gap:12px;padding:10px 0;position:relative}
.fstep::before{content:"";position:absolute;left:15px;top:42px;bottom:-10px;width:2px;background:var(--line)}
.fstep:last-of-type::before{display:none}
.fstep__n{width:32px;height:32px;border-radius:50%;background:#e4e9f2;color:var(--muted);display:grid;place-items:center;font-weight:900;flex-shrink:0;z-index:1}
.fstep__b{flex:1;min-width:0}
.fstep__b b{display:block;font-size:14.5px}
.fstep__b small{color:var(--muted);font-weight:700;font-size:11.5px}
.fstep__c{flex-shrink:0}
.fstep__c .tm-in{padding:9px 10px;border:1px solid var(--line);border-radius:9px;font-family:var(--font);font-weight:700;font-size:14px}
/* ---------- Click-to-call ---------- */
.tel{color:var(--blue);font-weight:800;text-decoration:none;white-space:nowrap}
.tel:hover{text-decoration:underline}
.pflow{display:flex;overflow-x:auto;padding-bottom:4px}
.pflow__step{flex:1;min-width:92px;text-align:center;position:relative;padding-top:4px}
.pflow__step::before{content:"";position:absolute;top:19px;left:-50%;width:100%;height:3px;background:var(--line);z-index:0}
.pflow__step:first-child::before{display:none}
.pflow__dot{position:relative;z-index:1;width:32px;height:32px;border-radius:50%;background:#e1e6ef;color:#fff;display:grid;place-items:center;margin:0 auto 9px;font-weight:900;font-size:13px}
.pflow__lbl{font-size:12px;font-weight:800;color:var(--muted-2);display:block;padding:0 4px}
.pflow__step.is-done .pflow__dot{background:var(--ok)}
.pflow__step.is-done::before{background:var(--ok)}
.pflow__step.is-current .pflow__dot{background:var(--orange);box-shadow:0 0 0 5px rgba(245,138,42,.22)}
.pflow__step.is-current .pflow__lbl{color:var(--ink)}
.pflow__step.is-failed .pflow__dot{background:var(--red);box-shadow:0 0 0 5px rgba(242,60,0,.18)}
.pflow__step.is-failed .pflow__lbl{color:var(--red)}
/* ---------- Weekly Schedule ---------- */
.sched{border-collapse:separate;border-spacing:0;width:100%;min-width:1000px}
.sched th{background:#f7f9fc;border-bottom:2px solid var(--line);padding:9px 6px;font-size:12px;text-transform:uppercase;color:var(--muted);font-weight:800;text-align:center}
.sched th small{display:block;color:var(--muted-2);font-weight:700;font-size:11px}
.sched td{border-bottom:1px solid var(--line);border-right:1px solid var(--line);padding:4px;vertical-align:top;width:150px;height:66px}
.sched__cell .sc{cursor:pointer;margin-bottom:4px}
.sched__cell .sc:last-child{margin-bottom:0}
/* per-day assignment chips */
.asg{display:flex;flex-wrap:wrap;gap:3px;margin-bottom:6px}
/* summary rows */
.sched tfoot .sum-row td{border-top:1px solid var(--line);background:#fbfcfe;font-size:11px;font-weight:700;vertical-align:top;padding:8px 6px}
.sched tfoot .sum-head td{background:#f0f3f8;font-weight:900;font-size:12px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);padding:9px 12px}
.sched tfoot .sched__lane{font-weight:800}
.sched th.is-today,.sched td.is-today{background:#eef4ff}
/* Today needs to be findable at a glance on a 9-day board, and a past day
   shouldn't read as something you can still plan into. */
.sched th.is-today{background:var(--blue);color:#fff;box-shadow:inset 0 -3px 0 var(--orange)}
.sched th.is-today small{color:#cfe0ff}
.sched th.is-past,.sched td.is-past{background:#f4f5f7}
.sched th.is-past{color:var(--muted-2)}
.sched td.is-past{opacity:.72}
.sched th.is-weekend:not(.is-today){background:#eef1f6}
.scfl{display:flex;gap:10px;padding:8px 10px;border-radius:8px;background:#fff;border:1px solid var(--line);cursor:pointer;align-items:flex-start}
.scfl:hover{border-color:var(--red)}
.scfl__k{font-size:16px;line-height:1.2}
.scfl__d{display:block;color:var(--muted);font-weight:700;font-size:12.5px;margin-top:2px}
.sched__cell.is-conflict{outline:2px solid var(--orange);outline-offset:-2px}
.sched__cell.is-conflict-crit{outline-color:var(--red)}
.sched__cell.is-flash{animation:scflash 1.5s ease-out}
@keyframes scflash{0%,45%{background:rgba(242,60,0,.18)}100%{background:transparent}}
/* HQ ↔ driver messaging */
.nav__badge{margin-left:auto;background:var(--red);color:#fff;font-size:10.5px;font-weight:900;min-width:17px;height:17px;border-radius:9px;display:inline-flex;align-items:center;justify-content:center;padding:0 5px;letter-spacing:.2px;box-shadow:0 0 0 2px rgba(0,0,0,.16)}
/* An author `display` beats the UA stylesheet's [hidden]{display:none}, so
   without this every badge set to hidden still rendered — as an empty red dot
   that looked like a permanent alert. */
.nav__badge[hidden]{display:none}
/* ---- #280 who did which component, and handing the remainder on -----------
   The panel that makes "Toby's box doesn't go empty" visible: per component,
   the driver who owns it and everything they captured against it. Prefix `hov-`,
   verified unused across css/app.css, driver/driver.css and portal/portal.css
   first. The state is WORDED, not colour-coded -- #278 owns the board's traffic
   light and a second palette here would be a second answer to one question -- so
   the only colour is a left edge, and "nothing captured yet" is drawn HATCHED
   exactly as .cmp-pill--indet is, because it is neither done nor not-due and it
   is the fact the hand-over refusal turns on. */
.hov{margin-top:14px;border-top:1px solid var(--line);padding-top:12px}
.hov__t{margin:0 0 8px;font-size:12px;font-weight:900;letter-spacing:.5px;text-transform:uppercase;color:var(--muted)}
.hov__list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}
/* ---- #277 component chips: which phases this component covers -------------
   Pickup / Line haul / Drop off, multi-select, collapsing to ONE "One shot"
   chip when a single driver does the lot (three chips saying the same thing is
   the noise that teaches people to stop reading a row). Prefix `cov-`, verified
   unused across css/app.css, driver/driver.css and portal/portal.css first. */
.cov{display:flex;flex-wrap:wrap;gap:5px;margin:4px 0 2px}
.table td.num,.table th.num{text-align:right;white-space:nowrap}
.sched__lane{position:sticky;left:0;background:#fff;z-index:2;min-width:196px;text-align:left;border-right:1px solid var(--line);padding:10px 12px}
/* Just wide enough for a site code (PHE/KTA/ZNE/HAZ) — the width freed here
   goes to the day columns, which is where the work is. */
.sched td.sched__depot,.sched th.sched__depot{width:64px;min-width:64px;max-width:64px}
.sched__depot{text-align:center;padding:10px 6px;vertical-align:middle;border-right:2px solid var(--line);background:#fbfcfe}
th.sched__depot{background:#f7f9fc}
.sc{border-radius:8px;padding:7px 9px;min-height:58px;font-size:11px;font-weight:700;display:flex;flex-direction:column;gap:2px}
.sc b{font-size:12px;font-weight:800;line-height:1.18}
.sc[data-cn]{cursor:pointer;transition:.12s}
.sc[data-cn]:hover{filter:brightness(.96);box-shadow:inset 0 0 0 2px var(--blue)}
/* ---- TRAFFIC LIGHT (#278) + CATEGORY CHIPS (#279) -------------------------
   Prefix `tl-`, verified unused across css/app.css, driver/driver.css and
   portal/portal.css (and across every .js and .html) before it was added.

   THE BACKGROUND IS NOT AVAILABLE, and that is the whole reason this block
   exists. Look four rules up: `.sc--mand` is already #e8433a (red) and
   `.sc--fatigue` is already #9ed06e (green), because the run's background
   encodes its TYPE. Painting status there would make a mandatory fatigue day
   read as an exception and a fatigue day read as a completed delivery.

   So status gets THREE channels of its own, none of them the background, and
   colour is never the only one — #278 asks for colour-blind-safe treatment:
     1. a 3px strip across the TOP of the run (`border-top`), declared
        transparent on every `.sc` so a run with no light is the same height as
        one with a light and nothing moves as the day progresses;
     2. a GLYPH on the roll-up pip and on every chip (· ▶ ✓ !), plus the phase's
        own letter code (PU / LH / DEL) which is drawn whatever the colour, so a
        chip's identity never depends on being able to see the fill;
     3. for RED ONLY, an `outline` ring around the run — "you see that yesterday
        there's still a red box". `outline` is used on `.sched__cell` (the td) for
        conflicts and drop-hover but nowhere on `.sc` itself, so it composes with
        `.sc--hired`'s and `.er-is-timed`'s inset box-shadows rather than
        replacing them. */
.sc{border-top:3px solid transparent}
.sc.tl-l--red{outline:2px solid #c62828;outline-offset:-2px}
/* The row itself, FIRST child of the run — "there's a little box at the top". */
.tl{display:flex;flex-wrap:wrap;align-items:center;gap:2px;margin:0 0 2px;line-height:1}
body.wall-sched .tl-c{font-size:10px}
body.wall-sched .tl-c i{font-size:10px}
body.wall-sched .tl-pip{width:15px;height:15px}
.sc[draggable=true]{cursor:grab}
.sc[draggable=true]:active{cursor:grabbing}
.sched__cell.drop-hover{outline:2px dashed var(--blue);outline-offset:-3px;background:#eef4ff!important}
.sk{font-size:11px;font-weight:800;padding:5px 10px;border-radius:6px}
.unsched{display:flex;gap:12px;flex-wrap:wrap;align-items:stretch}
.sk[data-newtype]{cursor:grab}
.sk[data-newtype]:active{cursor:grabbing}
/* ---------- Quotes ---------- */
.qtot{background:var(--panel-2);border:1px solid var(--line);border-radius:10px;padding:12px 14px;font-weight:800;display:flex;justify-content:space-between;align-items:center}
.qtot b{color:var(--blue);font-size:20px}
.qinfo{background:#e8effb;color:var(--blue);border-radius:10px;padding:11px 14px;font-weight:800;margin-bottom:12px}
.email{border:1px solid var(--line);border-radius:11px;padding:12px 14px;margin-bottom:10px;border-left:4px solid var(--muted-2)}
.email__h{display:flex;justify-content:space-between;font-size:13px;font-weight:800}
.email__h span{color:var(--muted-2);font-weight:700}
.email__s{font-weight:800;font-size:14px;margin:3px 0 4px}
.email__b{color:var(--muted);font-weight:600;font-size:13px;line-height:1.5;white-space:pre-wrap}
.reco{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 12px;border:1px solid var(--line);border-radius:9px;margin-bottom:7px;background:#fff}
.reco__d b{font-weight:800;font-size:13.5px;display:block}
.reco__d span{color:var(--muted);font-weight:700;font-size:12px}
.sev{display:inline-flex;align-items:center;gap:6px;font-weight:800;font-size:12px}
.sev::before{content:"";width:9px;height:9px;border-radius:50%;background:currentColor}
.table tr.is-ack{opacity:.5}
.table tr.is-ack .ev-type{font-weight:700}
.selects{display:flex;gap:10px;flex-wrap:wrap}
.selects select{padding:11px 14px;border:1px solid var(--line);border-radius:10px;font-family:var(--font);font-weight:700;font-size:13px;background:#fff}
/* ---------- Toast ---------- */
/* Hidden state has to clear `bottom` as well as the element's own height.
   translateY(120%) did not: an empty toast is 28px tall, so 120% is 34px, but it
   must travel 26px + 28px = 54px to get off screen — leaving a 20px black bar
   parked above the taskbar on every page. Percentages of a box whose height
   depends on its (variable) text can't be trusted here, so the offset is
   absolute and visibility/opacity make it unconditional. */
.toast{position:fixed;bottom:26px;left:50%;transform:translateX(-50%) translateY(calc(100% + 40px));opacity:0;visibility:hidden;background:var(--ink);color:#fff;padding:14px 22px;border-radius:12px;font-weight:800;box-shadow:0 10px 30px rgba(0,0,0,.3);transition:transform .35s,opacity .35s,visibility .35s;z-index:200}
.toast.is-show{transform:translateX(-50%) translateY(0);opacity:1;visibility:visible}
/* ---------- Wall-display mode ---------- */
body.wall .sidebar,body.wall .topbar{display:none}
body.wall .view{padding:22px}
/* wall display: drop interactive clutter, enlarge for distance reading */
body.wall .toolbar,body.wall .sched-hint,body.wall .sched-key,body.wall .depot-select,
body.wall .attach-add,body.wall .scfl-sub{display:none}
/* The live-fleet filter on a monitor: the selects and Clear are unreachable, so
   they go — but the count and the coverage caveat STAY, because a wall showing
   one depot's trucks is exactly where "this is not the whole fleet" needs saying,
   and the map heading names the slice. With no filter on, the bar has nothing
   left to say, so the whole card goes. */
body.wall .dfil__lbl,body.wall .dfil__sel,body.wall #df-clear{display:none}
body.wall .dfil:not(.is-on){display:none}
body.wall .dfil__count{margin-left:0;font-size:15px}
body.wall-sched .sched-unsched-card,body.wall-sched .sched-empties{display:none}
body.wall-sched .sched td,body.wall-sched .sched th{font-size:14px}
body.wall-sched .sched .lane-name{font-size:15px}
body.wall-sched .sc{font-size:13.5px}
body.wall-sched .sched-conflicts{font-size:15px}
body.wall-sched .scfl{cursor:default}
@media(max-width:560px){.kpis{grid-template-columns:1fr}}
/* ---- Visible keyboard focus ----------------------------------------------
   :focus-visible only shows the ring for keyboard users, so mouse and touch
   look unchanged. Without this, tabbing through a dialog gives no clue where
   you are — the browser default ring is invisible against the brand blues. */
a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible,
textarea:focus-visible,[tabindex]:focus-visible{
  outline:3px solid var(--yellow);outline-offset:2px;border-radius:6px}
.modal.is-open .modal__card:focus-visible,.dmodal.is-open .dmodal__card:focus-visible{outline:none}
/* Lost Revenue — dense spreadsheet-style grid with traffic-light idle bands.
   Row tint = how long idle (green <3d / amber 3-7d / red 7d+); the band stays on
   hover (a brightness nudge, not a flat overlay) so the severity never washes
   out. Service/rego/insurance cells carry their own status colour via .lr-chip. */
#lr-assets{border-collapse:collapse}
#lr-assets th,#lr-assets td{border:1px solid var(--line);padding:7px 10px;vertical-align:top}
#lr-assets tbody tr.lr-red{background:#fde4de}
#lr-assets tbody tr.lr-amber{background:#fdf1d3}
#lr-assets tbody tr.lr-green{background:#e7f5e9}
#lr-assets tbody tr:hover{filter:brightness(0.97)}
/* ---------- In-app assistant (#213) ----------------------------------------
   A chat column: intro card, a scrollable transcript of user/assistant bubbles,
   and a pinned composer. Bubbles reuse the brand palette (a dispatcher turn in
   blue, the assistant's answer on a light panel); grounding — which tools ran
   and which records they cited — sits under each answer so an answer is visibly
   backed by a lookup. Sized so it fits the dispatch floor and a phone. */
.asst{display:flex;flex-direction:column;gap:14px;max-width:900px;margin:0 auto}
@keyframes asstBounce{0%,80%,100%{transform:scale(.5);opacity:.5}40%{transform:scale(1);opacity:1}}
/* ==========================================================================
   COMPLIANCE REGISTER (LMS v2 #230). Own `cmp-` namespace — verified unused
   before adding. Deliberately NOT reusing any bare single-word class: `.reco`
   is a flex row, `.detail`/`.board`/`.kpis` are grids, and a panel that borrows
   one silently inherits its layout (that is exactly how the recommendations
   panel once shipped rendering sideways).
   ========================================================================== */
.cmp{display:flex;flex-direction:column;gap:14px}
.sched__cell.er-st-yard{background:#fffdf8}
details[open] > .er-pool__hd .er-pool__caret{transform:rotate(180deg)}
details[open] > .er-pool__hd .er-pool__caret{transform:rotate(180deg)}
.sched tr[hidden]{display:none}
.sc.er-is-timed{box-shadow:inset 3px 0 0 var(--blue)}
details[open] > .er-util__hd .er-util__caret{transform:rotate(180deg)}
#adb-rows tbody tr{cursor:pointer}
/* The version switch is chrome, not the document. `.adb-x` deliberately DOES
   print: it is the internal copy's own detail — "a version for us internally
   with all our stuff on it" — and it cannot leak, because the client copy is a
   separate sheet that never contains it rather than one with fields hidden. */
@media print{
  .adb-vsw,.adb-vsw__note,.adb-job{display:none!important}
}
/* ==========================================================================
   CONFLICT SURFACE — "somebody else was editing this too" (#269)
   ==========================================================================
   A banner, not a toast: a toast slides away while somebody is still reading
   which lane it meant. It never auto-hides, sits above everything (a dozen
   people work this board and a lost edit is the failure the app exists to
   replace), and is bounded in height so a burst of conflicts scrolls inside
   itself rather than covering the screen it is warning about. */
.clash{ position:fixed; z-index:1200; right:14px; bottom:14px; width:min(440px,calc(100vw - 28px));
  max-height:min(70vh,560px); overflow:auto; padding:13px 14px 12px;
  background:#fff; color:var(--ink); border:2px solid var(--orange); border-radius:12px;
  box-shadow:0 14px 40px rgba(20,30,60,.28) }
.clash__hd{ display:flex; align-items:center; gap:8px; font-size:14.5px }
.clash__ico{ font-size:17px; color:var(--orange) }
.clash__x{ margin-left:auto; border:0; background:transparent; cursor:pointer;
  font-size:22px; line-height:1; color:var(--muted); padding:0 4px }
.clash__p{ margin:8px 0 0; font-size:12.5px; font-weight:700; color:var(--muted); line-height:1.5 }
.clash__list{ list-style:none; margin:10px 0 0; padding:0 }
.clash__row{ font-size:12.5px; font-weight:700; padding:7px 9px; border-radius:8px;
  background:rgba(245,138,42,.10); margin-bottom:5px; line-height:1.45 }
.clash__row--more{ background:transparent; color:var(--muted); font-weight:800 }
.clash__who{ display:block; color:var(--muted-2); font-weight:700; font-size:11.5px }
.clash__act{ display:flex; gap:7px; margin-top:10px; flex-wrap:wrap }
body.wall .clash{ display:none }
.cref--req{border-color:var(--red);background:#fff6f2}
.cref--req .cref__row input{border-color:var(--red)}
.portal-link{display:flex;align-items:center;gap:11px;padding:9px 13px;margin-bottom:8px;border-radius:10px;background:rgba(245,138,42,.1);border:1px solid rgba(245,138,42,.28);color:#fff;font-weight:800;font-size:13px;transition:.15s}
.portal-link:hover{background:rgba(245,138,42,.2);border-color:rgba(245,138,42,.45);transform:translateY(-1px)}
@media(prefers-reduced-motion:reduce){.portal-link:hover{transform:none}}
.portal-link__ico{color:var(--orange);font-size:16px}
.gsearch-results{position:absolute;top:46px;left:0;right:0;background:#fff;border:1px solid var(--line);border-radius:12px;box-shadow:0 12px 40px rgba(34,31,32,.16);overflow:hidden;display:none;z-index:60;max-height:60vh;overflow-y:auto}
.gsearch-results.is-open{display:block}
/* inline validation */
.is-invalid{border-color:var(--red)!important;box-shadow:0 0 0 3px rgba(242,60,0,.15)!important}
.field-err{color:var(--red);font-weight:700;font-size:12px;margin-top:5px}
/* guided tour */
/* Above Leaflet's max (controls at 1000) for the same reason as .modal (#194) —
   a tour step highlighting a map screen must dim/point over the map, not under
   its controls — and above the modal (1100) so the existing tour-over-modal
   order is preserved when both clear the map together. */
.tour-hi{position:fixed;z-index:1200;border-radius:10px;box-shadow:0 0 0 9999px rgba(20,18,19,.62);pointer-events:none;transition:.2s}
.tour-tip{position:fixed;z-index:1201;background:#fff;border-radius:14px;box-shadow:0 20px 60px rgba(0,0,0,.4);padding:16px 18px;width:300px;max-width:92vw}
.tour-txt{font-weight:700;font-size:14px;line-height:1.5;color:var(--ink)}
.tour-nav{display:flex;align-items:center;justify-content:space-between;margin-top:14px}
.tour-nav>span{color:var(--muted-2);font-weight:800;font-size:12px}
.tour-nav>div{display:flex;gap:6px}
.btn--primary{background:var(--blue);color:#fff}
.btn--primary:hover{background:var(--blue-d)}
.btn--orange{background:var(--orange);color:#fff}
.btn--orange:hover{filter:brightness(.95)}
.btn--ghost{background:#eef1f6;color:var(--ink)}
.btn--ghost:hover{background:#e2e7ef}
.btn--sm{padding:8px 14px;min-height:36px;font-size:13px;border-radius:8px}
.section-title{font-size:13px;text-transform:uppercase;letter-spacing:1.2px;color:var(--muted);font-weight:800;margin:0 0 12px}
.badge--blue{background:#e8effb;color:var(--blue)}
.badge--green{background:#e4f6ec;color:var(--ok)}
.badge--orange{background:#fdefe0;color:#c96a12}
.badge--red{background:#fde6df;color:var(--red)}
.badge--grey{background:#eef1f6;color:var(--muted)}
.badge--arriving{background:#fdefe0;color:#c96a12}
.badge--arrived{background:#e4f6ec;color:var(--ok)}
/* A KPI that is a link (Speed alerts → #/speeding). An anchor so keyboard, Tab
   and open-in-new-tab work, same reasoning as the nav and the fleet-status rows. */
.kpi--link{color:inherit;text-decoration:none;transition:.15s;display:block}
.kpi--link:hover{transform:translateY(-1px);box-shadow:0 2px 4px rgba(34,31,32,.08),0 8px 28px rgba(34,31,32,.1)}
/* On the label, not the card: as a card-level ::after it landed on its own line
   under the sub-text and read as a stray character. */
.kpi--link .kpi__label::after{content:" ›";color:var(--muted);font-weight:900}
@media(prefers-reduced-motion:reduce){.kpi--link:hover{transform:none}}
/* Caption over a chart. Two speed charts sit together on the asset page and
   without a label on each the second reads as a continuation of the first. */
.chart-cap{font-size:11px;font-weight:900;text-transform:uppercase;letter-spacing:.7px;
  color:var(--muted);margin:12px 0 2px}
.chart-cap:first-child{margin-top:0}
/* Which limit a speed alert was measured against. Two visually distinct chips
   because they are two different claims: `signed` is indicative of a road
   offence (and rests on a dataset Main Roads do not warrant), `policy` is a
   breach of the limit we set for that asset — certain, but ours not the law's. */
.spd-src{display:inline-block;font-size:10px;font-weight:900;text-transform:uppercase;letter-spacing:.4px;
  padding:1px 5px;border-radius:4px;background:rgba(37,82,164,.1);color:var(--blue);vertical-align:1px}
.spd-src--own{background:rgba(107,114,128,.12);color:var(--muted)}
/* Above WA's statewide maximum: an offence on every road in the state, so this
   one is true whatever the zone data says. Deliberately the loudest of the three. */
.spd-cert{display:inline-block;font-size:10px;font-weight:900;text-transform:uppercase;letter-spacing:.4px;
  padding:1px 5px;border-radius:4px;background:rgba(242,60,0,.12);color:var(--red)}
.dash-row{display:grid;grid-template-columns:1.4fr 1fr;gap:18px;margin-top:18px;align-items:start}
.mini-map{height:340px;border-radius:var(--radius);overflow:hidden}
/* ---- "Map unavailable" (issue #288) ---------------------------------------
   Leaflet comes from unpkg, so every map screen has to be able to say the
   library is missing and still show what the map was the only place to read.
   .map-off-box is added to the map's OWN container: those containers carry a
   fixed map height (340px here, 440px on the geofence editor, 170px on a modal
   mini-map), and a 170px box would clip the panel. Each selector below is one
   level more specific than the height rule it overrides, so no !important. */
.mini-map.map-off-box,#map.map-off-box,#dash-map.map-off-box,.ev-mini-map.map-off-box,
div.map-off-box{height:auto;min-height:0;overflow:visible;border:none}
.list-panel{padding:18px}
.truck-icon{background:none;border:none}
.truck-icon div{width:30px;height:30px;border-radius:50% 50% 50% 0;transform:rotate(-45deg);display:grid;place-items:center;box-shadow:0 2px 6px rgba(0,0,0,.4);border:2px solid #fff}
.truck-icon span{transform:rotate(45deg);font-size:14px}
.job-assign{margin-top:11px;width:100%;padding:9px 10px;border:1px solid var(--blue);border-radius:8px;font-family:var(--font);font-weight:800;font-size:13px;color:var(--blue);background:#f2f6fd;cursor:pointer}
.job-assign:hover{background:#e8effb}
.filter-tabs{display:flex;gap:6px;background:#eef1f6;padding:5px;border-radius:12px}
.filter-tabs button{border:none;background:none;padding:9px 16px;border-radius:8px;font-weight:800;font-size:13px;color:var(--muted);cursor:pointer;font-family:var(--font)}
.filter-tabs button.is-active{background:#fff;color:var(--blue);box-shadow:0 1px 2px rgba(0,0,0,.08)}
.items-tbl{width:100%;border-collapse:collapse;margin-top:8px}
.items-tbl th,.items-tbl td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--line);font-weight:700;font-size:14px}
.items-tbl th{font-size:12px;text-transform:uppercase;color:var(--muted);letter-spacing:.5px}
.items-tbl select{padding:8px 8px;border:1px solid var(--line);border-radius:8px;font-family:var(--font);font-weight:600;font-size:14px;background:#fff}
/* New consignment form */
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:0 18px}
@media(max-width:640px){.form-grid{grid-template-columns:1fr}}
/* ---------- Richer POD ---------- */
.pod-photo{margin-top:16px}
.pod-photo__label{display:block;font-weight:800;font-size:13px;margin-bottom:8px}
.pod-photo__thumb{display:block;margin-top:10px;max-height:130px;border-radius:10px;border:1px solid var(--line)}
.pod-thumbs{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
.pod-thumbs .pt{position:relative}
.pod-thumbs img{height:84px;border-radius:10px;border:1px solid var(--line);display:block}
.pod-thumbs .pt-x{position:absolute;top:-7px;right:-7px;width:24px;height:24px;border-radius:50%;border:none;background:var(--red);color:#fff;font-weight:800;font-size:12px;cursor:pointer}
.geo-stamp{margin-top:16px;font-size:12px;font-weight:700;color:var(--muted);background:var(--panel-2);border:1px solid var(--line);border-radius:8px;padding:8px 12px}
.geo-stamp.is-ok{color:var(--ok)}
/* Every coordinate the dispatcher sees is a link to Google Maps (geoLink() in
   js/app.js). It inherits its colour from whatever line it sits on — these
   appear on a green POD stamp, a muted meta line and inside a table cell — so
   the only thing the style does is mark it as a link on hover/focus rather than
   recolour it. Dotted underline at rest so it reads as clickable without
   shouting; the driver portal deliberately has no equivalent. */
.geo-link{color:inherit;text-decoration:none;border-bottom:1px dotted currentColor}
.geo-link:hover,.geo-link:focus-visible{text-decoration:none;border-bottom-style:solid}
.geo-link:focus-visible{outline:2px solid var(--blue);outline-offset:2px;border-radius:2px}
.alert-row{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:10px;background:var(--panel-2);border:1px solid var(--line);font-weight:700;font-size:13px}
.alert-row .adot{width:9px;height:9px;border-radius:50%;flex-shrink:0}
.alert-row time{margin-left:auto;color:var(--muted-2);font-size:12px}
/* ---------- Reports ---------- */
.rep-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-bottom:18px}
.rep-2{display:grid;grid-template-columns:1fr 1fr;gap:18px}
.donut-wrap{display:flex;align-items:center;gap:20px;padding:22px}
.donut-num{font-size:40px;font-weight:900;color:var(--ok)}
.fstep--done .fstep__n{background:var(--ok);color:#fff}
.fstep--done::before{background:var(--ok)}
.fstep--active{background:#fff6ef;border:2px solid var(--orange);border-radius:14px;padding:12px 10px;margin:6px -4px}
.fstep--active .fstep__n{background:var(--orange);color:#fff}
.fstep--todo{opacity:.45}
@media(max-width:640px){
  .fstep--active{flex-wrap:wrap}
  .fstep--active .fstep__c{width:100%;margin-top:8px}
  .fstep--active .flow-btn{width:100%}
}
.flow-btn{min-height:44px;padding:10px 16px;font-size:14px;white-space:nowrap}
.tel--ico{display:inline-grid;place-items:center;width:24px;height:24px;border-radius:6px;background:#e8effb;font-size:12px;margin-left:6px;vertical-align:middle}
.tel--ico:hover{background:#d5e2f8;text-decoration:none}
/* ---------- Process flow chart (quotes & jobs) ---------- */
.proc-card{padding:20px 24px;margin-bottom:18px}
.asg-chip{border:1px solid var(--line);background:#f7f9fc;border-radius:7px;padding:3px 7px;font-size:10.5px;font-weight:800;color:var(--ink);cursor:pointer;line-height:1.3;white-space:nowrap}
.asg-chip:hover{background:#e8effb;border-color:var(--blue);color:var(--blue)}
.asg-chip.is-bad{background:#fde6df;border-color:#f6c7b8;color:var(--red)}
.sum-row b{font-size:15px;color:var(--blue)}
.sum-list{color:var(--muted);font-weight:700;font-size:10px;margin-top:2px;line-height:1.3}
.sum-maint{color:var(--red);font-weight:800;font-size:10px;margin-top:2px}
.util-bar{height:8px;border-radius:5px;background:#e4e9f2;overflow:hidden;margin-bottom:3px}
.util-fill{height:100%;border-radius:5px}
/* demurrage */
.demurrage-card{margin-top:20px;border-left:4px solid var(--orange)}
/* relay / multi-leg chain (consignment detail) */
.legs-card{margin-top:20px;border-left:4px solid var(--blue)}
.legs-sub{font-size:12.5px;font-weight:700;color:var(--muted);margin-bottom:12px}
.legs-issues{background:#fff6ef;border:1px solid #f6d3ab;border-radius:10px;padding:9px 12px;margin-bottom:12px}
.legs-issues.is-crit{background:#fde6df;border-color:#f3b6a3}
.legs-issue{display:flex;gap:8px;font-size:13px;font-weight:700;color:#7a4a12;line-height:1.35}
.legs-issues.is-crit .legs-issue{color:#a12c00}
.dem-row{display:flex;justify-content:space-between;align-items:baseline;gap:12px;font-weight:700;font-size:14px}
.dem-amt{font-weight:900;color:#c96a12;white-space:nowrap}
.dem-note{font-size:12px;font-weight:700;color:var(--muted);margin:2px 0 10px}
/* fleet documents & compliance */
.dd-pill{font-size:11px;font-weight:900;padding:2px 8px;border-radius:999px;white-space:nowrap}
.dd--ok{background:#e3f5ea;color:var(--ok)}
.dd--soon{background:#fdecd8;color:#c96a12}
.dd--exp{background:#fde6df;color:var(--red)}
.fleet-swms{display:flex;flex-wrap:wrap;gap:8px}
.fleet-swms__item{background:#f5f8fd;border:1px solid var(--line);border-radius:9px;padding:8px 12px;font-weight:800;font-size:13px}
.fleet-swms__item small{color:var(--muted-2);font-weight:700;margin-left:4px}
/* route / run reference material (#171) — reuses .attach-list / .attach-row */
.ref-card{margin-top:20px}
.ref-add{display:flex;flex-direction:column;gap:8px}
.ref-add__row{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.ref-add__row input,.ref-add__row select{padding:8px 10px;border:1px solid var(--line);border-radius:8px;font-family:inherit;font-weight:600;font-size:13px}
.ref-add__row input{flex:1;min-width:180px}
.ref-status{font-weight:700;font-size:12.5px;color:var(--muted)}
/* customer tracking link */
.track-card{margin-top:20px}
.track-link{width:100%;padding:9px 11px;border:1px solid var(--line);border-radius:8px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12.5px;background:#f7f9fc;color:var(--ink)}
.track-acts{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
/* zone pricing */
.reco-zone{background:#eef4ff;border:1px solid #d6e2fb;border-radius:9px;padding:8px 11px;margin-bottom:10px;font-weight:700;font-size:12.5px}
.reco-zone__acc{color:var(--muted)}
.reco-zone__permit{background:#fdecd8;color:#c96a12;font-weight:900;font-size:11px;padding:1px 7px;border-radius:999px}
/* ---------- Recommended charges ---------- */
.reco-hint{color:var(--muted);font-weight:700;font-size:13px;padding:10px 0}
.reco-banner{background:#e8effb;color:var(--blue);border-radius:9px;padding:9px 12px;font-weight:700;font-size:13px;margin-bottom:10px}
.reco--levy{background:#fdefe0;border-color:#f6d3ab}
.empty-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 0;border-bottom:1px solid var(--line)}
.empty-row:last-child{border-bottom:none}
.lane-classes{display:flex;gap:4px;flex-wrap:wrap;margin-bottom:5px}
.lane-classes span{background:#eef1f6;color:var(--muted);font-size:10px;font-weight:800;padding:2px 6px;border-radius:5px}
.lane-classes .lane-haz{background:#221F20;color:#fff}
.lane-name{font-weight:900;font-size:14px}
.lane-veh{color:var(--muted);font-weight:700;font-size:12px}
.lane-stream{color:var(--blue);font-weight:800;font-size:11px;margin-top:3px}
.qtot-lines{margin-top:10px;max-width:340px;margin-left:auto}
.qtot-lines>div{display:flex;justify-content:space-between;padding:7px 0;font-weight:700;border-bottom:1px solid var(--line)}
.qtot-total{border-bottom:none!important;font-weight:900;font-size:16px}
.qtot-total .mono{color:var(--blue);font-size:18px}
.qinfo--ok{background:#e4f6ec;color:var(--ok)}
.qinfo--bad{background:#fde6df;color:var(--red)}
.email--in{border-left-color:var(--blue);background:var(--panel-2)}
.email--out{border-left-color:var(--orange)}
.sev--critical{color:var(--red)}
.sev--warning{color:#c96a12}
.sev--info{color:var(--muted)}
.toast--ok{background:var(--ok)}
/* L&D queue — the five-state filter row and the status table */
.chk-bar{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:14px}
.chk-chips{display:flex;gap:8px;flex-wrap:wrap}
.chk-chip{border:1px solid var(--line);background:#fff;border-radius:20px;padding:7px 13px;font-family:var(--font);font-weight:800;font-size:12.5px;color:var(--muted);cursor:pointer;display:flex;align-items:center;gap:7px;transition:.15s}
.chk-chip:hover{border-color:var(--blue);color:var(--blue)}
.chk-chip.is-active{background:var(--blue);border-color:var(--blue);color:#fff}
.chk-chip span{background:rgba(0,0,0,.08);border-radius:10px;padding:0 7px;font-size:11.5px}
.chk-chip.is-active span{background:rgba(255,255,255,.25)}
.ldq-row--nonconformance{background:#fdf4f1}
.ldq-row--review{background:#f5f8ff}
.lr-chip{font-weight:800;white-space:nowrap}
.lr-chip--red{color:var(--danger)}
.lr-chip--amber{color:#c96a12}
.lr-chip--ok{color:var(--ok)}
.lr-chip--na{color:var(--muted);font-weight:700}
/* A `.fld` sitting on a toolbar rather than in a form grid: it keeps the label
   and the control but stops claiming a whole row. There was no inline variant of
   `.fld`, and the toolbar ones were stacking full-width without it. */
.fld--inline{display:inline-block;margin-bottom:0;min-width:150px}
.fld--inline span{font-size:12px;margin-bottom:4px;color:var(--muted)}
.fld--inline input,.fld--inline select{padding:9px 11px;font-size:14px}
