/* ==========================================================================
   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;text-transform:uppercase;color:#8f97a6;margin-top:3px}
/* Secondary tag under the app name — smaller and dimmer, so it reads as a
   subtitle to "Transport Manager" 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--req{border-color:var(--red);background:#fff6f2}
.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--req .cref__row input{border-color:var(--red)}
.cref__note{color:var(--red);font-weight:700;font-size:12px;margin-top:6px}
.cn-custref--req .cn-in{border:1px solid var(--red);background:#fff6f2}
.cn-warn{background:#fff6f2;border:1px solid var(--red);color:#8a2b12;font-weight:700;font-size:13px;border-radius:10px;padding:10px 14px;margin:0 0 12px;line-height:1.5}
@media print{.cn-warn{display:none}}
.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)}
.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}

/* ---------- 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)}
.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}
.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}}

/* 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}
.req{color:var(--red);font-weight:900}

/* 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}

.topbar__right{display:flex;align-items:center;gap:22px}
.clock{font-weight:800;color:var(--blue);font-variant-numeric:tabular-nums}
.user-wrap{position:relative}
.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}
.user-menu{position:absolute;right:0;top:50px;background:#fff;border:1px solid var(--line);border-radius:12px;box-shadow:0 12px 40px rgba(34,31,32,.18);min-width:210px;z-index:60;display:none;overflow:hidden}
.user-menu.is-open{display:block}
.user-menu__hd{padding:14px 16px;border-bottom:1px solid var(--line)}
.user-menu__hd b{display:block;font-weight:800}
.user-menu__hd span{color:var(--muted);font-weight:700;font-size:13px}
.user-menu__item{width:100%;text-align:left;background:none;border:none;padding:13px 16px;font-family:var(--font);font-weight:800;font-size:14px;color:var(--ink);cursor:pointer}
.user-menu__item:hover{background:var(--panel-2);color:var(--red)}
.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}
.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}

.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
.section-title{font-size:13px;text-transform:uppercase;letter-spacing:1.2px;color:var(--muted);font-weight:800;margin:0 0 12px}

.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}
.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)}

/* ---------- 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}
/* 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}}
.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}}

/* 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)}
/* 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}

/* ---------- Asset detail page (issue #111) ----------
   A summary strip answers the top questions once, then a two-column grid on wide
   screens (collapsing to one on mobile) splits the textual detail from the map
   and activity. Tabs group the left column (Overview / Service / Commercial) so
   reaching Service or Jobs no longer means scrolling past Identity, Odoo and two
   maps. The Leaflet trail stays in the always-visible right column — never inside
   a hidden tab — so it needs no invalidateSize handling and the trail-drawing
   code (shared with #112) is untouched. */
.asset-hd{display:flex;justify-content:space-between;align-items:flex-start;gap:14px;flex-wrap:wrap}
.asset-sum{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;margin-top:18px}
.asset-sum__item{background:var(--panel-2);border:1px solid var(--line);border-radius:var(--radius-sm);padding:12px 14px;min-width:0}
.asset-sum__k{font-size:11px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted);font-weight:800}
.asset-sum__v{font-weight:900;font-size:20px;line-height:1.15;margin-top:4px;overflow-wrap:anywhere}
.asset-sum__sub{font-size:12px;color:var(--muted);font-weight:700;margin-top:3px;overflow-wrap:anywhere}
.asset-sum__sub .geo-link{font-weight:700}

.asset-cols{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.12fr);gap:18px;align-items:start;margin-top:18px}
.asset-col{min-width:0}
@media(max-width:960px){.asset-cols{grid-template-columns:1fr}}

/* 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%}}
.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}
.map-off{padding:16px 18px;border-radius:var(--radius);background:#fff8f2;
  border:1px solid #f6d5bd;border-left:4px solid var(--orange)}
.map-off__t{margin:0 0 7px;font-weight:900;font-size:12px;letter-spacing:.05em;
  text-transform:uppercase;color:#a2560f}
.map-off__p{margin:0 0 10px;color:var(--muted);font-weight:700;font-size:13px;line-height:1.6}
.map-off__p:last-of-type{margin-bottom:12px}
.map-off__p--sm{font-size:12px;color:var(--muted-2)}
.map-off__p a{color:var(--blue)}
.map-off__go{background:#fff}
/* 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}
.list-panel{padding:18px}
.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 screen ---------- */
.map-wrap{display:grid;grid-template-columns:300px 1fr;gap:18px;height:calc(100vh - 66px - 52px)}
#map{width:100%;height:100%}
#dash-map{width:100%;height:340px}
.map-panel{height:100%}
.map-side{overflow:auto;padding:6px}
/* 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}
.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}

/* ---------- 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}
.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}

/* ---------- 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)}
.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)}

/* 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}
.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}
.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)}
.sig-preview{border:1px dashed var(--line);border-radius:12px;padding:14px;text-align:center;margin-top:14px}
.sig-preview img{max-width:100%;height:auto}

/* ---------- 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)}
.sig-meta{background:var(--panel-2);border:1px solid var(--line);border-radius:10px;padding:12px 14px;font-weight:700;font-size:14px;margin-bottom:16px}
.sig-meta b{color:var(--blue)}
.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)}

/* New consignment form */
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:0 18px}
#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}
@media(max-width:640px){.form-grid{grid-template-columns:1fr}}
.sig-pad-wrap{position:relative;border:2px solid var(--blue);border-radius:12px;background:#fff;overflow:hidden}
.sig-pad-label{position:absolute;top:8px;left:12px;font-size:12px;color:var(--muted-2);font-weight:800;text-transform:uppercase;letter-spacing:.5px;pointer-events:none}
.sig-pad{display:block;width:100%;height:200px;touch-action:none;cursor:crosshair}
.sig-baseline{position:absolute;left:24px;right:24px;bottom:34px;border-bottom:2px dashed #d3d9e3;pointer-events:none}
.chk{display:flex;align-items:center;gap:10px;margin-top:16px;font-weight:700;font-size:14px}
.chk input{width:20px;height:20px}

/* ---------- 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)}
.modal__card.is-fail .modal__head h2{color:var(--red)}
#sig-mode.is-fail-active{background:#fde6df;color:var(--red)}

/* ---------- Driver Run Sheet ---------- */
.run-head{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:20px}
.run-driver{display:flex;align-items:center;gap:12px}
.run-driver__av{width:46px;height:46px;border-radius:50%;background:var(--blue);color:#fff;display:grid;place-items:center;font-weight:900;font-size:16px}
.run-driver__name{font-weight:900;font-size:18px}
.run-driver__sub{color:var(--muted);font-weight:700;font-size:13px}
.run-picker{margin-left:auto}
.run-picker select{padding:11px 14px;border:1px solid var(--line);border-radius:10px;font-family:var(--font);font-weight:700;background:#fff}
.run-summary{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:18px}
.run-chip{background:#fff;border:1px solid var(--line);border-radius:12px;padding:10px 16px;font-weight:800}
.run-chip b{font-size:20px;color:var(--blue);display:block}
.run-list{display:flex;flex-direction:column;gap:14px;max-width:820px}
.run-card{background:#fff;border:1px solid var(--line);border-radius:16px;box-shadow:var(--shadow);padding:18px;display:flex;gap:16px;align-items:flex-start}
.run-card.is-done{opacity:.62}
.run-seq{width:38px;height:38px;border-radius:10px;background:var(--ink);color:#fff;display:grid;place-items:center;font-weight:900;flex-shrink:0}
.run-card.is-next .run-seq{background:var(--orange)}
.run-b{flex:1;min-width:0}
.run-route{font-weight:900;font-size:17px}
.run-meta{color:var(--muted);font-weight:700;font-size:13px;margin-top:4px;display:flex;flex-wrap:wrap;gap:4px 14px}
.run-actions{display:flex;flex-direction:column;gap:8px;align-items:stretch;min-width:150px}
.run-actions .btn{width:100%}
@media(max-width:640px){.run-card{flex-direction:column}.run-actions{flex-direction:row;min-width:0}}

/* ---------- Alerts panel ---------- */
.alerts{display:flex;flex-direction:column;gap:8px;max-height:280px;overflow:auto}
.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}
.badge--arriving{background:#fdefe0;color:#c96a12}
.badge--arrived{background:#e4f6ec;color:var(--ok)}

/* ---------- 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}
/* Fleet Composition (#/composition) sections flow as a masonry (CSS
   multi-column) so a short card (e.g. By depot, ~6 rows) sitting beside a tall
   one (By type, ~10 rows) can't hold a fixed two-column row open and leave a big
   vertical dead-space gap before the next section — see #130. Whole cards pack
   top-to-bottom then into the next column; break-inside:avoid keeps each intact
   and each is sized to its own content. Its own class on purpose — .rep-2
   (Reports) and .comp-grid (the compliance snapshot) are used by other screens
   and must not change. Collapses to one column on a phone at the shared 1100px
   breakpoint below. */
.comp-flow{columns:2;column-gap:18px}
.comp-flow>*{break-inside:avoid;margin:0 0 18px}
.donut-wrap{display:flex;align-items:center;gap:20px;padding:22px}
.donut-num{font-size:40px;font-weight:900;color:var(--ok)}
.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}
@media(max-width:1100px){.rep-grid{grid-template-columns:1fr}.rep-2{grid-template-columns:1fr}.comp-flow{columns:1}}

/* ---------- Help / glossary (collapsible) ---------- */
.help-group{padding:0;overflow:hidden}
.help-group summary,.help-item summary{list-style:none;cursor:pointer}
.help-group summary::-webkit-details-marker,.help-item summary::-webkit-details-marker{display:none}
.help-head{display:flex;justify-content:space-between;align-items:center;padding:16px 22px;font-weight:800;font-size:16px}
.help-head:hover{background:var(--panel-2)}
.help-head__r{display:flex;align-items:center;gap:10px}
.help-count{background:#eef1f6;color:var(--muted);border-radius:999px;padding:2px 10px;font-size:12px;font-weight:800}
.help-caret{transition:.2s;color:var(--muted-2);font-size:13px}
.help-group[open]>.help-head .help-caret{transform:rotate(90deg)}
.help-panel{padding:0 22px 8px}
.help-item{border-top:1px solid var(--line)}
.help-term{display:flex;justify-content:space-between;align-items:center;padding:13px 0;font-weight:800;font-size:14.5px}
.help-term:hover{color:var(--blue)}
.help-term::after{content:"+";color:var(--muted-2);font-weight:900;font-size:16px;margin-left:12px}
.help-item[open]>.help-term::after{content:"–"}
.help-def{color:var(--muted);font-weight:600;line-height:1.6;font-size:14px;padding:0 0 14px}
.help-def b{color:var(--ink)}

/* ---------- Consignment Note (connote) ---------- */
.cn-toolbar{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}
.cn-sheet{background:#fff;color:#111;max-width:840px;margin:0 auto;border:1px solid #333;font-size:12px;line-height:1.3}
.cn-sheet input.cn-in{border:none;border-bottom:1px solid #999;font-family:var(--font);font-size:12px;font-weight:700;padding:1px 3px;min-width:60px;background:transparent;color:#111}
.cn-sheet input.cn-in:focus{outline:none;border-bottom-color:var(--blue);background:#f4f8ff}
.cn-head{display:grid;grid-template-columns:1.5fr 2fr 1.4fr;border-bottom:2px solid #111}
.cn-brand{padding:8px;border-right:1px solid #333}
.cn-logo{width:54px;display:block}
.cn-word{font-weight:900;font-size:18px;line-height:1;margin:2px 0}
.cn-word b{color:#221F20} .cn-word i{color:var(--orange);font-style:normal}
.cn-co{font-size:10px;font-weight:600;color:#333;margin-top:4px}
.cn-depots{display:grid;grid-template-columns:1fr 1fr;gap:2px 10px;padding:8px;border-right:1px solid #333;font-size:9.5px;line-height:1.25}
.cn-idbox{padding:8px;text-align:right}
.cn-barcode{text-align:right}
.cn-cno{font-weight:900;font-size:18px;letter-spacing:1px}
.cn-booking{font-size:11px;font-weight:700}
.cn-order{background:#111;color:#fff;padding:3px 6px;border-radius:4px;margin:4px 0;display:flex;justify-content:space-between;align-items:center;gap:6px}
.cn-order span{font-size:10px} .cn-order b{color:#F58A2A}
.cn-order input.cn-in{color:#F58A2A;border-bottom-color:#666}
.cn-acct{font-size:10px;font-weight:700}
.cn-row2{display:grid;grid-template-columns:1fr 1fr;border-bottom:1px solid #333}
.cn-dates{padding:8px;border-right:1px solid #333;display:flex;flex-direction:column;gap:10px;font-weight:700}
.cn-freightacct{padding:8px;position:relative;display:flex;flex-direction:column;gap:4px}
.cn-tag{display:inline-block;background:#111;color:#fff;font-size:9px;font-weight:800;padding:2px 8px;border-radius:3px;margin-bottom:3px;align-self:flex-start}
.cn-party{display:grid;grid-template-columns:26px 1fr 150px;border-bottom:1px solid #333}
.cn-side{background:#eee;writing-mode:vertical-rl;transform:rotate(180deg);text-align:center;font-weight:800;font-size:10px;letter-spacing:1px;padding:4px}
.cn-partybody{padding:7px;display:flex;flex-direction:column;gap:5px;border-right:1px solid #333;border-left:1px solid #333}
.cn-partybody div{display:flex;align-items:baseline;gap:4px}
.cn-partybody .cn-in{flex:1}
.cn-state{background:#e4e9f2;font-weight:800;font-size:10px;padding:1px 6px;border-radius:3px}
.cn-times{padding:7px;display:flex;flex-direction:column;gap:12px;font-weight:800}
.cn-freight{width:100%;border-collapse:collapse;border-bottom:1px solid #333}
.cn-freight th{background:#111;color:#fff;font-size:10px;padding:4px 6px;text-align:left}
.cn-freight th:nth-child(3),.cn-freight th:nth-child(4),.cn-freight th:nth-child(5){text-align:center;width:80px}
.cn-freight td{border:1px solid #ccc;padding:5px 6px;height:20px;font-weight:600}
.cn-freight td:nth-child(3),.cn-freight td:nth-child(4),.cn-freight td:nth-child(5){text-align:center}
.cn-charge{background:#fff6ef;font-weight:800;color:#111;text-align:right!important}
.cn-special{border-bottom:1px solid #333;padding:7px;min-height:56px}
.cn-specialbody{font-weight:700;padding-top:4px}
.cn-sign{display:grid;grid-template-columns:1fr 1fr 0.7fr 0.7fr;border-bottom:1px solid #333}
.cn-sign:nth-of-type(odd){}
.cn-sigbox{padding:6px 8px;border-right:1px solid #333;min-height:56px;display:flex;flex-direction:column}
.cn-sigbox:last-child{border-right:none}
.cn-sigbox span{font-size:9px;font-weight:800;text-transform:uppercase;color:#333}
.cn-sigline{flex:1;border-bottom:1px solid #111;margin:14px 0 2px;font-weight:700}
.cn-sigbox small{font-size:8px;color:#666}
.cn-foot{padding:8px;font-size:9.5px;line-height:1.35;color:#333}
.cn-foot-bold{font-weight:800;color:#111;margin:3px 0}
@media print{
  .sidebar,.topbar,.cn-toolbar,.ptoast,.toast{display:none!important}
  .main,.view,#app{display:block!important;padding:0!important;margin:0!important}
  .cn-sheet{border:none;max-width:100%}
  @page{size:A4 portrait;margin:10mm}
}

/* ---------- Portal QR codes ---------- */
.qr-row{display:flex;gap:24px;flex-wrap:wrap}
.qr-item{display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px;border:1px solid var(--line);border-radius:14px;background:var(--panel-2)}
.qr-box{width:160px;height:160px;display:grid;place-items:center;background:#fff;border-radius:8px}
.qr-item b{font-weight:800}
.qr-item small{color:var(--muted);font-weight:700;font-size:12px}
/* The codes are computed in js/app.js rather than fetched from a CDN (issue
   #265). One <rect> per dark module inside a viewBox measured in modules, so the
   same SVG serves the 104px login card and the 160px Settings box. */
.qr-svg{display:block;width:100%;height:100%}
/* A code that could NOT be generated says so and still shows the URL — the CDN
   failure this replaced left a blank white square and no explanation. */
.qr-off{background:var(--panel-2);border:1px dashed var(--line);padding:6px;align-content:center;gap:4px;text-align:center}
.qr-off__t{font-weight:800;font-size:10.5px;color:var(--red);line-height:1.25}
.qr-off__u{font-size:9px;font-weight:700;color:var(--muted);word-break:break-all;user-select:all;line-height:1.3}

/* ---------- 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;text-transform:uppercase;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}
/* 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}
.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}
.flow-btn{min-height:44px;padding:10px 16px;font-size:14px;white-space:nowrap}
@media(max-width:640px){
  .fstep--active{flex-wrap:wrap}
  .fstep--active .fstep__c{width:100%;margin-top:8px}
  .fstep--active .flow-btn{width:100%}
}

/* ---------- Click-to-call ---------- */
.tel{color:var(--blue);font-weight:800;text-decoration:none;white-space:nowrap}
.tel:hover{text-decoration:underline}
.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}
.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)}

/* ---------- Schedule depot filter bar ---------- */
.depot-bar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;background:#fff;border:1px solid var(--line);border-radius:12px;padding:12px 16px;margin-bottom:18px;box-shadow:var(--shadow)}
.depot-bar__label{font-weight:900;font-size:14px;color:var(--ink)}
.depot-tabs{display:flex;gap:6px;flex-wrap:wrap}
.depot-tab{border:1px solid var(--line);background:#f4f6fa;color:var(--muted);font-family:var(--font);font-weight:800;font-size:13px;padding:8px 15px;border-radius:9px;cursor:pointer;transition:.12s}
.depot-tab:hover{background:#e8effb;color:var(--blue)}
.depot-tab.is-active{background:var(--blue);color:#fff;border-color:var(--blue)}
.depot-select{display:none}
.depot-bar__count{margin-left:auto;color:var(--muted);font-weight:700;font-size:13px}
@media(max-width:720px){
  .depot-tabs{display:none}
  .depot-select{display:block;padding:9px 12px;border:1px solid var(--line);border-radius:9px;font-family:var(--font);font-weight:700}
  .depot-bar__count{margin-left:0;width:100%}
}

/* ---- Per-depot page (#/depot/<id>, issue #99) ---- */
.depot-head{display:flex;align-items:center;gap:16px;flex-wrap:wrap;padding:18px 20px}
.depot-head__id{display:flex;align-items:center;gap:13px;min-width:0}
.depot-dot{width:14px;height:14px;border-radius:4px;flex-shrink:0;box-shadow:0 0 0 3px rgba(34,31,32,.06)}
.depot-code{font-size:12px;font-weight:800;color:var(--muted-2);letter-spacing:.6px;vertical-align:2px}
.depot-head__meta{color:var(--muted);font-weight:700;font-size:13px;margin-top:3px}
.depot-head__geo{color:var(--blue)}
.depot-head__stat{margin-left:auto;text-align:right;line-height:1.1}
.depot-head__stat b{font-size:26px;font-weight:900;color:var(--blue)}
.depot-head__stat small{display:block;color:var(--muted);font-weight:700;font-size:12px}
.depot-cams{padding:18px 20px}
.depot-cams__hd{display:flex;align-items:center;gap:12px;margin-bottom:14px}
.depot-cams__tag{margin-left:auto;font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.4px;padding:3px 10px;border-radius:20px;background:#fdf0e4;color:var(--orange)}
.depot-cam-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:14px}
.depot-cam{margin:0}
.depot-cam__frame{position:relative;aspect-ratio:16/9;border-radius:10px;overflow:hidden;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;
  background:repeating-linear-gradient(45deg,#20242c,#20242c 12px,#242832 12px,#242832 24px);color:#98a2b3;border:1px solid #2b303a}
.depot-cam__ico{font-size:30px;opacity:.5;filter:grayscale(1)}
.depot-cam__none{font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:.6px;color:#8b93a3}
.depot-cam__cap{margin-top:8px;font-size:13px;font-weight:700;color:var(--muted)}
.depot-cam__cap b{color:var(--ink)}
.depot-cams__note{margin:16px 0 0;color:var(--muted);font-weight:600;font-size:12.5px;line-height:1.6;border-top:1px solid var(--line);padding-top:12px}
.depot-approx{margin:-2px 0 12px;color:var(--muted);font-weight:600;font-size:12.5px;line-height:1.55;background:#f8fafc;border:1px solid var(--line);border-radius:9px;padding:9px 11px}
.depot-approx b{color:var(--ink)}
.depot-empty{color:var(--muted);font-weight:700;font-size:13px;padding:10px 6px;line-height:1.6}
.depot-geo{color:var(--blue);font-weight:800;white-space:nowrap}
.depot-picker{display:flex;gap:8px;flex-wrap:wrap}
@media(max-width:560px){ .depot-cam-grid{grid-template-columns:1fr} }

/* ---------- 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}
.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)}
/* 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}
.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}
/* swap picker */
.swap-opt{width:100%;display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px 14px;border:1px solid var(--line);border-radius:11px;background:#fff;margin-bottom:8px;cursor:pointer;font-family:var(--font)}
.swap-opt:hover{border-color:var(--blue);background:var(--panel-2)}
.swap-opt.is-cur{border-color:var(--blue);background:#e8effb}
.swap-opt.is-bad{opacity:.55;cursor:not-allowed}
.swap-opt__l{display:flex;flex-direction:column;text-align:left}
.swap-opt__l b{font-weight:800;font-size:15px}
.swap-opt__l small{color:var(--muted);font-weight:700;font-size:12px}
.swap-tag{font-size:11px;font-weight:800;color:var(--muted-2)}
.swap-opt.is-cur .swap-tag{color:var(--blue)}
.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}

/* Week navigation above the board */
.sched-nav{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:12px}
.sched-nav__btns{display:flex;gap:8px}
.sched-nav__label{font-weight:800;font-size:14px;color:var(--ink)}
.sched-nav__off{font-weight:800;font-size:12px;color:var(--orange);background:#fffaf3;border:1px solid var(--orange);border-radius:20px;padding:3px 10px}
@media(max-width:820px){
  .sched-nav{gap:8px}
  .sched-nav__label{font-size:13px;width:100%;order:3}
}
/* schedule conflict detection */
.sched-conflicts{padding:14px 18px;margin-bottom:18px;border-left:4px solid var(--red)}
.sched-conflicts.is-warn{border-left-color:var(--orange)}
.sched-conflicts.is-clear{border-left-color:var(--ok);color:var(--ok);font-weight:800;display:flex;align-items:center;gap:8px}
.scfl-hd{display:flex;align-items:center;gap:6px;font-size:15px;margin-bottom:6px;flex-wrap:wrap}
.scfl-ico{font-size:18px}
.scfl-sub{color:var(--muted);font-weight:700;font-size:12px;margin-left:auto}
.scfl-day{margin-top:10px}
.scfl-day__hd{font-size:11px;font-weight:900;text-transform:uppercase;letter-spacing:1px;color:var(--muted-2);margin-bottom:5px}
.sched-conflicts ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}
.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--warning:hover{border-color:var(--orange)}
.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}
.scfl-alt{display:block;margin-top:5px;font-size:12.5px;font-weight:700;color:var(--ok);background:#e3f5ea;border-radius:7px;padding:4px 8px}
.scfl-alt b{color:var(--ink)}
.scfl-alt--none{color:#c96a12;background:#fdecd8}
.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}
.msg-wrap{display:flex;gap:16px;height:calc(100vh - 170px);min-height:440px}
.msg-list{width:300px;flex:none;overflow:auto;padding:8px}
.msg-drv{display:block;width:100%;text-align:left;background:none;padding:10px 12px;border-radius:10px;cursor:pointer;border:1px solid transparent}
.msg-drv:hover{background:#f5f8fd}
.msg-drv.is-active{background:#eef4ff;border-color:var(--blue)}
.msg-drv__top{display:flex;align-items:center;gap:8px}
.msg-drv__top b{font-size:14px}
.msg-unread{margin-left:auto;background:var(--red);color:#fff;font-size:11px;font-weight:900;min-width:18px;height:18px;border-radius:9px;display:inline-flex;align-items:center;justify-content:center;padding:0 5px}
.msg-drv__sub{color:var(--muted);font-size:12px;font-weight:700;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.msg-thread{flex:1;display:flex;flex-direction:column;padding:0;overflow:hidden}
.msg-thread__hd{padding:12px 16px;border-bottom:1px solid var(--line);display:flex;align-items:baseline;gap:10px}
.msg-thread__hd span{color:var(--muted);font-weight:700;font-size:13px}
.msg-scroll{flex:1;overflow:auto;padding:16px;display:flex;flex-direction:column;gap:10px;background:#f7f9fc}
.msg-bub{max-width:74%;padding:9px 12px;border-radius:14px}
.msg-bub__t{font-size:14px;line-height:1.35}
.msg-bub__m{font-size:11px;font-weight:700;color:var(--muted-2);margin-top:3px}
.msg-bub--in{align-self:flex-start;background:#fff;border:1px solid var(--line);border-bottom-left-radius:4px}
.msg-bub--out{align-self:flex-end;background:var(--blue);color:#fff;border-bottom-right-radius:4px}
.msg-bub--out .msg-bub__m{color:rgba(255,255,255,.72)}
.msg-empty{margin:auto;color:var(--muted);font-weight:700}
.msg-compose{display:flex;gap:8px;padding:12px;border-top:1px solid var(--line);background:#fff}
.msg-compose input{flex:1;padding:11px 14px;border:1px solid var(--line);border-radius:10px;font-size:14px;font-family:inherit}
@media(max-width:820px){.msg-wrap{flex-direction:column;height:auto}.msg-list{width:auto;max-height:190px}.msg-thread{height:68vh}}
/* demurrage */
.demurrage-card{margin-top:20px;border-left:4px solid var(--orange)}
/* relay marker in the consignments list */
.leg-pill{display:inline-block;margin-left:6px;padding:1px 7px;border-radius:999px;background:#e8effb;color:var(--blue);font-size:11px;font-weight:800;white-space:nowrap}
/* 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}
.leg-row{display:flex;align-items:center;gap:12px;padding:10px 12px;border:1px solid var(--line);border-radius:11px;background:#fff}
.leg-seq{flex:none;width:38px;height:38px;border-radius:10px;background:var(--ink);color:#fff;display:grid;place-items:center;font-weight:900;line-height:1}
.leg-seq span{display:block;font-size:8.5px;font-weight:700;color:var(--muted-2);letter-spacing:.3px}
.leg-body{flex:1;min-width:0}
.leg-route{font-weight:800;font-size:14.5px}
.leg-meta{font-size:12.5px;font-weight:700;color:var(--muted);margin-top:2px}
.leg-job{color:var(--blue)}
.leg-none{color:var(--red);font-weight:800}
/* ---- #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}
.hov-c{display:flex;align-items:flex-start;gap:10px;padding:9px 11px;border:1px solid var(--line);
  border-left:4px solid var(--muted-2);border-radius:10px;background:#fff}
.hov-c--done{border-left-color:var(--ok)}
.hov-c--doing{border-left-color:var(--orange)}
.hov-c--blocked{border-left-color:var(--yellow)}
.hov-c--todo{border-left-color:var(--muted-2)}
.hov-c--inline{margin-top:6px;padding:6px 8px;border:0;border-left:3px solid var(--muted-2);border-radius:0;background:transparent}
.hov-c--inline.hov-c--done{border-left-color:var(--ok)}
.hov-c--inline.hov-c--doing{border-left-color:var(--orange)}
.hov-c--inline.hov-c--blocked{border-left-color:var(--yellow)}
.hov-c__n{flex:none;width:34px;height:34px;border-radius:9px;background:var(--ink);color:#fff;
  display:grid;place-items:center;font-weight:900;font-size:14px;line-height:1}
.hov-c__n small{display:block;font-size:8px;font-weight:700;color:var(--muted-2);letter-spacing:.3px}
.hov-c__b{flex:1;min-width:0}
.hov-c__who{font-weight:800;font-size:13.5px;display:flex;flex-wrap:wrap;gap:8px;align-items:baseline}
.hov-c__ph{font-size:11.5px;font-weight:800;color:var(--blue)}
.hov-c__rt{font-size:12px;font-weight:700;color:var(--muted);margin-top:1px}
.hov-c__cap{display:flex;flex-wrap:wrap;gap:4px 10px;margin-top:4px}
.hov-c__st1{font-size:12px;font-weight:700;color:var(--ink)}
.hov-c__st1 b{color:var(--blue)}
.hov-c__cap--none{display:block;font-size:12px;font-weight:800;color:var(--muted);padding:3px 8px;border-radius:7px;
  background-image:repeating-linear-gradient(45deg,#eef0f4 0 5px,#f8f9fb 5px 10px);border:1px solid var(--line)}
.hov-c__blk{font-size:12px;font-weight:800;color:#7a4a12;margin-top:3px}
.hov-c__state{flex:none;font-size:11px;font-weight:900;letter-spacing:.4px;text-transform:uppercase;color:var(--muted);align-self:center}
.hov-do{margin-top:14px;border:1px solid var(--line);border-radius:10px;background:#fbfcfe;padding:0 11px}
.hov-do>summary{cursor:pointer;padding:10px 0;font-weight:900;font-size:13px;color:var(--blue);list-style:none}
.hov-do>summary::-webkit-details-marker{display:none}
.hov-do>summary::before{content:'\21C4';margin-right:7px}
.hov-do__help{font-size:12.5px;font-weight:700;color:var(--muted);line-height:1.45;margin:0 0 10px}
.hov-do .fld{margin-bottom:10px}
.hov-do .btn{margin-bottom:12px}

/* ---- #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}
.cov-chip{display:inline-block;padding:1px 8px;border-radius:999px;font-size:11px;font-weight:800;
  letter-spacing:.2px;white-space:nowrap;background:#eef1f6;color:var(--muted);border:1px solid var(--line)}
.cov-chip--pickup{background:#fff4e2;color:#8a5510;border-color:#f0d3a8}
.cov-chip--linehaul{background:#e8effb;color:var(--blue);border-color:#c6d6f3}
.cov-chip--delivery{background:#e6f4ec;color:#166a3c;border-color:#bde0cb}
.cov-chip--all{background:var(--ink);color:#fff;border-color:var(--ink)}
/* The one thread number, on the internal con note sheet only. */
.cov-thread{margin-top:6px;font-size:11.5px;font-weight:800;color:var(--muted);line-height:1.4}
.cov-thread b{color:var(--blue);font-size:13px}
.cov-thread small{display:block;font-size:10px;font-weight:700;color:var(--muted-2)}
.cov-po{margin-top:4px;font-size:11.5px;font-weight:800;color:var(--muted);line-height:1.4}
.cov-po b{color:var(--ink);font-size:13px}
.cov-po small{display:block;font-size:10px;font-weight:700;color:var(--muted-2)}
.cov-po--none b{color:#a12c00}
/* the changeover sits between the two legs it joins */
.leg-over{display:flex;align-items:center;gap:8px;margin:6px 0 6px 19px;padding:7px 12px 7px 20px;
  border-left:2px dashed var(--line);font-size:12.5px;font-weight:700;color:#7a4a12;background:#fff6ef;
  border-radius:0 10px 10px 0}
.leg-over.is-done{color:var(--muted);background:#f4f6fa}
.leg-over b{color:inherit}
.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}
/* open incidents panel (Events screen) */
.inc-panel{margin-bottom:18px;border-left:4px solid var(--red)}
.inc-item{display:flex;gap:12px;align-items:flex-start;padding:10px 12px;border:1px solid var(--line);border-radius:10px;margin-bottom:8px}
.inc-item:last-child{margin-bottom:0}
.inc-item__b{flex:1}
.inc-item__d{font-weight:700;font-size:13.5px;margin-top:3px}
.inc-item__m{font-size:12px;font-weight:700;color:var(--muted);margin-top:3px}
.inc-sev-tag{font-size:11px;font-weight:900;text-transform:uppercase;letter-spacing:.5px;padding:1px 7px;border-radius:999px}
.inc-sev-tag--low{background:#e3f5ea;color:var(--ok)}
.inc-sev-tag--medium{background:#fdecd8;color:#c96a12}
.inc-sev-tag--high{background:#fde6df;color:var(--red)}
.inc-status-tag{background:var(--line);color:var(--muted)}
.inc-panel__hd{display:flex;flex-wrap:wrap;align-items:center;gap:12px;justify-content:space-between;margin-bottom:12px}
.inc-filters{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
.inc-filters select{padding:6px 10px;border:1px solid var(--line);border-radius:8px;font-weight:700;font-size:13px;background:#fff}
.inc-item[data-id]{cursor:pointer;transition:border-color .12s,background .12s}
.inc-item[data-id]:hover,.inc-item[data-id]:focus-visible{border-color:var(--blue);background:var(--bg-soft,#f6f8fc);outline:none}
.inc-item.is-resolved{opacity:.62}
.inc-item__go{flex:none;align-self:center;color:var(--muted);font-size:22px;font-weight:900;line-height:1}
.inc-shots{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}
.inc-shot{padding:0;border:1px solid var(--line);border-radius:10px;overflow:hidden;cursor:pointer;background:#000;width:104px;height:104px}
.inc-shot img{width:100%;height:100%;object-fit:cover;display:block}
.inc-shot:hover,.inc-shot:focus-visible{border-color:var(--blue);outline:none}
/* 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}
/* Asset register summary on the Settings page (the register itself is #/assets) */
.as-sum{display:flex;flex-wrap:wrap;gap:8px}
.as-sum__pill{background:#f5f8fd;border:1px solid var(--line);border-radius:9px;padding:7px 12px;font-weight:700;font-size:13px;color:var(--muted)}
.as-sum__pill b{color:var(--ink);font-size:15px;font-weight:900;margin-right:4px}
.as-sum__pill--ok{background:#e9f7ef;border-color:#c6e9d5}
.as-sum__pill--ok b{color:#136c3c}
.as-sum__pill--warn{background:#fff6ec;border-color:#f3d9b8}
.as-sum__pill--warn b{color:#c96a12}
.as-sum__types{margin-top:12px;font-weight:700;font-size:13px;color:var(--muted);line-height:1.6}
.fleet-swms__item small{color:var(--muted-2);font-weight:700;margin-left:4px}
/* Geofence editor (#/geofences) */
.gf-kind{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--line);border-radius:9px;padding:7px 12px;font-weight:800;font-size:13px;cursor:pointer;background:#fff}
.gf-kind.is-on{border-color:var(--blue);background:#eef3fb;color:var(--blue)}
.gf-kind input{margin:0}
.gf-cb{display:flex;align-items:center;gap:8px;font-weight:700;font-size:13.5px;padding:5px 0;cursor:pointer}
.gf-cb input{width:18px;height:18px;flex:none}
.gf-cbs{display:flex;flex-wrap:wrap;gap:4px 16px}
.gf-tog{display:inline-flex;align-items:center;gap:6px;font-weight:800;font-size:12.5px;cursor:pointer}
.gf-tog input{width:16px;height:16px}
/* A / B endpoint labels on a line fence (Leaflet permanent tooltip) */
.gf-end-lbl{background:var(--blue);color:#fff;border:none;box-shadow:none;font-weight:900;font-size:11px;padding:1px 6px;border-radius:6px}
.gf-end-lbl::before{display:none}
/* invoicing reviewer queue */
.inv-list{display:flex;flex-direction:column;gap:14px}
.inv-card{background:var(--card,#fff);border:1px solid var(--line);border-radius:14px;padding:16px 18px;box-shadow:0 1px 3px rgba(20,30,60,.04)}
.inv-card.is-queried{border-left:4px solid var(--orange)}
.inv-card__hd{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}
.inv-sub{color:var(--muted);font-weight:700;font-size:12.5px;margin-top:3px}
.inv-flag{background:#fdecd8;color:#c96a12;font-size:11px;font-weight:900;padding:1px 7px;border-radius:999px}
.inv-total{font-size:22px;font-weight:900;color:var(--blue);text-align:right;white-space:nowrap}
.inv-total span{display:block;font-size:11px;font-weight:700;color:var(--muted-2)}
.inv-lines{width:100%;border-collapse:collapse;margin:12px 0}
.inv-lines td{padding:5px 0;font-weight:600;font-size:13.5px;border-bottom:1px solid var(--line)}
.inv-lines td.num{text-align:right;white-space:nowrap}
.inv-lines .inv-sub-row td{border-bottom:none;color:var(--muted);font-weight:800;padding-top:8px}
.inv-acts{display:flex;justify-content:flex-end;gap:8px}
.inv-approved{color:var(--green,#1f9d55);font-weight:800;font-size:13px;text-align:right}
/* job documents & permits */
.attach-card{margin-top:20px}
.attach-card.is-needed{border-left:4px solid var(--red)}
.attach-warn{background:#fde6df;color:var(--red);font-weight:800;font-size:13px;padding:9px 12px;border-radius:9px;margin-bottom:10px}
.attach-list{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}
.attach-row{display:flex;align-items:center;gap:10px;border:1px solid var(--line);border-radius:9px;padding:8px 10px}
.attach-ico{font-size:16px;color:var(--muted-2);width:20px;text-align:center;flex:none}
.attach-b{flex:1;min-width:0}
.attach-b b{display:block;font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.attach-b small{color:var(--muted);font-weight:700;font-size:12px}
.attach-add{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.attach-add select,.attach-add input{padding:8px 10px;border:1px solid var(--line);border-radius:8px;font-family:inherit;font-weight:600;font-size:13px}
.attach-add input{flex:1;min-width:170px}
/* 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)}
/* compliance snapshot */
.comp-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:10px}
.comp-row{display:flex;align-items:center;gap:12px;border:1px solid var(--line);border-radius:10px;padding:10px 12px}
.comp-row.is-ok{background:#f3faf5;border-color:#cfe9d8}
.comp-row.is-warn{background:#fff8ee;border-color:#f3dcb8}
.comp-row.is-bad{background:#fdf0ec;border-color:#f5cec1}
.comp-n{font-size:24px;font-weight:900;min-width:34px;text-align:center}
.comp-row.is-ok .comp-n{color:var(--ok)} .comp-row.is-warn .comp-n{color:#c96a12} .comp-row.is-bad .comp-n{color:var(--red)}
.comp-b b{display:block;font-size:13px}
.comp-b small{color:var(--muted);font-weight:700;font-size:11.5px}
/* job inbox — inbound customer requests */
.inb-list{display:flex;flex-direction:column;gap:14px}
.inb-card{background:var(--card,#fff);border:1px solid var(--line);border-radius:14px;padding:16px 18px;box-shadow:0 1px 3px rgba(20,30,60,.04)}
.inb-card--new{border-left:4px solid var(--orange)}
.inb-card--accepted{border-left:4px solid var(--ok)}
.inb-card--rejected{border-left:4px solid var(--red);opacity:.75}
.inb-hd{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}
.inb-src{background:#eef4ff;color:var(--blue);font-size:11px;font-weight:900;text-transform:uppercase;letter-spacing:.5px;padding:2px 8px;border-radius:999px}
.inb-sub{color:var(--muted);font-weight:700;font-size:12.5px;margin-top:4px}
.inb-body{margin:10px 0}
.inb-dl{display:grid;grid-template-columns:auto 1fr;gap:3px 14px;margin:0}
.inb-dl dt{color:var(--muted);font-weight:700;font-size:12.5px}
.inb-dl dd{margin:0;font-weight:800;font-size:13px}
.inb-notes{background:#f7f9fc;border-radius:8px;padding:8px 10px;font-weight:700;font-size:12.5px;margin-top:8px}
.inb-acts{display:flex;justify-content:flex-end;gap:8px}
.inb-acts--done{color:var(--muted);font-weight:700;font-size:12.5px}
.inb-acts--done a{color:var(--blue);font-weight:800}
/* 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}
/* job forms (jacking / oversize) */
.jf-card{margin-top:20px}
.jf-card.is-outstanding{border-left:4px solid var(--orange)}
.jf-row{border-top:1px solid var(--line);padding:10px 0}
.jf-row:first-of-type{border-top:none}
.jf-row b{font-size:14px}
.jf-pending{display:block;color:#c96a12;font-weight:700;font-size:12.5px;margin-top:2px}
.jf-meta{display:block;color:var(--ok);font-weight:800;font-size:12.5px;margin-top:2px}
.jf-vals{display:grid;grid-template-columns:auto 1fr;gap:2px 14px;margin:8px 0 0}
.jf-vals dt{color:var(--muted);font-weight:700;font-size:12.5px}
.jf-vals dd{margin:0;font-weight:800;font-size:12.5px}
/* 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}
.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}
.depot-chip{display:inline-block;background:#e8effb;color:var(--blue);font-weight:800;font-size:12px;padding:5px 7px;border-radius:8px;letter-spacing:.3px}
.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}
.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}
/* start–finish and how long that is. A 150px cell can't always hold both on
   one line, so each part is kept whole and the duration drops beneath the
   range rather than the range itself breaking after the en dash. */
.sc-time{font-weight:900;color:#b23b12;display:flex;flex-wrap:wrap;column-gap:7px;row-gap:1px}
.sc-rng{white-space:nowrap}
.sc-dur{font-weight:800;color:var(--muted);white-space:nowrap}
.sc-route{color:#2c2c2c}
.sc-goods{color:#555;font-size:10.5px}
.sc-cn{color:var(--blue);font-weight:900;margin-top:2px}
.sc[data-cn]{cursor:pointer;transition:.12s}
.sc[data-cn]:hover{filter:brightness(.96);box-shadow:inset 0 0 0 2px var(--blue)}
.sc--empty{min-height:58px;background:transparent}
.sc--fatigue{background:#9ed06e;color:#1f4d09;align-items:center;justify-content:center}
.sc--mand{background:#e8433a;color:#fff;align-items:center;justify-content:center;text-align:center}
.sc--transit{background:#d7dbe2;color:#3a3f49;align-items:center;justify-content:center}
.sc--deliver{background:#ffe6cf}
.sc--load{background:#cfe3ff}
.sc--rt{background:#f3d0e6}
.sc--arrive{background:#e7dcff;align-items:center;justify-content:center}
.sc--off{background:#ffe14d;color:#5a4b00;align-items:center;justify-content:center;text-align:center}

/* ---- 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}
.tl-l--grey{border-top-color:#c3cad6}
.tl-l--amber{border-top-color:#e8890c}
.tl-l--green{border-top-color:#1f9d55}
.tl-l--red{border-top-color:#c62828}
.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}
/* The pip is the LEGEND's swatch. It is deliberately NOT drawn on a run — see the
   measurement in tlRow(): a pip plus three chips needs 125px in a 94px row and
   wrapped to three lines. */
.tl-pip{display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;
  border-radius:999px;border:1px solid rgba(34,31,32,.16);flex:none}
.tl-pip i{font-style:normal;font-weight:900;font-size:9px;line-height:1}
.tl-pip--grey{background:#dfe4ec;color:#5c6472}
.tl-pip--amber{background:#f9a825;color:#3d2b00}
.tl-pip--green{background:#1f9d55;color:#fff}
.tl-pip--red{background:#c62828;color:#fff}
/* One chip per phase this component covers, and they have to fit a ~94px row:
   the letter code carries the identity, the fill and the glyph carry the state,
   and the padding is as tight as it is because three of them measured 84px here
   against 94px available. */
.tl-c{display:inline-flex;align-items:center;gap:1px;padding:1px 2px;border-radius:4px;
  font-size:8px;font-weight:900;white-space:nowrap;
  border:1px solid rgba(34,31,32,.14)}
.tl-c i{font-style:normal;font-weight:900;font-size:8px}
.tl-c--grey{background:#eef1f6;color:#5c6472}
.tl-c--amber{background:#ffe6b8;color:#7a4a06}
.tl-c--green{background:#d6efdf;color:#125c32}
.tl-c--red{background:#fbdcda;color:#9c1f1f}
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}
/* The legend, and the HORIZON note under it — which is the honest half. */
.tl-leg{padding:16px 18px;margin-top:14px}
.tl-leg__row{display:flex;flex-wrap:wrap;gap:14px;margin-bottom:9px}
.tl-k{display:inline-flex;align-items:center;gap:5px;font-size:12px;font-weight:800;color:var(--ink)}
.tl-leg__p{margin:0 0 8px;color:var(--muted);font-weight:600;font-size:12.5px;line-height:1.6}
.tl-leg__p b{color:var(--ink)}
.tl-leg__p:last-child{margin-bottom:0}
.tl-leg__p--hz{color:#7a4a12;background:#fff6ef;border-left:4px solid var(--orange);
  border-radius:0 9px 9px 0;padding:9px 12px;font-weight:700}
/* The same answer written out, in the run edit modal — the chips are 8px and
   their explanation is a hover title, which is no use on a touch screen. */
.tl-p{margin-top:2px;padding:11px 13px;border-radius:10px;background:#f8fafc;border:1px solid var(--line)}
.tl-p--red{background:#fdf0ee;border-color:#f3cdc6}
.tl-p--none{color:var(--muted);font-weight:700;font-size:12.5px;line-height:1.55}
.tl-p__hd{display:flex;align-items:center;gap:7px;font-size:13.5px;margin-bottom:7px}
.tl-p__r{display:flex;align-items:baseline;gap:6px;flex-wrap:wrap;font-size:12.5px;
  font-weight:700;padding:4px 0;border-top:1px dashed var(--line)}
.tl-p__s{color:var(--muted);font-weight:700;font-size:11.5px;text-transform:uppercase;letter-spacing:.4px}
.tl-p__w{flex-basis:100%;color:var(--muted);font-weight:600;font-size:11.5px;line-height:1.5}
.tl-p__f{margin:8px 0 0;color:var(--muted);font-weight:600;font-size:11.5px;line-height:1.5}
.tl-p__f b{color:var(--ink)}

.sched-hint{margin-top:12px;color:var(--muted);font-weight:700;font-size:12px}
.sc[draggable=true]{cursor:grab}
.sc[draggable=true]:active{cursor:grabbing}
.sc-dragging{opacity:.4}
.sched__cell.drop-hover{outline:2px dashed var(--blue);outline-offset:-3px;background:#eef4ff!important}
.unsched-card[draggable=true]{cursor:grab}
.unsched-grip{color:var(--muted-2);font-size:14px;cursor:grab}
.sched-key{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}
.sk{font-size:11px;font-weight:800;padding:5px 10px;border-radius:6px}
.unsched-head{width:100%;display:flex;align-items:center;justify-content:space-between;background:none;border:none;cursor:pointer;font-family:var(--font);padding:0}
.unsched-caret{color:var(--muted-2);font-weight:900;font-size:13px}
.unsched{display:flex;gap:12px;flex-wrap:wrap;align-items:stretch}
.unsched-drop{flex:1;min-width:200px;border:2px dashed var(--line);border-radius:12px;display:grid;place-items:center;color:var(--muted-2);font-weight:800;font-size:13px;padding:16px;text-align:center}
.unsched-drop.is-hover{border-color:var(--red);background:#fff6f2;color:var(--red)}
.sk[data-newtype]{cursor:grab}
.sk[data-newtype]:active{cursor:grabbing}
.unsched-card{border:1px solid var(--line);border-radius:12px;padding:14px;width:290px;background:var(--panel-2)}
.unsched-top{display:flex;align-items:center;gap:8px;font-weight:900;color:var(--blue)}
.unsched-route{font-weight:800;font-size:14px;margin-top:6px}
.unsched-goods{color:var(--muted);font-weight:700;font-size:12px;margin-top:2px}
.unsched-ctrls{display:flex;gap:6px;margin-top:11px;flex-wrap:wrap}
.unsched-ctrls select{padding:8px;border:1px solid var(--line);border-radius:8px;font-family:var(--font);font-weight:700;font-size:12px;flex:1;min-width:120px;background:#fff}

/* ---------- 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}
.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}
.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}
.qinfo{background:#e8effb;color:var(--blue);border-radius:10px;padding:11px 14px;font-weight:800;margin-bottom:12px}
.qinfo--ok{background:#e4f6ec;color:var(--ok)} .qinfo--bad{background:#fde6df;color:var(--red)}
.email{border:1px solid var(--line);border-radius:11px;padding:12px 14px;margin-bottom:10px;border-left:4px solid var(--muted-2)}
.email--in{border-left-color:var(--blue);background:var(--panel-2)}
.email--out{border-left-color:var(--orange)}
.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}

/* ---------- 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{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--levy{background:#fdefe0;border-color:#f6d3ab}
.reco__d b{font-weight:800;font-size:13.5px;display:block}
.reco__d span{color:var(--muted);font-weight:700;font-size:12px}

/* ---------- Events Log ---------- */
.ev-type{display:inline-flex;align-items:center;gap:8px;font-weight:800}
.ev-ico{width:30px;height:30px;border-radius:8px;display:grid;place-items:center;color:#fff;font-size:14px;flex-shrink:0}
.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}
.sev--critical{color:var(--red)} .sev--warning{color:#c96a12} .sev--info{color:var(--muted)}
.table tr.is-ack{opacity:.5}
.table tr.is-ack .ev-type{font-weight:700}
.ev-unack{width:8px;height:8px;border-radius:50%;background:var(--blue);display:inline-block}
.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}
/* event detail modal */
.ev-hero{display:flex;align-items:center;gap:14px;margin-bottom:16px}
.ev-hero .ev-ico{width:46px;height:46px;font-size:20px;border-radius:12px}
.ev-hero h3{font-size:18px;margin:0}
.ev-hero .ev-sub{color:var(--muted);font-weight:700;font-size:13px;margin-top:2px}
.ev-mini-map{height:170px;border-radius:12px;overflow:hidden;margin-top:14px;border:1px solid var(--line)}

/* ---------- 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}
.toast--ok{background:var(--ok)}

/* ---------- Wall-display mode ---------- */
body.wall .sidebar,body.wall .topbar{display:none}
body.wall .view{padding:22px}
/* depot monitor header (wall display) */
.wall-hd{display:flex;align-items:center;gap:14px;background:var(--ink);color:#fff;padding:14px 24px}
.wall-hd[hidden]{display:none}
.wall-hd__mark{width:38px;height:38px}
.wall-hd__word{font-weight:900;font-size:20px;letter-spacing:.5px}
.wall-hd__word b{color:#fff} .wall-hd__word i{color:var(--orange);font-style:normal}
.wall-hd__title{font-size:19px;font-weight:800;color:#fff;border-left:1px solid rgba(255,255,255,.25);padding-left:14px}
.wall-hd__clock{margin-left:auto;font-size:19px;font-weight:800;color:var(--yellow);font-variant-numeric:tabular-nums}
/* 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}
.wall-links{display:flex;flex-wrap:wrap;gap:8px}

/* ---------- Responsive / tablet ---------- */
@media(max-width:1100px){
  .kpis{grid-template-columns:repeat(2,1fr)}
  .dash-row{grid-template-columns:1fr}
  .board{grid-template-columns:repeat(2,1fr)}
  .detail{grid-template-columns:1fr}
  .map-wrap{grid-template-columns:1fr;height:auto}
  #map{height:60vh}
}
@media(max-width:820px){
  .sidebar{position:fixed;left:0;top:0;z-index:60;transform:translateX(-100%);transition:transform .25s;box-shadow:0 0 40px rgba(0,0,0,.4)}
  @media(prefers-reduced-motion:reduce){.sidebar{transition:none}}
  .sidebar.is-open{transform:translateX(0)}
  /* Tighter brand in the mobile drawer — it scrolls away with the menu */
  .brand{padding:14px 20px 10px;gap:6px}
  .brand__mark{width:64px}
  .brand__word{font-size:21px;border-bottom-width:3px;padding-bottom:4px}
  .brand__app{font-size:11px;letter-spacing:2px}
  .nav__item{padding:11px 15px}
  .hamburger{display:block}
  .view{padding:18px}
  .board{grid-template-columns:1fr}
  .user__name{display:none}
}
@media(max-width:560px){.kpis{grid-template-columns:1fr}}

/* ---------- L&D checks / site forms (see NF.Forms) ---------- */
.ld-card{margin-top:20px}
.ld-pending{color:var(--muted);font-weight:700;font-size:13px;line-height:1.55}
.ld-sub{border:1px solid var(--line);border-radius:12px;padding:14px;margin-top:10px;background:#fbfcfe}
.ld-sub.is-bad{border-color:var(--red);background:#fff6f2}
.ld-sub__hd{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;font-size:13px;font-weight:700;margin-bottom:10px}
.ld-sub__hd b{font-size:15px}
.ld-sub__no{color:var(--muted-2);font-weight:800}
.ld-sub__by{color:var(--muted)}
/* 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}
/* layout + colour only — .geo-link above owns the link affordance, so the old
   text-decoration pair here would double up with its border on hover */
.ld-sub__geo{margin-left:auto;color:var(--ok);font-weight:800}
.ld-nc{background:#fff;border:1px solid var(--red);border-radius:10px;padding:10px 12px;margin-bottom:10px;font-weight:700;font-size:13px;color:#8a2b12;line-height:1.5}
.ld-nc__ref{margin-top:6px;font-weight:600;color:var(--ink)}
.ld-vals{display:grid;grid-template-columns:minmax(160px,1fr) minmax(80px,auto);gap:4px 14px;margin:0}
.ld-vals dt{font-weight:700;font-size:13px;color:var(--muted);line-height:1.4}
.ld-vals dd{margin:0;font-weight:800;font-size:13px;text-align:right}
.ld-vals dd.is-bad{color:var(--red)}
.ld-shots{display:flex;flex-wrap:wrap;gap:10px;margin-top:12px}
.ld-shot{border:1px solid var(--line);background:#fff;border-radius:10px;padding:0;overflow:hidden;width:120px;cursor:pointer;text-align:left}
.ld-shot img{width:100%;height:86px;object-fit:cover;display:block}
.ld-shot span{display:block;padding:6px 8px;font-weight:700;font-size:11px;line-height:1.3;color:var(--muted)}
.ld-shot:hover{border-color:var(--blue)}
/* office sign-off shown on the submission itself */
.ld-rv{border-radius:10px;padding:9px 12px;margin-bottom:10px;font-weight:700;font-size:13px;line-height:1.5}
.ld-rv--ok{background:#e9f7ef;border:1px solid #c6e9d5;color:#136c3c}
.ld-rv--no{background:#fdeee8;border:1px solid #f7cdbd;color:#a92c00}
/* 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}
/* reviewer sign-off panel (also used by the review modal) */
.lcv-review{margin-top:16px;border-radius:11px;padding:13px 15px;font-weight:700;font-size:13.5px;border:1px solid var(--line)}
.lcv-review--open{background:#f7f9fd}
.lcv-review--ok{background:#e9f7ef;border-color:#c6e9d5;color:#136c3c}
.lcv-review--no{background:#fdeee8;border-color:#f7cdbd;color:#a92c00}
.lcv-review__note{margin-top:6px;padding:8px 10px;background:rgba(255,255,255,.7);border-radius:8px;font-weight:600}
.lcv-concerns{margin:6px 0 0;padding:0;list-style:none}
.lcv-concerns li{padding:3px 0;color:#c96a12;font-weight:700;font-size:13px}
.lcv-acts{display:flex;gap:10px;justify-content:flex-end;margin-top:12px;flex-wrap:wrap}
@media(max-width:820px){
  .ld-vals{grid-template-columns:1fr auto}
  .chk-bar{flex-direction:column;align-items:stretch}
}
/* ---- 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}

/* An asset number is a link to its page. Inherits the colour of whatever line
   it sits on — the alerts list, fleet status and the events log all style their
   own text — and only adds the affordance. */
.asset-link{color:inherit;text-decoration:none;border-bottom:1px dotted currentColor}
.asset-link:hover{border-bottom-style:solid}

/* ==========================================================================
   WET HIRE OPERATIONS
   --------------------------------------------------------------------------
   The board is a bus x time-of-day Gantt, not the lane x day grid the transport
   schedule uses. That is the whole point of the section: wet hire's wasted
   capacity is a hole in the MIDDLE of a working day — a crew bus goes out at
   dawn and back at knock-off — so a day-per-column board would collapse the one
   thing worth looking at into a single cell.

   Blocks are positioned with percentage lefts/widths against a fixed window
   (WH_T0..WH_T1 in js/app.js), so the row is resolution-independent and the
   whole grid stays one CSS grid rather than a canvas.
   ========================================================================== */
.wh-toolbar{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:14px}
.wh-day{display:flex;align-items:center;gap:6px}
.wh-day input[type=date]{font-family:var(--font);font-weight:800;font-size:15px;padding:9px 10px;
  border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--panel);color:var(--ink)}
.wh-nav{min-width:40px;height:40px;border:1px solid var(--line);background:var(--panel);border-radius:var(--radius-sm);
  font-weight:900;font-size:16px;cursor:pointer;color:var(--ink)}
.wh-nav:hover{background:var(--panel-2);border-color:var(--blue-l)}

/* ---- the grid ---- */
.wh-board{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden}
.wh-scroll{overflow-x:auto}
.wh-grid{min-width:1000px}
.wh-row{display:grid;grid-template-columns:var(--wh-lane,176px) var(--wh-rec,196px) 1fr;border-top:1px solid var(--line)}
.wh-row:first-child{border-top:0}
.wh-lane{padding:10px 12px;border-right:1px solid var(--line);background:var(--panel-2);display:flex;
  flex-direction:column;justify-content:center;gap:2px;min-height:56px}
.wh-lane__bus{font-weight:900;font-size:14px;letter-spacing:.2px}
.wh-lane__sub{font-size:11.5px;color:var(--muted);font-weight:700;line-height:1.25;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wh-track{position:relative;min-height:56px;background:
  repeating-linear-gradient(90deg,transparent 0,transparent calc(100%/18 - 1px),var(--line) calc(100%/18 - 1px),var(--line) calc(100%/18))}

/* time ruler */
.wh-ruler{display:grid;grid-template-columns:var(--wh-lane,176px) var(--wh-rec,196px) 1fr;background:var(--panel-2);border-bottom:2px solid var(--line);position:sticky;top:0;z-index:3}
.wh-ruler__pad{border-right:1px solid var(--line);padding:8px 12px;font-weight:900;font-size:11.5px;
  letter-spacing:.6px;text-transform:uppercase;color:var(--muted)}
.wh-ruler__scale{position:relative;height:34px}
.wh-tick{position:absolute;top:0;bottom:0;border-left:1px solid var(--line);padding-left:5px;
  font-size:11px;font-weight:800;color:var(--muted);display:flex;align-items:center}

/* ---- blocks ---- */
.wh-blk{position:absolute;top:7px;bottom:7px;border-radius:8px;padding:4px 8px;overflow:hidden;
  font-size:11.5px;font-weight:800;line-height:1.2;cursor:pointer;border:1px solid transparent;
  display:flex;flex-direction:column;justify-content:center}
.wh-blk small{display:block;font-weight:700;font-size:10.5px;opacity:.85;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wh-blk--am{background:#e8effb;border-color:#c3d4f2;color:var(--blue-d)}
.wh-blk--pm{background:#fdf0e2;border-color:#f7d5ae;color:#8a4a08}
.wh-blk--charter{background:#e6f6ec;border-color:#b6e3c6;color:#136b39}
.wh-blk--charter.is-draggable{cursor:grab}
.wh-blk--charter.is-draggable:active{cursor:grabbing}
.wh-blk:focus-visible{outline:2px solid var(--blue);outline-offset:1px}
.wh-blk.wh-dragging{opacity:.4}

/* the idle window — the product this section exists to sell */
.wh-gap{position:absolute;top:11px;bottom:11px;border-radius:8px;border:1.5px dashed #cbd5e6;
  background:repeating-linear-gradient(135deg,#fafbfe 0,#fafbfe 7px,#f2f5fb 7px,#f2f5fb 14px);
  display:flex;align-items:center;justify-content:center;gap:6px;font-size:11px;font-weight:800;
  color:var(--muted);cursor:pointer;transition:border-color .12s,background .12s}
.wh-gap:hover,.wh-gap:focus-visible{border-color:var(--ok);background:#f1faf4;color:#136b39;outline:none}
.wh-gap--sellable{border-color:#9ed3b4;color:#17794a}
.wh-gap.drop-hover{border-color:var(--ok);border-style:solid;background:#e6f6ec}
.wh-gap__h{font-weight:900}

/* signon pip */
.wh-pip{position:absolute;top:50%;width:9px;height:9px;margin:-4.5px 0 0 -4.5px;border-radius:50%;
  background:var(--panel);border:2px solid var(--blue);z-index:2}
.wh-pip--pm{border-color:var(--orange)}

/* free buses */
.wh-row--free .wh-lane{background:#fbfcfe}
.wh-row--free .wh-lane__bus{color:var(--muted)}
.wh-free-note{position:absolute;left:10px;top:50%;transform:translateY(-50%);font-size:11.5px;
  font-weight:800;color:var(--muted-2)}

/* ---- charter palette ---- */
.wh-pal{display:flex;gap:8px;align-items:center;flex-wrap:wrap;padding:12px 14px;background:var(--panel-2);
  border:1px solid var(--line);border-radius:var(--radius);margin-bottom:14px}
.wh-pal__lbl{font-size:11.5px;font-weight:900;letter-spacing:.5px;text-transform:uppercase;color:var(--muted)}
.wh-chip{padding:7px 12px;border-radius:999px;background:#e6f6ec;border:1px solid #b6e3c6;color:#136b39;
  font-weight:800;font-size:12.5px;cursor:grab}
.wh-chip:active{cursor:grabbing}

/* ---- stat strip ---- */
.wh-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(168px,1fr));gap:12px;margin-bottom:14px}
.wh-stat{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:12px 14px;box-shadow:var(--shadow)}
.wh-stat b{display:block;font-size:23px;font-weight:900;letter-spacing:-.4px;line-height:1.15}
.wh-stat span{display:block;font-size:11.5px;font-weight:800;color:var(--muted);margin-top:3px}
.wh-stat small{display:block;font-size:10.5px;font-weight:700;color:var(--muted-2);margin-top:5px;line-height:1.35}
.wh-stat--sell b{color:var(--ok)}
.wh-stat--warn b{color:var(--orange)}

/* ---- the assumption notice — never let a derived number read as measured --- */
.wh-caveat{background:#fff8ec;border:1px solid #f6dcb4;border-radius:var(--radius);padding:12px 14px;
  margin-bottom:14px;font-size:12.5px;font-weight:700;color:#7a4a08;line-height:1.5}
.wh-caveat b{font-weight:900}

@media(max-width:1180px){
  /* The recommendation column is the first thing to go: the board is still
     usable without it, and the timeline is not usable when squeezed. */
  .wh-row,.wh-ruler{grid-template-columns:var(--wh-lane,176px) 1fr}
  .wh-rec,.wh-ruler__pad+.wh-ruler__pad{display:none}
}
@media(max-width:860px){
  .wh-row,.wh-ruler{grid-template-columns:var(--wh-lane,120px) 1fr}
  .wh-lane__sub{display:none}
}
/* A departure outside the board's window, pinned to the edge rather than drawn
   off it. Marked so it can never be mistaken for a departure at 04:00. */
.wh-blk--off{border-style:dashed;opacity:.82}
.wh-blk{min-width:54px}
/* The gap band runs BEHIND the departures. A movement block is a fixed-width
   marker rather than a duration, so it routinely overlaps the start of the
   window that follows it; drawn on top, the band made the gap look like it
   began before the last departure had left. */
.wh-gap{z-index:0}
.wh-blk{z-index:1}
/* A bus that is hired in rather than owned. It has no asset page, so its
   number is not a link — this says why instead of leaving a dead-looking
   number next to live ones. */
.wh-hired{display:inline-block;margin-left:6px;padding:1px 6px;border-radius:999px;
  background:var(--line);color:var(--muted);font-size:9.5px;font-weight:900;
  letter-spacing:.4px;text-transform:uppercase;vertical-align:middle}

/* ---- bus card ----------------------------------------------------------
   Clicking a bus number opens this rather than navigating, which is the same
   interaction the live map uses: the marker opens a popup and the popup holds
   the link to the asset page. The number is a <button> because it opens a
   card; the "Asset details" inside is a real <a> so open-in-new-tab works. */
.wh-bus{font:inherit;font-weight:900;color:var(--blue);background:none;border:0;padding:0;
  cursor:pointer;letter-spacing:.2px;border-bottom:1px dotted var(--blue-l)}
.wh-bus:hover{color:var(--blue-d);border-bottom-style:solid}
.wh-bus:focus-visible{outline:2px solid var(--blue);outline-offset:2px;border-radius:3px}
.wh-pop{position:absolute;z-index:80;width:min(280px,calc(100vw - 16px));background:var(--panel);
  border:1px solid var(--line);border-radius:var(--radius);box-shadow:0 8px 30px rgba(34,31,32,.16);
  padding:12px 14px;font-size:13px;font-weight:700}
.wh-pop hr{border:none;border-top:1px solid var(--line);margin:9px 0}
.wh-pop__hd{display:flex;align-items:center;gap:7px}
.wh-pop__id{font-weight:900;font-size:16px;letter-spacing:.2px}
.wh-pop__x{margin-left:auto;background:none;border:0;font-size:14px;color:var(--muted);
  cursor:pointer;padding:2px 4px;line-height:1}
.wh-pop__x:hover{color:var(--ink)}
.wh-pop__sub{color:var(--muted);font-size:12px;font-weight:700;margin-top:3px;line-height:1.4}
.wh-pop__row{margin-top:5px}
.wh-pop__row b{font-size:15px}
.wh-pop__sell{margin-top:7px;padding:5px 8px;border-radius:8px;background:#e6f6ec;
  color:#136b39;font-weight:900;font-size:12px}
.wh-pop__foot{margin-top:10px;padding-top:9px;border-top:1px solid var(--line)}
.wh-pop__foot a{color:var(--blue);font-weight:900}
.wh-pop__foot a:hover{text-decoration:underline}

/* ---- recommendation column ---------------------------------------------
   What to look at on this row. Colour carries the kind, because the four are
   different decisions: a duplicated trip is a saving to go and check, an idle
   window is revenue to go and sell, and a hired-in bus while your own sit
   still is money already going out of the door. */
.wh-rec{padding:8px 10px;border-right:1px solid var(--line);display:flex;flex-direction:column;
  justify-content:center;gap:2px;min-height:56px;background:var(--panel);overflow:hidden}
.wh-rec__hd{display:flex;align-items:baseline;gap:5px}
.wh-rec__ico{font-size:12px;font-weight:900;flex:none;line-height:1.3}
.wh-rec__t{font-size:11.5px;font-weight:900;line-height:1.25}
.wh-rec__n{font-size:10px;font-weight:900;opacity:.7;flex:none}
.wh-rec__d{font-size:10.5px;font-weight:700;color:var(--muted);line-height:1.3;
  overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.wh-rec__more{font-size:10px;font-weight:800;color:var(--muted-2);margin-top:1px}
.wh-rec--dup{background:#fdf0e8;border-left:3px solid var(--red)}
.wh-rec--dup .wh-rec__t,.wh-rec--dup .wh-rec__ico{color:#a52c05}
.wh-rec--cons{background:#eef3fc;border-left:3px solid var(--blue-l)}
.wh-rec--cons .wh-rec__t,.wh-rec--cons .wh-rec__ico{color:var(--blue-d)}
.wh-rec--hire{background:#fdf6e3;border-left:3px solid var(--yellow)}
.wh-rec--hire .wh-rec__t,.wh-rec--hire .wh-rec__ico{color:#8a6508}
.wh-rec--sell{background:#eef8f1;border-left:3px solid var(--ok)}
.wh-rec--sell .wh-rec__t,.wh-rec--sell .wh-rec__ico{color:#136b39}
.wh-rec--free{background:var(--panel-2)}
.wh-rec--free .wh-rec__t,.wh-rec--free .wh-rec__ico{color:var(--muted-2)}

/* 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)}
.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}

/* ---------- 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}
.asst-intro{padding:16px 20px;border-left:4px solid var(--blue)}
.asst-intro__lead{margin:0 0 8px;font-weight:700;color:var(--ink);line-height:1.55}
.asst-intro__note{margin:0;font-weight:700;font-size:12.5px;color:var(--muted);line-height:1.6}
.asst-log{display:flex;flex-direction:column;gap:12px;overflow-y:auto;padding:4px 2px;min-height:220px;max-height:calc(100vh - 380px)}
.asst-empty{color:var(--muted);font-weight:700;text-align:center;padding:40px 20px}
.asst-turn{display:flex}
.asst-turn--user{justify-content:flex-end}
.asst-turn--ai{justify-content:flex-start}
.asst-bub{max-width:82%;padding:12px 15px;border-radius:14px;font-weight:600;line-height:1.55;white-space:pre-wrap;word-wrap:break-word;overflow-wrap:anywhere}
.asst-bub--user{background:var(--blue);color:#fff;border-bottom-right-radius:4px}
.asst-bub--ai{background:var(--panel);border:1px solid var(--line);color:var(--ink);border-bottom-left-radius:4px;box-shadow:var(--shadow)}
.asst-bub--partial{border-left:3px solid var(--orange)}
.asst-bub--err{border-left:3px solid var(--red);background:#fde6df;color:#8a1f00;font-weight:700}
.asst-err-ico{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:50%;background:var(--red);color:#fff;font-weight:900;font-size:12px;margin-right:4px;vertical-align:-3px}
.asst-text{white-space:pre-wrap}
.asst-muted{color:var(--muted);font-weight:700}
/* thinking indicator */
.asst-bub--think{color:var(--muted);font-weight:700;display:inline-flex;align-items:center;gap:8px}
.asst-dots{display:inline-flex;gap:4px}
.asst-dots i{width:7px;height:7px;border-radius:50%;background:var(--muted-2);display:inline-block;animation:asstBounce 1.2s infinite ease-in-out both}
.asst-dots i:nth-child(1){animation-delay:-.24s}
.asst-dots i:nth-child(2){animation-delay:-.12s}
@keyframes asstBounce{0%,80%,100%{transform:scale(.5);opacity:.5}40%{transform:scale(1);opacity:1}}
@media(prefers-reduced-motion:reduce){.asst-dots i{animation:none}}
/* grounding + sources under an answer */
.asst-ground{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:10px;padding-top:10px;border-top:1px dashed var(--line)}
.asst-ground__lbl{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:var(--muted)}
.asst-chip{font-size:12px;font-weight:800;padding:3px 9px;border-radius:999px;background:#e8effb;color:var(--blue)}
.asst-src{margin-top:8px;font-size:12.5px}
.asst-src__lbl{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:var(--muted)}
.asst-src ul{margin:4px 0 0;padding-left:18px;color:var(--ink);font-weight:600}
.asst-src li{margin:2px 0;line-height:1.5}
.asst-src li b{color:var(--blue)}
.asst-partial{margin-top:8px;font-size:12.5px;font-weight:700;color:#c96a12}
.asst-inj{margin-top:8px;font-size:12px;font-weight:700;color:var(--muted);font-style:italic}
/* composer */
.asst-bar{display:flex;gap:10px;align-items:flex-end;background:var(--panel);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow);padding:10px;flex-wrap:wrap}
.asst-input{flex:1 1 200px;min-width:0;resize:none;font-family:var(--font);font-size:15px;font-weight:600;color:var(--ink);border:1px solid var(--line);border-radius:10px;padding:11px 13px;line-height:1.5;max-height:160px;overflow-y:auto;background:var(--panel-2)}
.asst-input:focus{outline:2px solid var(--blue-l);outline-offset:-1px;background:#fff}
.asst-input:disabled{background:#f0f2f6;color:var(--muted-2);cursor:not-allowed}
.asst-send{flex:0 0 auto;min-height:44px}
.asst-send:disabled{opacity:.5;cursor:not-allowed}
.asst-note{flex:1 1 100%;font-weight:700;font-size:13px;color:var(--muted);line-height:1.6}
.asst-state{padding:22px;color:var(--muted);font-weight:700;line-height:1.6}
.asst-state--off{border-left:4px solid var(--orange)}
.asst-foot{margin:10px 2px 0;font-weight:700;font-size:11.5px;color:var(--muted-2);line-height:1.55}

/* ---- action-proposal card (#214): the model proposes, you confirm ---- */
.asst-prop{margin-top:12px;border:1px solid var(--line);border-left:4px solid var(--blue);border-radius:12px;background:var(--panel-2);padding:12px 14px}
.asst-prop.is-confirmed{border-left-color:var(--ok)}
.asst-prop.is-invalid,.asst-prop.is-error{border-left-color:var(--red)}
.asst-prop.is-cancelled{border-left-color:var(--muted-2);opacity:.85}
.asst-prop__hd{display:flex;align-items:center;gap:8px;margin-bottom:6px}
.asst-prop__kind{font-size:10.5px;font-weight:800;text-transform:uppercase;letter-spacing:.7px;color:var(--blue);background:#e8effb;padding:3px 9px;border-radius:999px}
.asst-prop__sum{font-weight:700;color:var(--ink);line-height:1.5}
.asst-prop__change{display:flex;align-items:stretch;gap:10px;flex-wrap:wrap;margin-top:10px}
.asst-prop__party{flex:1 1 120px;min-width:0;display:flex;flex-direction:column;gap:2px;background:var(--panel);border:1px solid var(--line);border-radius:9px;padding:7px 11px;font-weight:800;color:var(--ink)}
.asst-prop__plbl,.asst-prop__mlbl{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.6px;color:var(--muted)}
.asst-prop__from{border-left:3px solid var(--muted-2)}
.asst-prop__to{border-left:3px solid var(--orange)}
.asst-prop__arrow{align-self:center;font-weight:900;font-size:18px;color:var(--muted)}
.asst-prop__metas{display:flex;flex-wrap:wrap;gap:8px 18px;margin-top:10px}
.asst-prop__meta{display:flex;flex-direction:column;gap:1px;font-weight:800;font-size:13px;color:var(--ink)}
.asst-prop__warns{margin-top:10px;display:flex;gap:8px;background:#fff6ea;border:1px solid #f5d9b0;border-radius:9px;padding:8px 11px}
.asst-prop__wico{color:var(--orange);font-weight:900;line-height:1.4}
.asst-prop__warns ul{margin:0;padding-left:16px;font-weight:700;font-size:12.5px;color:#8a5316;line-height:1.5}
.asst-prop__ground{margin-top:10px;font-size:12.5px}
.asst-prop__ground ul{margin:4px 0 0;padding-left:18px;color:var(--ink);font-weight:600}
.asst-prop__ground li{margin:2px 0;line-height:1.5}
.asst-prop__ground li b{color:var(--blue)}
.asst-prop__acts{display:flex;gap:10px;margin-top:12px;flex-wrap:wrap}
.asst-prop__btn{flex:0 0 auto}
.asst-prop__done{display:flex;align-items:flex-start;gap:7px;margin-top:12px;padding:9px 12px;border-radius:9px;font-weight:800;font-size:13px;line-height:1.45}
.asst-prop__done--ok{background:#e6f6ee;color:#0f6d3b}
.asst-prop__done--cancel{background:#eef1f6;color:var(--muted)}
.asst-prop__done--invalid{background:#fde6df;color:#8a1f00}
.asst-prop__dico{font-weight:900}
@media(max-width:640px){
  .asst-bub{max-width:90%}
  .asst-log{max-height:none}
  .asst-send{flex:1 1 100%}
  .asst-prop__btn{flex:1 1 100%}
  .asst-prop__arrow{transform:rotate(90deg)}
}

/* ==========================================================================
   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}
.cmp-actor{display:flex;flex-wrap:wrap;align-items:center;gap:10px;background:var(--panel-2);border:1px solid var(--line);border-radius:11px;padding:9px 12px;font-weight:700;font-size:12.5px}
.cmp-actor__roles{color:var(--blue);font-weight:800}
.cmp-actor__note{color:var(--muted);font-weight:600;font-size:11.5px;flex:1 1 260px;line-height:1.45}
.cmp-tabs{display:flex;flex-wrap:wrap;gap:7px}
.cmp-tab{font-family:inherit;font-weight:800;font-size:12.5px;padding:7px 13px;border-radius:9px;border:1px solid var(--line);background:#fff;color:var(--muted);cursor:pointer}
.cmp-tab.is-on{background:var(--blue);border-color:var(--blue);color:#fff}
.cmp-filters,.cmp-hzs{display:flex;flex-wrap:wrap;align-items:center;gap:12px;font-weight:700;font-size:12.5px;color:var(--muted)}
.cmp-hz{font-family:inherit;font-weight:800;font-size:12px;padding:5px 11px;border-radius:8px;border:1px solid var(--line);background:#fff;color:var(--muted);cursor:pointer}
.cmp-hz.is-on{background:var(--orange);border-color:var(--orange);color:#fff}
.cmp-head__lead{margin:0 0 6px;font-weight:700;color:var(--ink);line-height:1.5}
.cmp-head__names{margin:0;padding-left:18px;font-weight:700;line-height:1.7}
.cmp-head__names span{color:var(--red);font-weight:800;font-size:12px;margin-left:6px}
.cmp-head__more,.cmp-head__as{margin:6px 0 0;color:var(--muted);font-weight:600;font-size:11.5px}
.cmp-empty b{display:block;margin-bottom:4px;color:var(--ink)}
.cmp-empty p{margin:0;color:var(--muted);font-weight:600;font-size:12.5px;line-height:1.55}
.cmp-note{color:var(--muted);font-weight:600;font-size:12.5px;line-height:1.55}
.cmp-acts{display:flex;flex-wrap:wrap;gap:8px}
.cmp-btns{display:flex;gap:6px;white-space:nowrap}
.cmp-kind,.cmp-code{font-size:10.5px;font-weight:800;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);background:var(--panel-2);border-radius:5px;padding:1px 5px;margin-left:4px}
.cmp-blk{color:var(--red)}
.cmp-ok{color:var(--muted);font-weight:600;font-size:12px}
.cmp-noev{color:var(--red);font-weight:700;font-size:11.5px}
.cmp-row--blocked{background:#fff7f4}
.cmp-row--void{opacity:.55;text-decoration:line-through}
/* Status pills. `indeterminate` gets its OWN look and never the compliant green:
   "we cannot confirm currency" is not "current", and painting it green would be
   inventing a status. */
.cmp-pill{display:inline-block;font-size:11px;font-weight:800;padding:2px 8px;border-radius:999px;white-space:nowrap}
.cmp-pill--compliant{background:#e4f5ea;color:#1f7a44}
.cmp-pill--expiring{background:#fdefe0;color:#b4650f}
.cmp-pill--grace{background:#fdf0e4;color:#9a4d00}
.cmp-pill--waived{background:#eef2fb;color:var(--blue-d)}
.cmp-pill--unverified{background:#f2f0fb;color:#5b46b8}
.cmp-pill--rejected{background:#fde6df;color:#8a1f00}
.cmp-pill--expired{background:#fde6df;color:#b3200a}
.cmp-pill--missing{background:#eceff4;color:#5b6472}
.cmp-pill--indet{background:repeating-linear-gradient(135deg,#f4f2ea,#f4f2ea 5px,#eae6d8 5px,#eae6d8 10px);color:#7a6520;border:1px dashed #c9b877}
.cmp-tr__hd{display:flex;flex-wrap:wrap;align-items:center;gap:9px;margin-bottom:4px}
.cmp-tr__hd b{font-size:14px}
.cmp-tr__via{color:var(--muted);font-weight:600;font-size:11.5px;margin-left:auto}
.cmp-tr__reason{margin:0 0 8px;color:var(--ink);font-weight:600;font-size:12.5px;line-height:1.5}
.cmp-tr__none,.cmp-tr__waiv{margin:6px 0 0;color:var(--muted);font-weight:600;font-size:12px}
/* The roster join on the overdue report (#236 report 3). `cmp-ros`/`cmp-need`
   verified unused in css/app.css, driver/driver.css and all three apps' JS
   before they were added.
   `--unknown` is HATCHED, exactly as `.cmp-pill--indet` is, and for the same
   reason: a staff record or a driver with no line on the board is "we cannot
   tell", which must not be drawn like the settled grey of "not working". Today
   and tomorrow are the two that need a phone call, so they carry the red and
   the orange; the detail line under the chip is what stops the chip having to
   carry a day and a count in a table cell. */
.cmp-ros{display:inline-block;font-size:11px;font-weight:800;padding:2px 8px;border-radius:999px;white-space:nowrap}
.cmp-ros--today{background:#fde6df;color:#b3200a}
.cmp-ros--tomorrow{background:#fdefe0;color:#b4650f}
.cmp-ros--none{background:#eceff4;color:#5b6472}
.cmp-ros--unknown{background:repeating-linear-gradient(135deg,#f4f2ea,#f4f2ea 5px,#eae6d8 5px,#eae6d8 10px);color:#7a6520;border:1px dashed #c9b877}
.cmp-ros__d{display:block;color:var(--muted);font-weight:600;font-size:11px;margin-top:3px}
.cmp-head__names .cmp-ros{margin-left:6px}
.cmp-head__names .cmp-ros__d{display:inline;margin:0 0 0 6px}
/* Who is short, in a table cell: names first, then how many were not listed. */
.cmp-need{font-weight:700;font-size:12px;line-height:1.6}
.cmp-need span{color:var(--muted);font-weight:600}
.cmp-gate{border-left:4px solid var(--red)}
.cmp-gate__list{margin:0;padding-left:18px;font-weight:700;line-height:1.6}
.cmp-gate__who{margin:8px 0 0;color:var(--muted);font-weight:600;font-size:12.5px}
/* The audit evidence pack (#236 report 11). `cmp-pk` verified unused in
   css/app.css, driver/driver.css and all three apps' JS before it was added.

   THE THREE SECTION STATES ARE DRAWN AS THREE DIFFERENT THINGS, and the third
   is the one that matters: `ok` is produced, `empty` is "we looked and there is
   nothing in this period", and `unavailable` is "we could not look" — which is
   HATCHED, exactly as .cmp-pill--indet and .cmp-ros--unknown are, and for the
   same reason. A grey pill on an unread change log would let a bundle handed to
   a regulator read as a clean record of something nobody ever looked at. */
.cmp-pk__bar{display:flex;flex-wrap:wrap;align-items:center;gap:10px;padding:12px 14px}
.cmp-pk__bar label{display:flex;align-items:center;gap:6px;font-weight:800;font-size:12px;
  color:var(--muted);text-transform:uppercase;letter-spacing:.4px}
.cmp-pk__wait{padding:22px;color:var(--muted);font-weight:700}
.cmp-pk__prov{border-left:4px solid var(--blue)}
.cmp-pk__warn{color:var(--red)}
.cmp-pk__k{display:flex;flex-wrap:wrap;gap:8px;align-items:baseline;padding:3px 0;
  font-size:12.5px;line-height:1.6;border-top:1px solid var(--line)}
.cmp-pk__k b{min-width:110px;font-weight:800}
.cmp-pk__k span{color:var(--muted);font-weight:600;flex:1 1 260px}
.cmp-pk__sec{margin-top:14px}
.cmp-pk__hd{display:flex;flex-wrap:wrap;align-items:center;gap:10px}
.cmp-pk__n{color:var(--muted);font-weight:800;font-size:12px}
.cmp-pk__says{flex:1 1 100%;margin:2px 0 0;color:var(--ink);font-weight:600;
  font-size:12.5px;line-height:1.6}
.cmp-pk__more{margin:8px 0 0;color:var(--muted);font-weight:600;font-size:12px;line-height:1.6}
.cmp-pk-chip{display:inline-block;font-size:11px;font-weight:800;padding:2px 9px;border-radius:999px;
  white-space:nowrap}
.cmp-pk-chip--ok{background:#e4f4ea;color:#186c37}
.cmp-pk-chip--empty{background:#eceff4;color:#5b6472}
.cmp-pk-chip--unavailable{background:repeating-linear-gradient(135deg,#f4f2ea,#f4f2ea 5px,#eae6d8 5px,#eae6d8 10px);
  color:#7a6520;border:1px dashed #c9b877}


/* ==========================================================================
   ASSESSMENTS (#238) — the dispatcher's quiz authoring and question analysis.
   `qz-` verified unused in css/app.css, driver/driver.css, js/app.js and
   driver/driver.js before it was added. Deliberately NOT a bare single-word
   class: `.card`, `.chk`, `.act`, `.list`, `.detail`, `.board`, `.kpis`,
   `.tabs`, `.reco`, `.grid`, `.items`, `.sum` and `.flow` are all already taken
   here, and borrowing one silently inherits its layout — which is exactly how
   the recommendations panel once shipped rendering sideways.
   ========================================================================== */
.qz-q{border:1px solid var(--line);border-radius:12px;padding:14px;margin-bottom:12px;background:#fbfcfe}
.qz-q__hd{display:flex;align-items:center;gap:8px;margin-bottom:10px}
.qz-q__n{font-weight:900;font-size:12px;color:var(--muted);letter-spacing:.4px;text-transform:uppercase}
.qz-q__tools{margin-left:auto;display:flex;gap:4px}
.qz-opts{margin-top:8px}
.qz-opt{display:flex;align-items:center;gap:8px;margin-bottom:6px}
.qz-opt input[type=text]{flex:1;min-width:0}
.qz-opt__tick{display:flex;align-items:center;gap:5px;font-weight:800;font-size:12px;color:var(--muted);white-space:nowrap}
.qz-opt__x{border:none;background:none;color:var(--red);font-weight:900;cursor:pointer;font-size:15px;padding:0 4px}
.qz-warn{background:#fff6f2;border:1px solid var(--red);border-radius:10px;padding:10px 12px;margin-bottom:12px;
  color:#8a2b12;font-weight:700;font-size:12.5px;line-height:1.55}
.qz-warn ul{margin:6px 0 0;padding-left:18px}
/* Failure rate. A bar rather than a bare number so the worst-first ordering is
   readable at a glance; the number is still printed beside it, because a bar is
   not a figure anyone can quote in a toolbox talk. */
.qz-rate{display:flex;align-items:center;gap:8px;min-width:130px}
.qz-rate__bar{flex:1;height:8px;border-radius:5px;background:#eceff4;overflow:hidden}
.qz-rate__fill{display:block;height:100%;background:var(--orange);border-radius:5px}
.qz-rate__fill--hot{background:var(--red)}
.qz-rate__n{font-weight:900;font-size:12.5px;min-width:42px;text-align:right}
.qz-stem{font-weight:800}
.qz-gone{display:inline-block;font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.3px;
  padding:1px 6px;border-radius:5px;background:#eceff4;color:#5b6472;margin-left:6px}
.qz-lead{color:var(--muted);font-weight:700;font-size:13px;line-height:1.55;margin:4px 0 0;max-width:720px}
/* "No attempts recorded yet" is NOT a 0% failure rate — the same refusal the
   dashboard's speed tile makes about showing 0 before it has looked. */
/* The heading carries its own class rather than being styled as `.qz-empty b` —
   a bare `b` selector would make every emphasis in the body copy a block too,
   breaking "This is not a 0% failure rate" across three lines. The same mistake
   was made once on the driver's result panel; see `.qz-res__t`. */
.qz-empty{padding:34px 26px;text-align:center;color:var(--muted);font-weight:700;line-height:1.6}
.qz-empty__t{display:block;color:var(--ink);font-size:15px;margin-bottom:6px}

/* ==========================================================================
   THE ENGINE ROOM (#247) — `er-` prefix
   Verified unused in css/app.css and driver/driver.css before adding. Behaviour
   hooks plus the minimum styling; the board's own .sched/.sc/.sum-row rules do
   the heavy lifting and are deliberately reused rather than duplicated.
   ========================================================================== */

/* --- driver qualifications on the row (Anthony: "it used to be a little
   column there") — dense enough to sit under a name without stealing height --- */
.er-quals{display:flex;flex-wrap:wrap;gap:3px;margin:3px 0 2px}
.er-q{display:inline-block;padding:1px 5px;border-radius:4px;background:#eef2fa;
  border:1px solid var(--line);color:var(--ink);font-size:9px;font-weight:900;letter-spacing:.3px}
/* "No tickets on file" is a gap in the register, NOT a driver who holds none —
   so it reads as an absence of data rather than as a qualification chip. */
.er-q--none{background:transparent;border-style:dashed;color:var(--muted);
  font-weight:800;letter-spacing:0;text-transform:none}

/* --- depot / projects grouping (his sheet's row order, top to bottom) --- */
.er-grp__hd{position:sticky;left:0;background:#f4f7fc;border-top:2px solid var(--line);
  border-bottom:1px solid var(--line);padding:7px 12px;text-align:left;white-space:nowrap}
.er-grp__name{font-size:12px;font-weight:900;letter-spacing:.6px;text-transform:uppercase;color:var(--ink)}
.er-grp__n{margin-left:8px;font-size:11px;font-weight:800;color:var(--muted)}
.er-grp__idle{margin-left:10px;padding:1px 7px;border-radius:99px;background:var(--orange);
  color:#fff;font-size:10px;font-weight:900}
.er-grp__ok{margin-left:10px;font-size:10px;font-weight:900;color:var(--ok)}
/* The projects note. That division is booked months ahead and shares drivers, so
   it is on this board for coordination even though it is managed elsewhere. */
.er-grp__note{margin-left:10px;font-size:10px;font-weight:700;color:var(--muted);
  font-style:italic;white-space:normal}

/* --- the idle signal. This is the morning scan: "I literally go down. I see
   that every driver is working." A cell holding only a time is YARD DUTIES, not
   work, so it is marked rather than counted as covered. --- */
.er-idle{display:block;margin:2px 0;padding:2px 6px;border-radius:5px;
  border:1px dashed var(--orange);background:#fff7ee;color:#8a4b06;
  font-size:10px;font-weight:900;text-align:center}
.sched__cell.er-st-yard{background:#fffdf8}

/* --- what the truck is pulling, by code (TR / DD / FT / F100 / DL) --- */
.er-rig{display:flex;flex-wrap:wrap;gap:3px;margin-top:3px}
.er-unit{display:inline-flex;align-items:center;gap:3px;padding:1px 5px;border-radius:4px;
  background:#fff;border:1px solid var(--line);font-size:9px;font-weight:800;color:var(--ink)}
.er-unit i{font-style:normal;font-weight:900;color:var(--blue);letter-spacing:.3px}

/* --- the pools rail. "A pool of drivers, a pool of trucks, a pool of trailers
   and a pool of dollies is what makes up a job." --- */
.er-rail{padding:18px 20px;margin-top:18px}
.er-rail__hd{margin-bottom:14px}
.er-rail__sub{display:block;margin-top:4px;color:var(--muted);font-weight:700;font-size:12.5px;line-height:1.6}
.er-rail__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(215px,1fr));gap:12px}
.er-pool{border:1px solid var(--line);border-radius:10px;padding:11px 12px;background:#fbfcfe}
.er-pool--err{border-style:dashed;background:transparent}
/* Each pool is a <details> COLLAPSED BY DEFAULT — four open cards pushed the
   board itself below the fold. The summary is the whole point: it carries the
   free/total count and the source, so the question the rail answers is readable
   without opening anything. Chrome drops the disclosure triangle once a summary
   is `display:flex`, so the marker is suppressed on both engines and drawn back
   as .er-pool__caret. */
.er-pool__hd{display:flex;align-items:baseline;justify-content:space-between;gap:8px;
  cursor:pointer;list-style:none;user-select:none}
.er-pool__hd::-webkit-details-marker{display:none}
.er-pool__hd:focus-visible{outline:2px solid var(--blue);outline-offset:3px;border-radius:5px}
.er-pool__ttl{display:flex;flex-direction:column;gap:1px;min-width:0}
.er-pool__hd b{font-size:12px;font-weight:900;letter-spacing:.5px;text-transform:uppercase}
/* The SOURCE rides in the SUMMARY, not only in the footer, because the footer is
   now behind the fold: 'register' is the live 1,189-asset register and 'fleet'
   is the sample list of a couple of dozen, and a count without it reads as the
   whole business either way. */
.er-pool__src{font-size:9.5px;font-weight:800;color:var(--muted);letter-spacing:0}
.er-pool__n{font-size:18px;font-weight:900;color:var(--ok);white-space:nowrap;margin-left:auto}
.er-pool__n i{font-style:normal;font-size:12px;font-weight:800;color:var(--muted)}
/* Nothing free is a shortage (red); nothing on the register is a pool nobody has
   entered (muted, italic — the same treatment the dolly footer row gives it).
   They must not render the same: they need different phone calls. */
.er-pool__n--out{color:var(--red)}
.er-pool__n--none{font-size:11.5px;font-weight:800;color:var(--muted);font-style:italic}
.er-pool__caret{flex:0 0 auto;align-self:center;width:0;height:0;
  border-left:4px solid transparent;border-right:4px solid transparent;
  border-top:5px solid var(--muted);transition:transform .12s ease}
details[open] > .er-pool__hd .er-pool__caret{transform:rotate(180deg)}
.er-pool__bd{margin-top:9px}
.er-pool__chips{display:flex;flex-wrap:wrap;gap:4px;min-height:22px}
.er-chip{display:inline-flex;align-items:center;gap:4px;padding:2px 7px;border-radius:5px;
  background:#fff;border:1px solid var(--line);font-size:11px;font-weight:800;cursor:grab}
.er-chip i{font-style:normal;font-weight:900;color:var(--blue);font-size:9px;letter-spacing:.3px}
.er-pool__none{font-size:11px;font-weight:800;color:var(--red)}
.er-pool__more{font-size:11px;font-weight:800;color:var(--muted);align-self:center}
/* The footer keeps the fuller statement — how many are allocated or away, and
   the source again in words — for whoever has opened the section. */
.er-pool__ft{margin-top:8px;padding-top:7px;border-top:1px solid var(--line);
  font-size:10.5px;font-weight:700;color:var(--muted);line-height:1.5}
.er-pool__note{display:block;margin-top:4px;font-size:11px;font-weight:700;color:var(--muted)}

/* ==========================================================================
   HIRED-IN CAPACITY — SUBCONTRACTORS AND GREY FLEET (#260)
   --------------------------------------------------------------------------
   Anthony French, 14 Aug: "this has gaps in it because we use subcontractors as
   well. We also use our Hazelmere grey guys down here in the projects area."

   Everything here is drawn so a hired-in row can NEVER be mistaken for one of
   ours, in either direction:

     • HATCHED, not coloured, wherever the claim is "we hold nothing". A green
       chip on somebody nobody has checked is the one failure with a licence
       consequence; a red one puts a warning on correctly arranged work. Same
       treatment .cmp-pill--indet, .cmp-ros--unknown, .adb-c--unknown and
       .ses-att--none already use, for exactly the same reason.
     • THE HIRED COUNT IS NEVER GREEN. The four pools' .er-pool__n is green
       because it is capacity we can allocate; hired-in capacity is not, so it
       carries its own muted treatment and reads "booked / rows", never "free".
     • A HIRED ROW IS TINTED, faintly, so a scan down the board can see where
       the boundary is without reading a word. Faint on purpose — it must not
       compete with the conflict outlines, which are what somebody is acting on.
   All prefixes verified unused in css/app.css, driver/driver.css and
   portal/portal.css first.
   ========================================================================== */

/* --- the carrier on the row --- */
.er-car{display:flex;flex-wrap:wrap;align-items:center;gap:4px 6px;margin:3px 0 2px;
  padding:3px 6px;border-radius:6px;font-size:10.5px;font-weight:800;line-height:1.4;
  background:repeating-linear-gradient(135deg,#f4f2ea,#f4f2ea 5px,#eae6d8 5px,#eae6d8 10px);
  border:1px dashed #c9b877;color:#6f5c1c}
.er-car i{font-style:normal;font-weight:900;font-size:9px;letter-spacing:.4px;
  padding:0 4px;border-radius:3px;background:#fff;border:1px solid #c9b877;color:#7a6520}
.er-car b{font-size:11px;font-weight:900;color:#5c4c14}
.er-car__cov,.er-car__ph{flex:0 0 100%;font-size:9.5px;font-weight:700;color:#7a6a3a;letter-spacing:0}
/* A projects row is OUR capacity managed elsewhere, not something we bought, so
   it is blue-grey rather than the hired hatch — a different fact, drawn
   differently, but still not the plain treatment an employed row gets. */
.er-car--projects{background:repeating-linear-gradient(135deg,#eef1f7,#eef1f7 5px,#e0e6f0 5px,#e0e6f0 10px);
  border-color:#a9b6cd;color:#3c4a63}
.er-car--projects i{border-color:#a9b6cd;color:#2f3d55}
.er-car--projects b{color:#2f3d55}
/* The control that sets it. Quiet until hovered — it is a per-row setting
   somebody touches rarely, and forty of them shouting would be the noise the
   rest of this board is careful to avoid. */
.er-car__set{display:block;margin-top:3px;padding:1px 5px;border:1px dashed var(--line);
  border-radius:5px;background:transparent;color:var(--muted);
  font-family:inherit;font-size:9.5px;font-weight:800;cursor:pointer}
.er-car__set:hover{border-color:var(--blue);color:var(--blue)}
.er-row--hired > .sched__lane{background:#fdfcf6}
.er-row--projects > .sched__lane{background:#f8fafd}

/* "We hold no record" — NOT a qualification chip and NOT the "no tickets on
   file" one beside it. That one says our register has a gap; this says the
   record belongs to somebody else's employer, which is a different sentence
   with a different action. Hatched for the reason at the top of this block. */
.er-q--norec{background:repeating-linear-gradient(135deg,#f4f2ea,#f4f2ea 4px,#eae6d8 4px,#eae6d8 8px);
  border:1px dashed #c9b877;color:#7a6520;font-weight:800;letter-spacing:0;text-transform:none}

/* An empty day on a hired-in row. Muted, never the orange of .er-idle: that
   orange means "one of our drivers has nothing on", which is money going out
   the door. Capacity we arranged and did not call on costs us nothing. */
.er-idle--hired{border-style:dashed;border-color:#c9b877;background:#faf8f0;color:#7a6520}

/* Who is covering a run, ON the run — the row header scrolls out of sight on a
   board this wide, and "a subbie is covering this" must never read as "nobody
   is on this". */
.sc-hired{display:inline-flex;align-items:center;gap:4px;margin-top:3px;padding:1px 5px;
  border-radius:4px;font-size:9.5px;font-weight:900;letter-spacing:.2px;
  background:#fff;border:1px dashed #c9b877;color:#7a6520}
.sc-hired i{font-style:normal;font-weight:900;font-size:8.5px;letter-spacing:.4px;color:#8a7326}
.sc-hired--projects{border-color:#a9b6cd;color:#3c4a63}
.sc-hired--projects i{color:#3c4a63}
.sc--hired{box-shadow:inset 3px 0 0 #c9b877}

/* --- the rail card that is NOT a pool --- */
.er-hired{border:1px solid #e3dcc4;border-radius:10px;padding:11px 12px;background:#fdfcf6}
details[open] > .er-pool__hd .er-pool__caret{transform:rotate(180deg)}
.er-pool__n--hired{color:#7a6520;font-size:16px}
.er-chip--hired{cursor:default;background:#fffdf6;border-color:#ddd2ae}
.er-chip--hired i{color:#8a7326}
.er-chip--projects{background:#f7f9fc;border-color:#c8d3e4}
.er-chip--projects i{color:#3c4a63}
.er-chip__st{font-style:normal;font-size:9px;font-weight:800;color:var(--muted);letter-spacing:0}

/* Carriers on file — the master records, managed in place. */
.er-car__admin{margin-top:9px;padding-top:8px;border-top:1px solid var(--line)}
.er-car__admin-hd{display:flex;align-items:center;gap:8px;margin-bottom:6px}
.er-car__admin-hd b{font-size:11.5px}
.er-car__admin-hd .btn{margin-left:auto}
.er-car__list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:5px}
.er-car__list li{display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px;
  padding:5px 7px;border:1px solid var(--line);border-radius:7px;background:#fff}
.er-car__nm{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:900}
.er-car__nm i{font-style:normal;font-size:8.5px;font-weight:900;letter-spacing:.4px;
  padding:1px 4px;border-radius:3px;background:#f4f2ea;border:1px solid #c9b877;color:#7a6520}
.er-car__meta{font-size:10px;font-weight:700;color:var(--muted)}
.er-car__list .reg-btns{margin-left:auto;display:flex;gap:4px}
.er-car__none,.er-car__note{margin:6px 0 0;font-size:10.5px;font-weight:700;
  color:var(--muted);line-height:1.55}
.er-car__help{margin:0 0 10px;padding:9px 11px;border-radius:9px;background:#faf8f0;
  border:1px solid #e3dcc4;font-size:12px;font-weight:700;color:#6f5c1c;line-height:1.55}

/* --- what the utilisation panel did NOT count --- */
.er-grp__hired{font-size:10px;font-weight:800;color:#7a6520;padding:1px 6px;
  border-radius:5px;background:#faf8f0;border:1px dashed #c9b877}
.er-util__note--hired{color:#6f5c1c}
.er-util__hired{display:inline-block;margin-left:6px;padding:0 5px;border-radius:4px;
  font-size:9px;font-weight:900;letter-spacing:.2px;
  background:#faf8f0;border:1px dashed #c9b877;color:#7a6520}
/* The row whose ONLY work was hired-in. A zero here would read as spare when the
   asset was on the road, so it is called out rather than left to arithmetic. */
.er-util__hired--only{background:#fff3e2;border-color:var(--orange);color:#8a4b06}
.er-util__r--hired td{background:#fdfcf6}
/* DAYS OUT WITH A CARRIER — its own column, never folded into idle (#297). Dashed
   like the chip above so it reads as the same distinct fact rather than as one of
   our own figures; a day here is deliberately absent from `Idle days`. */
.er-util__oh{color:#7a6520;border-bottom:1px dashed #c9b877}

/* The #/crew half of this block lives further down, immediately after the
   .crew-q--* family — `.crew-q` is declared there and would otherwise win on
   background and border, which is exactly how a hatched refusal turns into a
   flat pill without anybody touching it. */

/* --- the dollies row in the day footer --- */
.er-src{display:block;font-size:9.5px;font-weight:800;color:var(--muted);text-transform:none;letter-spacing:0}
/* "None on register" is a pool nobody has entered, not a shortage — the two need
   different phone calls, so they must not render the same. */
.er-none{font-size:10.5px;font-weight:800;color:var(--muted);font-style:italic}

/* ---- Allocate from the pool (#247) --------------------------------------
   A driver chip is NOT draggable — a row on this board is a driver — so it
   must not carry the grab cursor that promises a drag. The rig chips sit ON
   the run, because what a truck is pulling belongs to the job rather than to
   the lane's single default trailer. */
.er-chip--static{cursor:default;opacity:.92}
.er-chip--dragging{opacity:.45}
/* The drop target is the RUN, not the day cell: a cell holds several runs and a
   trailer belongs to one of them. Outlined rather than filled so the run's own
   type colour still reads underneath. */
.er-drop{outline:2px solid var(--blue);outline-offset:1px;
  box-shadow:0 0 0 4px rgba(37,82,164,.16)}
.er-rig{display:flex;flex-wrap:wrap;gap:3px;margin-top:4px}
.er-rig__a{display:inline-flex;align-items:center;gap:3px;padding:1px 3px 1px 5px;
  border-radius:5px;background:rgba(255,255,255,.82);border:1px solid var(--line);
  font-size:9.5px;font-weight:900;letter-spacing:.2px;color:var(--ink);line-height:1.5}
.er-rig__a i{font-style:normal;opacity:.65}
/* 18px is under the 44px touch minimum on purpose: this is a dispatcher's
   pointer-driven board, not a driver's tablet, and a 44px control per trailer
   would push a five-asset road train past the height of its own cell. */
.er-rig__x{border:0;background:transparent;cursor:pointer;padding:0 2px;
  font-size:12px;line-height:1;font-weight:900;color:var(--muted);min-width:14px}
.er-rig__x:hover{color:var(--red)}
.er-rig__x:focus-visible{outline:2px solid var(--blue);outline-offset:1px;border-radius:3px}

/* ---- collapsing a depot group (#247) -------------------------------------
   "You've got forty plus drivers on there, sorting is quite big and clunky."
   A <details> cannot wrap table rows, so the header is a real <button>: natively
   focusable, Enter/Space for free. The rows are hidden, never removed — see the
   note in js/app.js above erGroupedRows. */
.er-grp__x{display:flex;align-items:baseline;gap:0;width:100%;background:none;border:0;padding:0;
  font-family:inherit;text-align:left;cursor:pointer;color:inherit}
.er-grp__x:focus-visible{outline:2px solid var(--blue);outline-offset:2px;border-radius:4px}
.er-grp__caret{flex:0 0 auto;align-self:center;margin-right:7px;width:0;height:0;
  border-left:4px solid transparent;border-right:4px solid transparent;
  border-top:5px solid var(--muted);transition:transform .12s ease}
.er-grp__x[aria-expanded="false"] .er-grp__caret{transform:rotate(-90deg)}
/* The count and the idle count are inside the button on purpose: they are what
   the fold must not hide, since "3 with no work today" is why the board is open. */
.er-grp__folded{margin-left:10px;font-size:10px;font-weight:800;color:var(--muted);font-style:italic}
.er-grp__all{margin-left:auto}
.sched tr[hidden]{display:none}

/* ---- the timing exception (#247) -----------------------------------------
   "There'll be times when timing is important, and we'll make notes of that and
   we HIGHLIGHT that." Today that highlight is a fill colour applied by hand in a
   SharePoint sheet, so this is deliberately loud — it is the exception on a
   day-based board, and it is rare by construction. */
.er-timed{display:block;margin-top:4px;padding:2px 6px;border-radius:5px;
  background:var(--blue);color:#fff;font-size:9px;font-weight:900;letter-spacing:.4px}
.er-timed__note{display:block;font-size:9.5px;font-weight:700;letter-spacing:0;
  text-transform:none;opacity:.92;white-space:normal;line-height:1.35;margin-top:1px}
.sc.er-is-timed{box-shadow:inset 3px 0 0 var(--blue)}
/* The field in the run editor. Off by default — the board is day-based. */
.er-tfld{margin:2px 0 4px;padding:11px 12px;border:1px solid var(--line);border-radius:10px;background:#f8fafc}
.er-tfld.is-off{opacity:.6}
.er-tfld__cb{display:flex;align-items:center;gap:9px;font-weight:700;font-size:13.5px;cursor:pointer}
.er-tfld__cb input{width:18px;height:18px}
.er-tfld input[type=text],.er-tfld #sc-timed-note{width:100%;margin-top:9px;padding:10px;
  border:1px solid var(--line);border-radius:8px;font-family:var(--font);font-weight:700;font-size:13.5px}
.er-tfld__hint{margin:8px 0 0;color:var(--muted);font-weight:700;font-size:11.5px;line-height:1.5}

/* ---- per-asset utilisation (#247) ---------------------------------------
   "We're going to know how many jobs TR104 did this year… do I need to buy more?"
   Collapsed by default like the pools rail, and for the same reason: the board is
   what the screen is for. The count rides in the summary so the fold cannot hide
   the one number somebody acts on. */
.er-util{padding:16px 20px;margin-top:18px}
.er-util__hd{display:flex;align-items:baseline;gap:10px;cursor:pointer;list-style:none;user-select:none}
.er-util__hd::-webkit-details-marker{display:none}
.er-util__hd:focus-visible{outline:2px solid var(--blue);outline-offset:3px;border-radius:5px}
.er-util__ttl{display:flex;flex-direction:column;gap:2px;min-width:0}
.er-util__hd b{font-size:13px;font-weight:900;letter-spacing:1px;text-transform:uppercase}
.er-util__sub{font-size:11.5px;font-weight:700;color:var(--muted)}
.er-util__n{margin-left:auto;font-size:18px;font-weight:900;color:var(--ok);white-space:nowrap}
.er-util__n i{font-style:normal;font-size:12px;font-weight:800;color:var(--muted)}
.er-util__n--out{color:var(--orange)}
/* "Not recorded" / "none on any run" are not numbers and must not look like one —
   the same treatment .er-pool__n--none gives an empty pool. */
.er-util__n--none{font-size:11.5px;font-weight:800;color:var(--muted);font-style:italic}
.er-util__caret{flex:0 0 auto;align-self:center;width:0;height:0;
  border-left:4px solid transparent;border-right:4px solid transparent;
  border-top:5px solid var(--muted);transition:transform .12s ease}
details[open] > .er-util__hd .er-util__caret{transform:rotate(180deg)}
.er-util__bd{margin-top:14px}
.er-util__ctrls{display:flex;flex-wrap:wrap;gap:14px;margin-bottom:12px}
.er-util__tabs{display:flex;gap:4px;flex-wrap:wrap}
/* 36px matches .btn--sm and the depot tabs beside them — this is a dispatcher's
   pointer-driven board, not a driver's tablet, so it is not the 44px minimum. */
.er-util__tab{font-family:var(--font);font-weight:800;font-size:12px;padding:6px 11px;border-radius:8px;
  border:1px solid var(--line);background:#fff;color:var(--muted);cursor:pointer;min-height:36px}
.er-util__tab.is-on{background:var(--blue);border-color:var(--blue);color:#fff}
/* Names and counts lead; the percentage is a column, never the headline. */
.er-util__hl{margin:0 0 8px;font-weight:700;font-size:13.5px;line-height:1.6;color:var(--ink)}
.er-util__hl--none{color:#8a4b06;background:#fff7ee;border:1px dashed var(--orange);
  border-radius:10px;padding:11px 13px}
.er-util__note{margin:0 0 12px;color:var(--muted);font-weight:700;font-size:12px;line-height:1.6}
.er-util__tbl th,.er-util__tbl td{white-space:nowrap}
.er-util__a{font-weight:800}
.er-util__a i{font-style:normal;font-weight:900;color:var(--blue);font-size:9.5px;
  letter-spacing:.3px;margin-right:5px}
/* An unknown figure is hatched and worded, never a 0 — printing null as 0% would
   put an asset on a disposal list for a window nobody has data for. */
.er-util__un{font-size:10.5px;font-weight:800;color:var(--muted);font-style:italic}
.er-util__more{margin:10px 0 0;color:var(--muted);font-weight:700;font-size:12px}

/* ==========================================================================
   THE JOB THREAD (#243/#246) — `jt-` prefix
   Verified unused in css/app.css and driver/driver.css before adding.
   ========================================================================== */
.jt-card{padding:20px 22px;margin-top:18px}
.jt-card--none{background:#fbfcfe}
.jt-hd{margin-bottom:14px}
.jt-sub{display:block;margin-top:4px;color:var(--muted);font-weight:700;font-size:12.5px;line-height:1.6}
.jt-none{color:var(--muted);font-weight:700;font-size:13px;line-height:1.6;margin:0}

/* The same digits at every stage — Q-10042 → B-10042 → C-10042. That identity IS
   the feature, so the flow shows the number at each stage rather than a label. */
.jt-flow{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-bottom:18px}
.jt-stage{display:flex;flex-direction:column;gap:2px;padding:8px 13px;border-radius:9px;
  border:1px solid var(--line);background:#fbfcfe;min-width:104px}
.jt-stage.is-reached{background:#fff;border-color:var(--blue)}
.jt-stage.is-on{background:var(--blue);border-color:var(--blue)}
.jt-stage__n{font-family:var(--mono,monospace);font-size:14px;font-weight:900;color:var(--ink)}
.jt-stage__l{font-size:10px;font-weight:900;letter-spacing:.5px;text-transform:uppercase;color:var(--muted)}
.jt-stage.is-on .jt-stage__n,.jt-stage.is-on .jt-stage__l{color:#fff}
.jt-arrow{color:var(--muted-2,#98a2b3);font-weight:900}
.jt-num{color:var(--blue);font-weight:900;letter-spacing:.3px}

.jt-grid{display:grid;grid-template-columns:minmax(240px,1fr) minmax(240px,1.2fr);gap:22px}
@media (max-width:820px){.jt-grid{grid-template-columns:1fr}}
.jt-po{color:var(--blue)}
/* A job that needs a PO and hasn't got one is STATED. Silently invoicing it is
   the failure this exists to prevent. */
.jt-warn{color:var(--red);font-weight:800}
.jt-link{color:var(--blue);font-weight:800}

.jt-hist__t{font-size:11px;font-weight:900;letter-spacing:.5px;text-transform:uppercase;
  color:var(--muted);margin:0 0 8px}
.jt-hist__l{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px}
.jt-hist__l li{display:flex;flex-wrap:wrap;gap:8px;align-items:baseline;padding-bottom:7px;
  border-bottom:1px solid var(--line);font-size:12.5px;font-weight:700}
.jt-hist__l li:last-child{border-bottom:0}
.jt-hist__l span{color:var(--muted);font-weight:700}
.jt-hist__l i{font-style:normal;color:var(--blue);font-weight:800}
.jt-hist__l em{font-style:normal;color:var(--muted);font-weight:600;flex-basis:100%}

/* Cancellation. "Only Tanya can cancel those, with my approval." */
.jt-cancel{margin-top:18px;padding:13px 15px;border-radius:10px;border:1px solid var(--line);background:#fbfcfe}
.jt-cancel--no{border-style:dashed}
.jt-cancel--done{color:var(--muted);font-weight:700;font-size:13px}
.jt-cancel__row{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
.jt-cancel__why{display:block;margin-top:3px;color:var(--muted);font-weight:700;font-size:12.5px;line-height:1.6}
/* There is NO verified identity until SSO (#66): the acting user is a selector,
   and the screen has to say so. A role check that presents itself as security is
   worse than none — same honesty the audit viewer and the assistant carry. */
.jt-unverified{margin:10px 0 0;padding-top:9px;border-top:1px dashed var(--line);
  color:var(--orange);font-weight:800;font-size:12px;line-height:1.6}

/* Conversion. Counts lead; the percentage may only sit beside them, and there is
   no percentage at all until something has been decided to divide by. */
.jt-conv{padding:16px 20px;margin-bottom:18px}
.jt-conv__nums{display:flex;flex-wrap:wrap;gap:26px;align-items:baseline}
.jt-conv__n{display:flex;flex-direction:column}
.jt-conv__n b{font-size:26px;font-weight:900;line-height:1}
.jt-conv__n i{font-style:normal;font-size:11px;font-weight:800;letter-spacing:.4px;
  text-transform:uppercase;color:var(--muted);margin-top:3px}
.jt-conv__n--won b{color:var(--ok)}
.jt-conv__pct{display:flex;flex-direction:column;margin-left:auto;text-align:right;
  font-size:26px;font-weight:900;color:var(--blue);line-height:1}
.jt-conv__pct i{font-style:normal;font-size:11px;font-weight:800;letter-spacing:.4px;
  text-transform:uppercase;color:var(--muted);margin-top:3px}
.jt-conv__note{margin:11px 0 0;color:var(--muted);font-weight:700;font-size:12.5px;line-height:1.6}

/* ==========================================================================
   LOCATIONS & CONTACTS + the two con note versions  (#249)   prefix: adb-
   --------------------------------------------------------------------------
   THREE STATES, THREE TREATMENTS, and the third is the one that matters.
   NF.contactStale answers `stale:null / known:false` for a contact nobody has
   ever confirmed. That is not "fresh" and not "gone stale" — so it is neither
   green nor amber. It is drawn HATCHED, exactly as .cmp-pill--indet is, because
   a hatch reads as "no answer here" rather than as a grade on a scale. A green
   pill on an unconfirmed number is the failure with the real consequence: a
   driver at a mine gate ringing a phone nobody has checked.
   ========================================================================== */
.adb-chip{display:inline-block;padding:2px 8px;border-radius:6px;font-size:11px;
  font-weight:800;line-height:1.6;white-space:nowrap}
.adb-chip--ok{background:rgba(31,157,85,.12);color:#14663a;border:1px solid rgba(31,157,85,.35)}
.adb-chip--old{background:rgba(245,138,42,.14);color:#8a4a05;border:1px solid rgba(245,138,42,.45)}
/* Never confirmed — hatched, never a flat fill, and never green. */
.adb-chip--never{color:#5b2b00;border:1px dashed var(--red);
  background:repeating-linear-gradient(135deg,rgba(242,60,0,.10) 0 5px,rgba(242,60,0,.02) 5px 10px)}
.adb-chip--arch{background:#eef1f6;color:var(--muted);border:1px solid var(--line)}

.adb-dot{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:7px;vertical-align:-1px}
.adb-dot--ok{background:var(--ok)}
.adb-dot--old{background:var(--orange)}
.adb-dot--never{background:var(--red)}
.adb-dot--none{background:var(--muted-2)}

.adb-actor{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:14px}
.adb-actor label{font-weight:800;font-size:13px;display:flex;align-items:center;gap:8px}
.adb-actor select{padding:8px 11px;border:1px solid var(--line);border-radius:9px;
  font-family:var(--font);font-weight:700;font-size:13px;background:#fff}
.adb-actor span{font-size:12px;font-weight:700;color:var(--muted)}

.adb-vtabs{display:flex;gap:8px;margin-bottom:14px;flex-wrap:wrap}
.adb-vtab{border:1px solid var(--line);background:#fff;border-radius:10px;padding:9px 15px;
  font-family:var(--font);font-weight:800;font-size:13.5px;cursor:pointer;color:var(--muted)}
.adb-vtab.is-on{background:var(--blue);border-color:var(--blue);color:#fff}
.adb-vtab i{font-style:normal;background:var(--orange);color:#fff;border-radius:20px;
  padding:1px 7px;font-size:11px;margin-left:5px}
.adb-vtab.is-on i{background:rgba(255,255,255,.28)}

.adb-filters{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;margin-bottom:14px}
.adb-filters label{display:block}
.adb-filters label span{display:block;font-size:11.5px;font-weight:800;color:var(--muted);margin-bottom:4px}
.adb-filters input,.adb-filters select{padding:9px 11px;border:1px solid var(--line);border-radius:9px;
  font-family:var(--font);font-weight:700;font-size:13.5px;background:#fff;min-width:170px}
.adb-cb{display:flex!important;align-items:center;gap:7px;font-weight:800;font-size:13px;padding-bottom:9px}
.adb-cb input{min-width:0;width:17px;height:17px}
.adb-filters .btn{margin-left:auto}

#adb-rows tbody tr{cursor:pointer}
.adb-site{display:block;color:var(--muted);font-weight:700;font-size:11px}
.adb-geo{font-size:11.5px;font-weight:800;margin-left:4px;white-space:nowrap}
.adb-nophone{color:var(--muted);font-weight:700;font-style:italic;font-size:12.5px}
.adb-by{display:block;color:var(--muted);font-weight:700;font-size:11px;margin-top:2px}
.adb-acts{display:flex;gap:6px;flex-wrap:wrap;white-space:nowrap}

.adb-back{border:1px solid var(--line);background:#fff;border-radius:9px;padding:7px 13px;
  font-family:var(--font);font-weight:800;font-size:13px;cursor:pointer;color:var(--blue);margin-bottom:12px}
.adb-head__t{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:10px}
.adb-head__t h2{font-size:22px}
.adb-note{font-size:12px;font-weight:700;color:var(--muted);line-height:1.55;margin:6px 0 0}
.adb-empty{border-left:4px solid var(--muted-2)}
.adb-empty b{font-size:15px}
.adb-empty p{font-size:13px;font-weight:700;color:var(--muted);line-height:1.6;margin:6px 0 0}

.adb-add{margin-top:18px;padding-top:14px;border-top:1px solid var(--line)}
.adb-add__row{display:flex;gap:9px;flex-wrap:wrap}
.adb-add__row input{flex:1 1 170px;padding:10px 12px;border:1px solid var(--line);border-radius:9px;
  font-family:var(--font);font-weight:700;font-size:14px;background:#fff}

/* The worklist. Names and counts lead; the "confirmed and current" figure sits
   BESIDE them and never in front — same refusal the compliance matrix makes
   about a headline percentage. */
.adb-lead{border-left:4px solid var(--blue);margin-bottom:14px}
.adb-lead p{font-size:13px;font-weight:700;color:var(--muted);margin:0;line-height:1.6}
.adb-tot{display:flex;gap:18px;flex-wrap:wrap;margin-top:10px}
.adb-tot__i{font-size:13px;font-weight:800;display:flex;align-items:center}
.adb-sec{margin-bottom:14px}
.adb-sec--never{border-left:4px solid var(--red)}
.adb-sec--old{border-left:4px solid var(--orange)}

/* ---- picking the pickup/delivery on a consignment ---- */
.adb-job{background:var(--panel-2);border:1px solid var(--line);border-radius:var(--radius);
  padding:15px 16px;margin:18px 0}
.adb-job__hd{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}
.adb-job__hd b{font-size:14.5px}
.adb-job__link{color:var(--blue);font-weight:800;font-size:12.5px}
.adb-job__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:18px;margin-top:12px}
.adb-end{background:#fff;border:1px solid var(--line);border-radius:var(--radius-sm);padding:13px 14px}
.adb-end .fld{margin-bottom:11px}
.adb-end .fld input,.adb-end .fld select,.adb-end .fld textarea{font-size:14px;padding:10px 12px}
.adb-end textarea{width:100%;border:1px solid var(--line);border-radius:9px;font-family:var(--font);
  font-weight:600;font-size:14px;padding:10px 12px;resize:vertical}
.adb-addr{font-size:12.5px;font-weight:700;color:var(--muted);margin:-6px 0 11px}

/* The prompt that stops a 2005 contact being the easy default. */
.adb-prompt{border-radius:var(--radius-sm);padding:10px 12px;margin:0 0 11px;font-size:12.5px;font-weight:700}
.adb-prompt--ok{background:rgba(31,157,85,.08);border:1px solid rgba(31,157,85,.3)}
.adb-prompt--warn{background:rgba(242,60,0,.06);border:1px solid rgba(242,60,0,.35)}
.adb-prompt p{margin:7px 0 0;line-height:1.55;color:var(--ink)}
.adb-prompt__acts{display:flex;gap:8px;flex-wrap:wrap;margin-top:9px}
.adb-snap{background:#fff;border:1px dashed var(--line);border-radius:var(--radius-sm);
  padding:9px 11px;font-size:12.5px;font-weight:700;line-height:1.6}
.adb-snap small{display:block;color:var(--muted);font-size:11px;margin-top:3px}
.adb-snap .adb-chip{margin-left:5px}

/* ---- con note: version switch + the two sheets ---- */
.adb-vsw{display:flex;gap:6px;flex-wrap:wrap}
.adb-vsw__b{border:1px solid var(--line);background:#fff;border-radius:8px;padding:7px 12px;
  font-family:var(--font);font-weight:800;font-size:12.5px;cursor:pointer;color:var(--muted)}
.adb-vsw__b.is-on{background:var(--blue);border-color:var(--blue);color:#fff}
.adb-vsw__note{background:rgba(37,82,164,.07);border:1px solid rgba(37,82,164,.25);
  border-radius:var(--radius-sm);padding:9px 12px;margin:0 auto 12px;max-width:820px;
  font-size:12.5px;font-weight:700;color:var(--blue-d);line-height:1.55}

/* A snapshot that recorded an unconfirmed contact SAYS SO on the sheet. */
.adb-cnwarn{border-radius:6px;padding:5px 8px;margin-top:5px;font-size:10.5px;font-weight:800;line-height:1.45}
.adb-cnwarn--never{background:rgba(242,60,0,.09);border:1px solid rgba(242,60,0,.4);color:#8c2200}
.adb-cnwarn--old{background:rgba(245,138,42,.12);border:1px solid rgba(245,138,42,.45);color:#8a4a05}
.adb-cnwarn--none{background:#f4f6fa;border:1px solid var(--line);color:var(--muted)}
.adb-ins{font-weight:800;line-height:1.5}
.adb-ins--ct{font-weight:700;color:var(--blue-d)}

.adb-x{max-width:820px;margin:14px auto 0;border-left:4px solid var(--orange)}
/* "No price recorded" is not $0 — it must never render like a figure. */
.adb-x-none{color:var(--red)}

.adb-cl{padding:20px 22px}
.adb-cl__hd{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;
  padding-bottom:14px;border-bottom:2px solid var(--ink)}
.adb-cl__brand{display:flex;align-items:center;gap:9px}
.adb-cl__id{text-align:right;font-size:12px;font-weight:700}
.adb-cl__no{font-size:20px;font-weight:900;letter-spacing:.5px}
.adb-cl__to{display:grid;grid-template-columns:2fr 1fr;gap:16px;padding:14px 0;
  border-bottom:1px solid var(--ink);font-size:13px;font-weight:700}
.adb-cl__to b{display:block;font-size:15px;font-weight:900;margin:3px 0}

.adb-lg{padding:18px 20px}
.adb-lg__hd{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;
  padding-bottom:12px;border-bottom:2px solid var(--ink);font-size:12.5px;font-weight:700}
.adb-lg__drv{text-align:right;font-weight:900;font-size:14px}
.adb-lg__drv small{display:block;font-weight:700;color:var(--muted);font-size:11px}
/* The rig (#247) — carried at full ink rather than the muted vehicle styling
   above it: on a printed leg sheet this is the line a driver checks against
   what is actually hooked up before pulling out. */
.adb-lg__drv small.adb-lg__rig{color:var(--ink);font-weight:900;font-size:12px;margin-top:2px}
.adb-lg__ends{display:grid;grid-template-columns:1fr 1fr;gap:14px;padding:14px 0;
  border-bottom:1px solid var(--ink)}
.adb-lg__end{font-size:12.5px;font-weight:700}
.adb-lg__end b{display:block;font-size:14.5px;font-weight:900;margin:3px 0}
.adb-lg__ct{margin-top:6px;font-weight:800}
.adb-lg__ct--none{color:var(--red)}
.adb-lg__ld{margin-top:10px;padding:8px 10px;border-radius:6px;font-size:12px;font-weight:700;
  background:rgba(31,157,85,.08);border:1px solid rgba(31,157,85,.3)}
.adb-lg__ld--stop{background:rgba(242,60,0,.08);border-color:rgba(242,60,0,.4);color:#8c2200}
@media(max-width:640px){
  .adb-cl__to,.adb-lg__ends{grid-template-columns:1fr}
}
/* 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}
}

/* ==========================================================================
   DRIVERS AS A RESOURCE (#263) — `crew-` prefix
   Verified unused in css/app.css, driver/driver.css and portal/portal.css first.
   ========================================================================== */
.crew-hd{padding:18px 20px;margin-bottom:16px}
.crew-sub{margin:0;color:var(--muted);font-weight:700;font-size:13px;line-height:1.65}
.crew-note{margin:0 0 8px;color:var(--muted);font-weight:700;font-size:12.5px;line-height:1.6}
.crew-id{color:var(--muted);font-weight:700}
.crew-dim{color:var(--muted-2,#98a2b3);font-weight:700}
.crew-empty{padding:28px 22px;text-align:center;color:var(--muted);font-weight:700}
.crew-ok{color:var(--ok);font-weight:800;font-size:13px;margin:0}

/* NAMES AND COUNTS, never a percentage — "94% compliant" hides the one driver
   who can't legally do tomorrow's Newman run. Same refusal as the compliance
   matrix and the speed KPI. */
.crew-lead{display:flex;flex-wrap:wrap;gap:14px;margin-top:14px}
.crew-lead__b{flex:1 1 240px;padding:12px 14px;border-radius:10px;border:1px solid var(--line);background:#fbfcfe}
.crew-lead__b.is-bad{border-color:var(--red);background:rgba(242,60,0,.05)}
.crew-lead__b b{display:block;font-size:28px;font-weight:900;line-height:1}
.crew-lead__b.is-bad b{color:var(--red)}
.crew-lead__b i{display:block;font-style:normal;font-size:11px;font-weight:800;letter-spacing:.4px;
  text-transform:uppercase;color:var(--muted);margin:3px 0 5px}
.crew-lead__b span{display:block;font-size:12px;font-weight:700;color:var(--ink);line-height:1.5}

/* A qualification chip carries the status of the TICKET behind it, not just the
   claim. `--indet` is hatched, exactly as .cmp-pill--indet is: held, but nobody
   has recorded an expiry, so currency cannot be confirmed. Telling a scheduler
   somebody is current when nobody knows is the one failure here with a licence
   consequence, so it must never render as the green case. */
.crew-qrow{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:4px}
.crew-q{display:inline-block;margin:0 3px 3px 0;padding:2px 7px;border-radius:5px;
  border:1px solid var(--line);background:#eef2fa;font-size:10.5px;font-weight:900;letter-spacing:.3px}
.crew-q--ok{background:rgba(31,157,85,.12);color:#14663a;border-color:rgba(31,157,85,.35)}
.crew-q--soon{background:rgba(245,138,42,.14);color:#8a4a05;border-color:rgba(245,138,42,.45)}
.crew-q--exp{background:rgba(242,60,0,.12);color:#8f2400;border-color:rgba(242,60,0,.45)}
.crew-q--none{background:transparent;border-style:dashed;color:var(--muted);
  font-weight:800;letter-spacing:0;text-transform:none}
.crew-q--indet{color:#5b2b00;border:1px dashed var(--orange);
  background:repeating-linear-gradient(135deg,rgba(245,138,42,.12) 0 5px,rgba(245,138,42,.02) 5px 10px)}
/* HIRED IN (#260) — we hold NO record for this person, which is neither the
   green of a current ticket nor the red of a lapsed one. Hatched, exactly as
   .crew-q--indet above and .cmp-pill--indet are, because a hatch reads as "no
   answer here" rather than as a grade on a scale. Declared AFTER .crew-q so the
   background and border actually take. */
.crew-q--norec{background:repeating-linear-gradient(135deg,#f4f2ea,#f4f2ea 4px,#eae6d8 4px,#eae6d8 8px);
  border:1px dashed #c9b877;color:#7a6520;font-weight:800;letter-spacing:0;text-transform:none}
.crew-hired{margin-top:18px;padding:16px 18px}

/* Availability, read from the schedule. `idle` is deliberately its own colour
   from `rest`: a rostered rest day is planned, and colouring the two alike is
   what buries the real gaps. */
.crew-av{display:inline-block;padding:2px 8px;border-radius:6px;font-size:11px;font-weight:800;white-space:nowrap}
.crew-av--working{background:rgba(31,157,85,.12);color:#14663a;border:1px solid rgba(31,157,85,.35)}
.crew-av--rest{background:#eef2fa;color:var(--muted);border:1px solid var(--line)}
.crew-av--idle{background:rgba(245,138,42,.14);color:#8a4a05;border:1px solid rgba(245,138,42,.45)}
.crew-av--off{background:rgba(242,60,0,.10);color:#8f2400;border:1px solid rgba(242,60,0,.4)}
.crew-av--nolane{background:transparent;border:1px dashed var(--line);color:var(--muted)}
.crew-big{font-size:15px;font-weight:900;padding:6px 10px;border-radius:8px;display:inline-block;margin:0}

.crew-week{display:grid;grid-template-columns:repeat(auto-fit,minmax(74px,1fr));gap:6px}
.crew-day{padding:8px 6px;border-radius:8px;border:1px solid var(--line);text-align:center;background:#fbfcfe}
.crew-day.is-today{border-color:var(--blue);border-width:2px}
.crew-day span{display:block;font-size:10px;font-weight:900;letter-spacing:.4px;
  text-transform:uppercase;color:var(--muted)}
.crew-day b{display:block;font-size:11.5px;font-weight:800;margin-top:3px}
.crew-day--working{background:rgba(31,157,85,.09)}
.crew-day--idle{background:rgba(245,138,42,.10)}
.crew-day--off{background:rgba(242,60,0,.08)}
.crew-day--rest{background:#eef2fa}

/* Advisories, NOT blocks. The word matters: a scheduler may allocate over any of
   these, which is the settled decision on #263 and the same call the engine
   room's guard makes. */
.crew-adv{display:inline-block;min-width:22px;padding:1px 7px;border-radius:99px;
  text-align:center;font-size:11px;font-weight:900;color:#fff;background:var(--muted)}
.crew-adv--high{background:var(--red)}
.crew-adv--med{background:var(--orange)}
.crew-adv--low{background:var(--muted)}
.crew-advl{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}
.crew-advl li{padding:9px 11px;border-radius:9px;border:1px solid var(--line);background:#fbfcfe}
.crew-advl li b{display:block;font-size:12.5px;font-weight:900}
.crew-advl li span{display:block;margin-top:2px;font-size:12px;font-weight:700;color:var(--muted);line-height:1.55}
.crew-advl--high{border-left:4px solid var(--red)}
.crew-advl--med{border-left:4px solid var(--orange)}
.crew-advl--low{border-left:4px solid var(--line)}

/* ==========================================================================
   RATES IN THE BACK (#259) — the Rates screen, the derivation panel and the
   provenance chips that ride on a quote and an invoice.
   Prefix `rc-`, verified unused in css/app.css, driver/driver.css and
   portal/portal.css before it was added. No bare single-word class is reused:
   `.reco` once collided and rendered a panel sideways.
   ========================================================================== */
/* `.card` carries NO padding of its own — a card whose direct child is a
   `.table` is a scroll container (the `:has(>.table)` rule), so a content card
   has to say it wants padding. Same reason the process card and the job-thread
   card do. A card that holds BOTH prose and a table wraps the table in a bare
   div, which the same rule matches, so the table still handles its own overflow. */
.rc-pad{padding:18px 20px}
.rc-hd{margin-bottom:14px}
.rc-sect{margin:18px 2px 8px}
.rc-note{margin:0;font-size:13.5px;font-weight:700;color:var(--muted);line-height:1.55;max-width:96ch}
.rc-note b{color:var(--ink)}
.rc-note-card{margin-bottom:14px}
.rc-tabs{display:inline-flex;gap:4px;background:#eef2fb;padding:4px;border-radius:10px;margin-bottom:14px}
.rc-vtab{border:none;cursor:pointer;font-family:var(--font);font-weight:800;font-size:13px;
  padding:6px 14px;border-radius:7px;background:transparent;color:var(--muted)}
.rc-vtab.is-on{background:#fff;color:var(--blue);box-shadow:0 1px 2px rgba(20,30,60,.12)}
.rc-cb{display:inline-flex;align-items:center;gap:7px;font-weight:800;font-size:13px;color:var(--muted)}
.rc-addcard{margin-bottom:14px}
.rc-hint{font-size:12.5px;font-weight:700;color:var(--muted);line-height:1.5;margin:10px 0}
.rc-hint b{color:var(--ink)}
.rc-foot{font-size:12.5px;font-weight:700;color:var(--muted);line-height:1.55;margin:12px 2px 0;max-width:96ch}
.rc-foot b{color:var(--ink)}
.rc-empty{text-align:center;color:var(--muted);font-weight:700;padding:26px 12px}
.rc-desc,.rc-effect,.rc-noteCell{font-size:12.5px;font-weight:700;color:var(--muted);line-height:1.5;max-width:46ch}
.rc-effect b{color:var(--ink)}
.rc-rate,.rc-uplift{width:100%;max-width:120px;padding:8px 10px;border:1px solid var(--line);
  border-radius:8px;font-family:var(--font);font-weight:700;font-size:13px}
.rc-retired{opacity:.6}
.rc-std{font-weight:800;color:var(--blue)}
.rc-allin{display:inline-block;font-size:11px;font-weight:800;padding:2px 7px;border-radius:5px;
  background:rgba(37,82,164,.1);color:var(--blue)}
.rc-any{font-size:12px;font-weight:700;color:var(--muted)}
.rc-sample{color:var(--orange);font-weight:800}

/* State of a service tier. `--un` is the one that matters: an uplift nobody has
   given us is neither 0 nor a number, so it is never drawn as either. */
.rc-chip{display:inline-block;font-size:11px;font-weight:800;padding:3px 9px;border-radius:6px;white-space:nowrap}
.rc-chip--ok{background:rgba(31,157,85,.12);color:#15703d}
.rc-chip--base{background:rgba(37,82,164,.1);color:var(--blue)}
.rc-chip--un{background:rgba(242,60,0,.1);color:#a52c00;
  background-image:repeating-linear-gradient(45deg,rgba(242,60,0,.08) 0 5px,transparent 5px 10px)}

/* Where a figure came from. Reused on the derivation panel, a quote's charge
   list and an invoice line, so the same claim reads the same everywhere. */
.rc-src{display:inline-block;font-size:10.5px;font-weight:800;padding:2px 7px;border-radius:5px;white-space:nowrap}
.rc-src--own{background:rgba(37,82,164,.12);color:var(--blue)}
.rc-src--std{background:#eef2fb;color:var(--muted)}
.rc-src--card{background:rgba(245,138,42,.14);color:#8a4a00}
.rc-src--terms{background:rgba(31,157,85,.12);color:#15703d}
.rc-src--tier{background:rgba(252,217,17,.22);color:#6b5300}
.rc-src--ov{background:rgba(245,138,42,.2);color:#8a4a00}
.rc-src--un{background:rgba(242,60,0,.12);color:#a52c00}
.rc-via{display:block;color:var(--muted);font-weight:700;font-size:11.5px;line-height:1.45;margin-top:2px}
.rc-ov{display:inline-block;font-size:11px;font-weight:800;padding:2px 7px;border-radius:5px;margin-top:3px;
  background:rgba(245,138,42,.18);color:#8a4a00}

/* An incomplete price. Full size, never a footnote: a total that is missing a
   charge is the one thing on this screen that costs money. */
.rc-warn{border-left:4px solid var(--red);margin-bottom:14px}
.rc-warn>b{display:block;font-size:14px;line-height:1.5;color:#8c2200}
.rc-warnline{margin:10px 0 0;padding:10px 12px;border-radius:8px;font-size:12.5px;font-weight:700;
  line-height:1.5;background:rgba(242,60,0,.08);border:1px solid rgba(242,60,0,.3);color:#8c2200}
.rc-unlist{margin:10px 0 0;padding-left:20px;font-size:12.5px;font-weight:700;line-height:1.6;color:var(--muted)}
.rc-unlist b{color:var(--ink)}
.rc-unrow{background:rgba(242,60,0,.05)}
.rc-trace{margin:6px 0 0;padding-left:20px;font-size:12.5px;font-weight:700;line-height:1.65;color:var(--muted)}
.rc-trace b{color:var(--ink)}
.rc-tot{margin-top:12px;border-top:1px solid var(--line);padding-top:10px}
.rc-tot>div{display:flex;justify-content:space-between;gap:16px;font-size:13px;font-weight:800;
  color:var(--muted);padding:3px 0}
.rc-tot__t{font-size:16px;font-weight:900;color:var(--ink);border-top:1px solid var(--line);
  margin-top:6px;padding-top:8px}
.rc-sub{color:var(--muted-2);font-weight:700}
.rc-up{color:var(--orange)}
.rc-down{color:#15703d}
.rc-card{margin-top:18px}
.rc-card .card{margin-bottom:12px}
.rc-none{font-size:13px;font-weight:700;color:var(--muted);line-height:1.55;margin:0 0 10px;max-width:90ch}
.rc-actions{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:12px}
.rc-meta{font-size:12px;font-weight:700;color:var(--muted)}
.rc-autobar{display:flex;align-items:flex-end;gap:12px;flex-wrap:wrap;margin-bottom:12px}
/* 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}
@media(max-width:640px){
  .rc-autobar{align-items:stretch}
  .rc-rate,.rc-uplift{max-width:none}
}

/* ---- Record management (NF.Registry) ---------------------------------------
   Behaviour hooks over the existing .card / .table / .btn / .fld / .modal
   vocabulary — there is no separate stylesheet here, the same arrangement the
   LMS `lrn-` classes use. `reg-` was verified unused in all three stylesheets
   before it was taken, and none of these are bare single-word classes: reusing
   one of those silently inherits its layout, which is how a panel once ended up
   as a flex row.
   -------------------------------------------------------------------------- */
.reg-fld{ display:block; margin-bottom:12px }
.reg-help{ display:block; margin-top:5px; color:var(--muted); font-weight:700; font-size:12px; line-height:1.45 }
.reg-help--lock{ color:#8a5a12 }
.reg-id{ margin:0 0 14px; color:var(--muted); font-weight:700; font-size:13px }
.reg-note{ margin:2px 0 0; color:var(--muted-2); font-weight:700; font-size:12px; line-height:1.45 }
/* A refusal from the writer, shown IN the form beside the field it is about —
   a toast slides away while somebody is still reading which field it meant. */
.reg-err{ margin:12px 0 0; padding:10px 12px; border-radius:9px; font-weight:800; font-size:13px;
  color:var(--red); background:rgba(242,60,0,.09); border:1px solid rgba(242,60,0,.28) }
.reg-btns{ white-space:nowrap }
.reg-btns .btn{ margin-left:4px }
.reg-foot{ color:var(--muted); font-weight:700; font-size:13px; margin:10px 2px 0; line-height:1.5 }
/* An archived row stays legible — it is still a record somebody may need to
   read — so this is a muted tint, never a strikethrough or a hidden row. */
.reg-row--arch > td{ background:repeating-linear-gradient(135deg,transparent,transparent 6px,rgba(20,30,60,.035) 6px,rgba(20,30,60,.035) 12px);
  color:var(--muted) }

/* ==========================================================================
   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 }

/* Sessions & attendance (LMS2-E, #235) — a toolbox talk, pre-start briefing or
   VOC, with its attendance roll. `ses-` verified unused in css/app.css,
   driver/driver.css and all three apps' JS before it was added, and deliberately
   NOT a bare single-word class: reusing one of .card/.chk/.act/.list/.detail
   silently inherits its layout, which is how a panel once ended up a flex row.
   Like the `lrn-*` family most of the screen borrows .card/.table/.btn/.cmp-pill;
   only the things below need real rules. */
.ses-note{ margin:0 0 14px; padding:11px 13px; border-radius:10px; background:#f4f7fd;
  border:1px solid var(--line); color:var(--ink); font-weight:700; font-size:12.5px; line-height:1.55 }
.ses-note--sm{ font-size:12px; margin:6px 0 12px }
.ses-err{ margin:8px 0 10px; padding:9px 11px; border-radius:9px; background:rgba(242,60,0,.08);
  border:1px solid rgba(242,60,0,.35); color:#a32900; font-weight:800; font-size:12.5px; line-height:1.5 }
/* THE attestation chip, and the third state is the one with a consequence.
   `--none` is HATCHED, exactly as .cmp-pill--indet is and for the same reason:
   "nobody has confirmed this person was in the room" is neither present nor
   absent, and a green tick there is a claim about attendance we cannot stand
   behind — on the record a client's HSE team and DEMIRS read. A signature and a
   presenter's mark are also kept apart, because they are different evidence. */
.ses-att{ display:inline-block; font-size:11px; font-weight:800; padding:2px 8px; border-radius:20px;
  white-space:nowrap; background:#eef2fb; color:var(--blue) }
.ses-att--sig{ background:rgba(31,157,85,.13); color:#146c3a }
.ses-att--mark{ background:#eef2fb; color:var(--blue) }
.ses-att--none{ background:repeating-linear-gradient(135deg,#f4f2ea,#f4f2ea 5px,#eae6d8 5px,#eae6d8 10px);
  color:#7a6520; border:1px dashed #c9b877 }
/* An outcome is three facts, not two: "not yet competent" is a recorded
   assessment that did not pass, which is a different phone call from nobody
   having assessed them at all — so it is amber, never the same grey as blank. */
.ses-out{ display:inline-block; font-size:11px; font-weight:800; padding:2px 8px; border-radius:20px;
  white-space:nowrap; color:var(--muted) }
.ses-out--competent{ background:rgba(31,157,85,.13); color:#146c3a }
.ses-out--not-yet-competent{ background:rgba(245,138,42,.15); color:#8a4a06 }
.ses-out--attended{ background:#eef2fb; color:var(--blue) }
.ses-form{ padding:16px; margin-bottom:14px }
.ses-form__t{ display:block; font-size:14.5px; margin-bottom:10px }
.ses-grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(230px,1fr)); gap:10px 14px; margin-bottom:12px }
.ses-fld{ display:block; margin-bottom:10px }
.ses-fld > span{ display:block; font-weight:800; font-size:12px; color:var(--muted); margin-bottom:4px }
.ses-fld > span i{ font-style:normal; font-weight:700; color:var(--muted-2) }
.ses-fld input, .ses-fld select, .ses-fld textarea{ width:100%; box-sizing:border-box; padding:8px 10px;
  border:1px solid var(--line); border-radius:9px; font-family:var(--font); font-size:14px; font-weight:700;
  background:#fff; color:var(--ink) }
.ses-roll{ display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:0 14px;
  max-height:220px; overflow:auto; padding:8px 10px; border:1px solid var(--line); border-radius:10px }
.ses-hd{ padding:15px; margin-bottom:12px }
.ses-hd__t{ display:flex; flex-wrap:wrap; align-items:center; gap:9px }
.ses-hd__t b{ font-size:16px }
.ses-hd__m{ margin:7px 0 0; color:var(--muted); font-weight:700; font-size:12.5px }
.ses-hd__n{ margin:8px 0 0; font-weight:700; font-size:12.5px; line-height:1.55 }
.ses-todo{ padding:15px; margin-bottom:12px }
.ses-todo__t{ display:block; font-size:14px; margin-bottom:7px }
.ses-todo__l{ margin:0 0 11px; padding-left:20px; font-weight:700; font-size:12.5px; line-height:1.6 }
.ses-off{ padding:15px; margin-bottom:12px; border-left:4px solid #1f9d55 }
.ses-off p{ margin:6px 0 0; font-weight:700; font-size:12.5px; line-height:1.55 }
.ses-cred{ margin-top:7px; padding:7px 10px; border-radius:8px; background:#f4f7fd;
  font-weight:700; font-size:12.5px }
.ses-add{ padding:14px; margin-top:12px; display:flex; gap:10px; align-items:flex-end; flex-wrap:wrap }
.ses-add .ses-fld{ margin:0; min-width:220px }
.ses-vers{ padding:15px; margin-top:12px }
.ses-vers p{ margin:5px 0 9px; color:var(--muted); font-weight:700; font-size:12px; line-height:1.55 }
.ses-vers__r{ padding:6px 0; border-top:1px solid var(--line); font-weight:700; font-size:12.5px }
/* A signature captured with no signal is HELD, never dropped — and the row says
   so, because "saved" and "saved on this phone" are different claims. */
.ses-inline{ margin-top:3px; color:#8a4a06; font-weight:700; font-size:11px }
.ses-empty{ padding:30px; text-align:center }
.ses-empty__t{ display:block; font-size:15px; margin-bottom:7px }
.ses-empty p{ margin:5px 0 0; color:var(--muted); font-weight:700; font-size:12.5px; line-height:1.55 }

/* ==========================================================================
   REVENUE PIPELINE / MONTH TO TARGET (#261) — the #/pipeline screen.
   Prefix `rvp-`, verified unused in css/app.css, driver/driver.css and
   portal/portal.css before it was added. No bare single-word class is reused:
   `.reco` once collided and rendered a panel sideways.

   ONE RULE RUNS THROUGH ALL OF IT: nothing on this page is drawn as an alarm.
   Ad hoc work is booked a day or two out, so a thin forward book is the normal
   shape of the business and a red gap tile would raise an alarm about the way
   Northfleet trade. The only warning colour here is on `.rvp-warn`, which
   carries the sentence saying a capture rate is NOT a performance measure —
   i.e. it warns against a misreading, never about the number.
   ========================================================================== */
.rvp-pad{padding:18px 20px}
.rvp-head{margin-bottom:14px;border-left:4px solid var(--primary)}
.rvp-h{font-size:22px;margin:0 0 6px}
.rvp-lead{margin:0 0 10px;font-size:13.5px;font-weight:700;color:var(--muted);line-height:1.55;max-width:96ch}
.rvp-lead b{color:var(--ink)}
/* The coverage caveat, FULL SIZE at the top — the same weight Fleet Overview
   gives its "live telemetry covers only the types ticked track live" note, and
   for the same reason: a total read as the whole business is the worst thing
   this page could cause. */
.rvp-cov{margin:0 0 10px;padding:11px 13px;border-radius:9px;background:#eef2fb;
  font-size:13px;font-weight:700;color:#2a3c60;line-height:1.6;max-width:100ch}
.rvp-cov b{color:var(--ink)}
.rvp-adhoc{margin:0;font-size:12.5px;font-weight:700;color:var(--muted);line-height:1.6;max-width:100ch}
.rvp-kpis{margin-bottom:12px}
.rvp-sect{margin:22px 2px 8px}
.rvp-sub{color:var(--muted-2);font-weight:700}
.rvp-line{margin:0 2px 14px;font-size:12.5px;font-weight:700;color:var(--muted);line-height:1.55;max-width:100ch}
.rvp-note{margin:8px 0 0;font-size:12.5px;font-weight:700;color:var(--muted);line-height:1.6;max-width:100ch}
.rvp-note b{color:var(--ink)}
.rvp-foot{font-size:12.5px;font-weight:700;color:var(--muted);line-height:1.55;margin:10px 2px 0;max-width:100ch}
.rvp-empty{text-align:center;color:var(--muted);font-weight:700;padding:26px 12px}

/* A figure nobody could value. A DASH, never $0 — hatched where it is a chip,
   exactly as .cmp-pill--indet and .rc-chip--un are, because "we could not price
   this" is a third answer and must not read as either a number or a nil. */
.rvp-dash{color:var(--muted-2);font-weight:900}
.rvp-nodata{color:var(--muted-2)}
.rvp-kpi--none .kpi__value{color:var(--muted-2)}
.rvp-bas{display:inline-block;font-size:10.5px;font-weight:800;padding:2px 7px;border-radius:5px;white-space:nowrap}
.rvp-bas--inv{background:rgba(31,157,85,.12);color:#15703d}
.rvp-bas--acc{background:rgba(37,82,164,.12);color:var(--blue)}
.rvp-bas--qte{background:#eef2fb;color:var(--muted)}
.rvp-bas--un{background:rgba(122,101,32,.08);color:#7a6520;border:1px dashed #c9b877;
  background-image:repeating-linear-gradient(135deg,rgba(201,184,119,.18) 0 5px,transparent 5px 10px)}
.rvp-undated{font-size:12px;font-weight:700;color:var(--muted-2);font-style:italic}
.rvp-runout{display:block;color:#8a4a00;font-weight:800;font-size:11px;margin-top:2px}
.rvp-amt{font-weight:900}
.rvp-why{font-size:12px;font-weight:700;color:var(--muted);line-height:1.5;max-width:52ch}

/* The target, or the refusal where the target would be. Both are full-size
   panels: with no target on file this page states nothing about the month, and
   saying that in a footnote would let the empty gap tile read as "on track". */
.rvp-target{margin:0 0 16px}
.rvp-target--none{border-left:4px solid var(--muted-2);
  background-image:repeating-linear-gradient(135deg,rgba(20,30,60,.02) 0 6px,transparent 6px 12px)}
.rvp-tgt{margin:0;font-size:14px;font-weight:700;line-height:1.55;max-width:100ch}
.rvp-target code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;
  background:#eef2fb;padding:1px 5px;border-radius:4px}

.rvp-2{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:14px;margin-bottom:4px}
.rvp-aside{border-left:4px solid var(--orange)}
.rvp-big{margin:2px 0 0;font-size:22px;font-weight:900;line-height:1.2}
.rvp-big small{display:block;margin-top:3px;font-size:12px;font-weight:800;color:var(--muted)}

/* The weighting, spelled out. The percentage NEVER appears on its own: it is
   always the share of a named number of decided quotes over a named window,
   because a bare "34%" on a revenue screen is a number nobody can check. */
.rvp-rate{margin:0;font-size:14px;font-weight:700;color:var(--muted);line-height:1.6;max-width:100ch}
.rvp-rate b{color:var(--ink);font-size:16px}
.rvp-rate small{display:block;margin-top:4px;font-size:12px;font-weight:700;color:var(--muted-2);line-height:1.55}
/* Warns against a MISREADING, not about a number — see the header note. */
.rvp-warn{margin:12px 0 0;padding:11px 13px;border-radius:9px;font-size:12.5px;font-weight:700;line-height:1.6;
  background:rgba(252,217,17,.14);border:1px solid rgba(252,217,17,.5);color:#6b5300;max-width:100ch}

/* ==========================================================================
   #236 REPORT 9 — CLIENT-SITE READINESS. `cmp-sr` verified unused in
   css/app.css, driver/driver.css and all three apps' JS before it was added.

   THREE VERDICTS, NOT TWO, and the third is the one with a licence
   consequence. `--ready` is cleared, `--no` is turned away, and
   `--indet` is cleared ON A TICKET WHOSE CURRENCY NOBODY HAS RECORDED — which
   is HATCHED, exactly as .cmp-pill--indet, .cmp-ros--unknown and
   .cmp-pk-chip--unavailable are, and for the same reason. `indeterminate` rides
   on `compliant` in NF.Compliance, so a flat green "Clear to attend" there
   tells a scheduler somebody is good for a mine gate when nobody knows whether
   the ticket has lapsed. Never collapse these three into two.
   ========================================================================== */
.cmp-sr-pill{display:inline-block;font-size:11px;font-weight:800;padding:2px 9px;border-radius:999px;
  white-space:nowrap}
.cmp-sr-pill--ready{background:#e4f4ea;color:#186c37}
.cmp-sr-pill--no{background:#fdeceb;color:#a11c0c}
.cmp-sr-pill--indet{background:repeating-linear-gradient(135deg,#f4f2ea,#f4f2ea 5px,#eae6d8 5px,#eae6d8 10px);
  color:#7a6520;border:1px dashed #c9b877}
/* The half-connected `appliesTo.sites` axis, stated in the report rather than
   only in a comment: this cannot say who NORMALLY works at a site. */
/* THE AXIS CAVEAT IS A HEADLINE, NOT A FOOTNOTE, and it is placed ABOVE the
   table for the same reason Fleet Overview's coverage caveat and .rvp-cov are:
   the screen is headed "Who can work at <site>" and prints "12 of 40 people
   would be turned away at the gate", and the sentence that stops a scheduler
   reading that as "the Newman crew" cannot be grey 12.5px type AFTER a 23-row
   table. Body-copy weight, full width, at the top — the convention this repo
   already states three times (the speed screen and the evidence pack both "say
   so in full rather than in a footnote"). */
.cmp-sr-axis{margin:0 0 12px;padding:12px 14px;border-radius:9px;border-left:4px solid var(--orange);
  background:#fff8ec;border-top:1px solid #f6dcb4;border-right:1px solid #f6dcb4;border-bottom:1px solid #f6dcb4;
  font-size:13px;font-weight:700;color:#5c4412;line-height:1.6;max-width:100ch}
.cmp-sr-axis b{color:var(--ink)}
.cmp-sr-ok{margin-top:12px}
.cmp-sr-ready li span{color:#186c37}
.cmp-sr-ind{color:#7a6520;font-weight:800}


/* ==========================================================================
   #236 REPORT 8 — THE ATTENDANCE REGISTER. `ses-ar` verified unused in
   css/app.css, driver/driver.css and all three apps' JS before it was added.
   Behaviour hooks and a date field; the attestation chips are the register's
   own .ses-att--sig / --mark / --none, reused deliberately so the report and
   the session record cannot draw the same attestation two different ways —
   including the hatched "Not confirmed", which is neither present nor absent.
   ========================================================================== */
.ses-ar-fld{display:flex;flex-direction:column;gap:4px}
.ses-ar-fld span{font-weight:800;font-size:11px;color:var(--muted);
  text-transform:uppercase;letter-spacing:.4px}
.ses-ar-fld input{padding:7px 9px;border:1px solid var(--line);border-radius:8px;
  font:inherit;font-weight:700;font-size:12.5px;background:#fff}
.ses-ar-sub{margin-top:2px;color:var(--muted);font-weight:600;font-size:11.5px}
.ses-ar-dim{color:var(--muted);font-weight:700}
/* Same claim .ses-inline makes on the record: a signature on a device is
   recorded, not yet on file, and the register must not print it as filed. */
.ses-ar-inline{display:block;margin-top:3px;color:#8a4a06;font-weight:700;font-size:10.5px}


/* A QUESTION NEVER ASKED IS NOT A QUESTION AT 0% (#236 report 6). The analysis
   rows come off recorded answers, so an authored question no attempt has drawn
   produces no row — normal, not exceptional, once `bankDraw` is in play. These
   name the remainder rather than letting the table read as the whole bank, the
   same refusal primeNote() and utScopeNote() make about a bounded scan. */
.qz-bank b{color:var(--ink)}
.qz-never{padding:16px 18px;margin-top:12px;border-left:4px solid var(--orange)}
.qz-never__t{display:block;font-size:13.5px;margin-bottom:4px}
.qz-neverl{margin:9px 0 0;padding-left:20px;font-weight:700;font-size:12.5px;line-height:1.7}
.qz-never__c{color:var(--muted);font-weight:700;font-size:11.5px}

/* ==========================================================================
   CON NOTE SPLITS — CN‹n›A / B / C …   (#281)
   --------------------------------------------------------------------------
   Prefix `spl-`, collision-checked against this file, driver/driver.css and
   portal/portal.css before it was written (0 hits in all three). Everything
   else is borrowed — .card, .badge, .btn, .section-title, .leg-pill, and
   .reg-err / .reg-foot for the writer's own refusal, which is the shape
   NF.Registry established and the one the LMS and the compliance screens reuse.

   No bare single-word class is reused: .card, .list, .detail, .board and the
   rest already carry layout in this file, and inheriting one silently is how a
   panel once became a flex row.

   The card is bordered in ORANGE rather than the components card's blue, because
   a split and a component are different decompositions of the same con note and
   the two panels sit one above the other on the consignment.
   ========================================================================== */
.spl-card{margin-top:20px;border-left:4px solid var(--orange)}
.spl-sub{font-size:12.5px;font-weight:700;color:var(--muted);margin-bottom:12px;line-height:1.55}
.spl-sub code{font-size:11.5px;background:#eef2fb;color:var(--blue);padding:1px 5px;border-radius:5px;font-weight:800}
.spl-sub a{color:var(--blue);font-weight:800}
.spl-none{color:var(--muted-2);font-weight:700}

/* THE TWO DISCLOSURES, at body-copy weight and ABOVE the list of parts.
   `.cmp-sr-axis` shipped as 12.5px muted small print a thousand pixels below the
   number it qualified and had to be corrected into a full panel above the table;
   this starts where that ended up. One PO producing N invoices, and a family
   counting as N+1 con notes, are both things a scheduler needs before they
   split — not a footnote they find afterwards. */
.spl-warn{margin:0 0 10px;padding:11px 13px;border-radius:10px;font-size:13px;font-weight:800;
  line-height:1.5;color:#7a4300;background:rgba(245,138,42,.10);border:1px solid rgba(245,138,42,.30)}
.spl-warn--count{color:var(--muted);background:rgba(37,82,164,.06);border-color:rgba(37,82,164,.20)}

.spl-po{margin:0 0 12px;font-size:13px;font-weight:700;color:var(--ink)}
.spl-po b{font-weight:800}
.spl-src{display:block;margin-top:3px;color:var(--muted-2);font-weight:700;font-size:11.5px;line-height:1.45}

.spl-list{display:flex;flex-direction:column;gap:8px;margin:0 0 12px}
.spl-row{display:flex;align-items:center;gap:12px;padding:10px 12px;border:1px solid var(--line);
  border-radius:11px;background:#fff}
.spl-row--cur{border-color:var(--orange);background:rgba(245,138,42,.05)}
/* A WITHDRAWN PART STAYS LEGIBLE — it is still a record somebody may need to
   read — so this is the same muted hatch .reg-row--arch uses for an archived
   master record, never a strikethrough and never a hidden row. */
.spl-row--void{background:repeating-linear-gradient(135deg,transparent,transparent 6px,rgba(20,30,60,.035) 6px,rgba(20,30,60,.035) 12px);
  color:var(--muted)}
.spl-l{flex:0 0 30px;height:30px;border-radius:9px;display:flex;align-items:center;justify-content:center;
  background:#eef2fb;color:var(--blue);font-weight:800;font-size:14px}
.spl-row--void .spl-l{background:rgba(20,30,60,.06);color:var(--muted)}
.spl-b{flex:1;min-width:0}
.spl-id{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-weight:800;font-size:13.5px}
.spl-id a{color:var(--blue)}
.spl-meta{margin-top:3px;color:var(--muted);font-weight:700;font-size:12px}

.spl-form{display:flex;align-items:flex-end;gap:10px;flex-wrap:wrap;margin-top:4px}
.spl-lab{display:flex;flex-direction:column;gap:4px;font-size:11.5px;font-weight:800;
  text-transform:uppercase;letter-spacing:.3px;color:var(--muted)}
.spl-lab input{padding:9px 11px;border:1px solid var(--line);border-radius:9px;font:inherit;
  font-weight:700;font-size:13px;min-width:120px}
.spl-lab input[type=number]{max-width:110px}
.spl-void{margin:0 0 12px;padding:10px 12px;border-radius:9px;font-size:12.5px;font-weight:700;
  line-height:1.5;color:var(--muted);background:rgba(20,30,60,.04);border:1px solid var(--line)}
