/* ============================================================================
   trakzo.css — redesign design-kit (Teztrak)
   Tokens + components extracted from the approved mockups
   (design-screens/login.html et al.). Dark-first with a light variant.

   SAFETY: every rule is scoped under `body.trakzo` (and tokens are only APPLIED
   inside that scope), so this file cannot affect any legacy screen that does not
   opt in. Loaded via the new trakzo_* layouts only. Never built by gulp.
   ============================================================================ */

:root{
  --bg:#090A0C; --panel:#101318; --raised:#171B21; --hover:#1E232B; --line:#252B34; --line-strong:#343C48;
  --fg:#F2F4F7; --fg2:#98A1AE; --fg3:#616A77;
  --lime:#3D9CF0; --lime-s:rgba(61,156,240,.14); /* Signal Azure — one accent everywhere (2026-09-05) */ --teal:#2BD9A8; --coral:#FF5470;
  --amber:#FFB93E; --violet:#9C6BFF; --slate:#5A6479;
  --g1:rgba(61,156,240,.26); --g2:rgba(43,217,168,.21); --g3:rgba(43,217,168,.10);
  --dot:rgba(255,255,255,.045); --ink:255,255,255;
  --r:9px; --r2:18px; --r3:16px;
  --sans:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,monospace;
}
html[data-theme="light"]{
  --bg:#F1F4F8; --panel:#FFFFFF; --raised:#F4F7FB; --hover:#E9EFF6; --line:#DFE6EE; --line-strong:#C3CEDC;
  --fg:#0E1218; --fg2:#4E5967; --fg3:#7C8695;
  --lime:#1F76D2; --lime-s:rgba(31,118,210,.12); /* Signal Azure (light) */ --teal:#0E9E76; --coral:#DA2E50;
  --amber:#B87503; --violet:#6C43C9; --slate:#7A8698;
  --g1:rgba(31,118,210,.42); --g2:rgba(43,217,168,.30); --g3:rgba(31,118,210,.16);
  --dot:rgba(16,24,40,.05); --ink:16,24,40;
}

/* THE VEHICLE GLYPH (2026-09-25) — one definition for every place a vehicle is
   drawn: Objects list cards, the device panel header, Events rows, Today
   Activity, the admin rail's Objects item and the admin Objects dialogs. A
   front-on car in the same 1.8px round-cap outline as every other icon here
   (the geometry of Lucide's car-front, ISC licence). It was drawn in two
   different shapes across six rules; they all read this token now. */
:root{--tz-vehicle-glyph:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'><path d='m21 8-2 2-1.5-3.7A2 2 0 0 0 15.65 5H8.4a2 2 0 0 0-1.9 1.26L5 10 3 8'/><rect x='3' y='10' width='18' height='8' rx='2'/><path d='M7 14h.01M17 14h.01M5 18v2M19 18v2'/></svg>")}

/* ---------- reset within scope ---------- */
body.trakzo{background:var(--bg);color:var(--fg);font:14px/1.5 var(--sans);
  -webkit-font-smoothing:antialiased;margin:0}
body.trakzo *{box-sizing:border-box}
/* height:auto + aspect-ratio instead of a fixed 16px height: ~38 call sites set
   `style="width:Npx"` inline, which overrode the width only and left the height
   pinned at 16 -- a 15px icon rendered 15x16 and an 18px one 18x16. Every symbol
   uses a square 0 0 24 24 viewBox, so deriving the height keeps them all square
   whatever width the call site asks for. */
body.trakzo svg.i{width:16px;height:auto;aspect-ratio:1;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;flex:none}
body.trakzo a{color:var(--fg2);text-decoration:none}
body.trakzo a:hover{color:var(--lime)}

/* ============================================================================
   AUTH SCREENS (login / register / forgot / reset / confirm)
   ============================================================================ */
body.trakzo.trakzo-auth{height:100vh;display:grid;place-items:center;padding:24px;overflow:hidden}

/* backdrop: quiet road network + brand-colour trips (pure CSS/SVG) */
body.trakzo .bgfx{position:fixed;inset:0;z-index:0;pointer-events:none;background:var(--bg)}
body.trakzo .bgfx .wash{position:absolute;inset:0;
  background:radial-gradient(52% 48% at 12% 18%, var(--g1) 0%, transparent 64%),
             radial-gradient(48% 44% at 88% 82%, var(--g2) 0%, transparent 62%)}
body.trakzo .bgfx svg{position:absolute;inset:0;width:100%;height:100%}
body.trakzo .bgfx .rd{fill:none;stroke:rgba(var(--ink),.085);stroke-width:2;stroke-linecap:round}
body.trakzo .bgfx .nd{fill:rgba(var(--ink),.20)}
body.trakzo .bgfx .tr{fill:none;stroke-width:2.4;stroke-linecap:round;stroke-dasharray:170 1420;
  animation:trakzo-run 14s linear infinite}
@keyframes trakzo-run{to{stroke-dashoffset:-1590}}
@media (prefers-reduced-motion:reduce){body.trakzo .bgfx .tr{animation:none;stroke-dasharray:none;opacity:.5}}
body.trakzo .grain{position:fixed;inset:0;z-index:1;pointer-events:none;opacity:.15;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E")}
html[data-theme="light"] body.trakzo .grain{opacity:.08}

/* theme toggle */
body.trakzo .thm{position:fixed;top:22px;right:22px;z-index:60;display:flex;align-items:center;gap:7px;
  height:36px;padding:0 14px;border:1px solid var(--line);border-radius:99px;background:var(--panel);
  color:var(--fg2);cursor:pointer;font:12.5px var(--sans);font-weight:560}
body.trakzo .thm:hover{color:var(--fg);border-color:var(--lime)}

/* auth card (scoped to auth screens so its fixed width does not leak to page cards) */
body.trakzo.trakzo-auth .card{position:relative;z-index:2;width:416px;max-width:100%;background:var(--panel);
  border-radius:var(--r2);box-shadow:0 30px 80px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,.05);
  overflow:hidden;animation:trakzo-rise .5s cubic-bezier(.22,1,.36,1) both}
html[data-theme="light"] body.trakzo.trakzo-auth .card{box-shadow:0 24px 60px rgba(20,35,60,.14), 0 0 0 1px rgba(16,24,40,.06)}
@keyframes trakzo-rise{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}
body.trakzo.trakzo-auth .card:before{content:"";position:absolute;top:0;left:0;right:0;height:3px;
  background:linear-gradient(90deg,transparent,var(--lime) 24%,var(--teal) 76%,transparent)}
body.trakzo.trakzo-auth .card .inner{padding:36px 38px 30px}
body.trakzo .crest{display:flex;justify-content:center;margin-bottom:20px}
body.trakzo .crest .well{width:98px;height:98px;border-radius:27px;background:var(--raised);
  display:grid;place-items:center;box-shadow:inset 0 0 0 1px rgba(255,255,255,.055)}
html[data-theme="light"] body.trakzo .crest .well{box-shadow:inset 0 0 0 1px rgba(16,24,40,.07)}
body.trakzo .crest .well img{height:76px;width:auto;display:block;max-width:100%}
body.trakzo .title{text-align:center;margin-bottom:26px}
body.trakzo .title h1{font-size:24px;font-weight:700;letter-spacing:-.03em;line-height:1.15}
body.trakzo .title p{font-size:13px;color:var(--fg3);margin-top:6px}

/* alerts (map Laravel flash → these) */
body.trakzo .alert{display:flex;align-items:flex-start;gap:9px;border-radius:var(--r);padding:11px 13px;
  font-size:13px;margin-bottom:16px;background:rgba(255,84,112,.12);color:var(--coral);border:0}
body.trakzo .alert.ok,body.trakzo .alert-success{background:rgba(43,217,168,.12);color:var(--teal)}
body.trakzo .alert-danger{background:rgba(255,84,112,.12);color:var(--coral)}

/* fields */
body.trakzo .fld{margin-bottom:14px}
body.trakzo .fld label{display:block;font-size:12px;color:var(--fg2);font-weight:600;margin-bottom:7px}
body.trakzo .ipt{position:relative}
body.trakzo .ipt .ic{position:absolute;left:13px;top:50%;transform:translateY(-50%);color:var(--fg3);transition:color .18s}
/* styles BOTH the design's inputs and Laravel Form:: .form-control output */
body.trakzo .ipt input,
body.trakzo .ipt .form-control{width:100%;background:var(--raised);border:1.5px solid transparent;
  border-radius:var(--r);color:var(--fg);font:14.5px var(--sans);padding:13px 13px 13px 41px;
  transition:border-color .18s,background .18s,box-shadow .18s;height:auto;box-shadow:none}
body.trakzo .ipt input::placeholder,body.trakzo .ipt .form-control::placeholder{color:var(--fg3)}
body.trakzo .ipt input:hover,body.trakzo .ipt .form-control:hover{background:var(--hover)}
body.trakzo .ipt input:focus,body.trakzo .ipt .form-control:focus{outline:none;border-color:var(--lime);
  background:var(--bg);box-shadow:0 0 0 4px color-mix(in srgb, var(--lime) 14%, transparent)}
html[data-theme="light"] body.trakzo .ipt input:focus,
html[data-theme="light"] body.trakzo .ipt .form-control:focus{background:#fff;box-shadow:0 0 0 4px rgba(95,138,8,.13)}
body.trakzo .ipt:focus-within .ic{color:var(--lime)}
body.trakzo .ipt .eye{position:absolute;right:7px;top:50%;transform:translateY(-50%);width:31px;height:31px;
  border:0;border-radius:7px;background:transparent;color:var(--fg3);cursor:pointer;display:grid;place-items:center}
body.trakzo .ipt .eye:hover{color:var(--fg);background:var(--hover)}

/* remember row + checkbox */
body.trakzo .rowx{display:flex;align-items:center;justify-content:space-between;margin:18px 0 22px}
body.trakzo .chk{display:flex;align-items:center;gap:9px;cursor:pointer;font-size:13px;color:var(--fg2);user-select:none;margin:0}
body.trakzo .chk input{position:absolute;opacity:0;width:0;height:0}
body.trakzo .chk .box{width:19px;height:19px;border-radius:6px;background:var(--raised);border:1.5px solid var(--line);
  display:grid;place-items:center;flex:none;transition:.16s}
body.trakzo .chk .box svg{width:11px;stroke-width:3.2;color:#fff;opacity:0;transition:.16s}
body.trakzo .chk input:checked + .box{background:var(--lime);border-color:var(--lime)}
body.trakzo .chk input:checked + .box svg{opacity:1}
body.trakzo .chk input:focus-visible + .box{outline:2px solid var(--lime);outline-offset:2px}
body.trakzo .lnk{font-size:13px;color:var(--fg2);font-weight:530}
body.trakzo .lnk:hover{color:var(--lime)}

/* primary button */
body.trakzo .go{position:relative;width:100%;border:0;border-radius:var(--r);background:var(--lime);
  color:#fff;font:15.5px var(--sans);font-weight:750;letter-spacing:-.012em;padding:14px 0;cursor:pointer;
  overflow:hidden;display:flex;align-items:center;justify-content:center;gap:9px;transition:filter .16s,transform .08s}
body.trakzo .go svg{transition:transform .22s cubic-bezier(.22,1,.36,1)}
body.trakzo .go:hover{filter:brightness(1.09)} body.trakzo .go:hover svg{transform:translateX(4px)}
body.trakzo .go:active{transform:translateY(1px)}

/* secondary (register/forgot as full-width) */
body.trakzo .go-2{display:flex;align-items:center;justify-content:center;width:100%;border:1px solid var(--line);
  border-radius:var(--r);background:var(--raised);color:var(--fg);font:14px var(--sans);font-weight:600;
  padding:12px 0;cursor:pointer;margin-top:10px;transition:.16s}
body.trakzo .go-2:hover{border-color:var(--lime);color:var(--fg)}

/* footer + links */
body.trakzo .links{margin-top:20px;padding-top:17px;border-top:1px solid var(--line);text-align:center;
  font-size:13px;color:var(--fg3)}
body.trakzo .links a{color:var(--fg2);font-weight:600}
body.trakzo .links a:hover{color:var(--lime)}
body.trakzo .foot{padding:15px 38px 20px;border-top:1px solid var(--line);text-align:center;
  font:11px var(--mono);letter-spacing:.06em;color:var(--fg3)}

/* external (Azure) methods */
body.trakzo .ext{margin-top:16px;text-align:center}
body.trakzo .ext h3{font-size:12px;color:var(--fg3);font-weight:600;margin:0 0 10px;text-transform:uppercase;letter-spacing:.06em}
body.trakzo .ext img{max-height:38px;width:auto;border-radius:8px}

/* app store links */
body.trakzo .applinks{display:flex;gap:10px;justify-content:center;margin-top:16px}
body.trakzo .applinks img{height:38px;width:auto}

/* captcha (whatever the include renders, keep it tidy) */
body.trakzo .fld img{max-width:100%;border-radius:var(--r)}

/* validation errors (error_for / help-block / text-danger) */
body.trakzo .help-block,body.trakzo .text-danger,body.trakzo .invalid-feedback{
  color:var(--coral);font-size:12px;margin-top:6px;display:block}
body.trakzo .has-error .form-control{border-color:var(--coral)}

/* ============================================================================
   MODAL / DIALOG SHELL (settings and other dialogs)
   Standalone-page variant: renders centered over a blurred backdrop.
   ============================================================================ */
body.trakzo.trakzo-modal-page{height:100vh;overflow:hidden}
body.trakzo .behind{position:fixed;inset:0;padding:16px;filter:blur(1px);opacity:.3;pointer-events:none}
body.trakzo .behind .b1{height:54px;background:var(--panel);border-radius:var(--r2);margin-bottom:8px}
body.trakzo .behind .b2{height:calc(100% - 78px);background:var(--panel);border-radius:var(--r2)}
body.trakzo .scrim{position:fixed;inset:0;background:rgba(4,5,6,.72)}
html[data-theme="light"] body.trakzo .scrim{background:rgba(20,28,40,.42)}

body.trakzo .modal-wrap{position:fixed;inset:0;z-index:50;display:grid;place-items:center;padding:24px}
body.trakzo .dlg{width:900px;max-width:100%;max-height:100%;background:var(--panel);border-radius:var(--r3);
  display:flex;flex-direction:column;overflow:hidden;
  box-shadow:0 40px 100px rgba(0,0,0,.6),0 0 0 1px rgba(255,255,255,.05);
  /* fill-mode is `forwards`, NOT `both`. `both` includes the BACKWARDS fill, which
     applies the `from` keyframe's transform before the animation runs — and inside
     the map's Setup child modal the dialog is inserted while still display:none, so
     the animation never starts and that transform is held indefinitely. A
     transformed ancestor becomes the containing block for every position:fixed
     descendant, which is how the row action menus (`data-position="fixed"`, placed
     by app.js from the trigger's viewport offset) ended up displaced off-screen and
     the gear looked dead. `forwards` keeps the identical animation but leaves the
     element untransformed before it starts and after it ends. */
  animation:trakzo-pop .28s cubic-bezier(.22,1,.36,1) forwards}
html[data-theme="light"] body.trakzo .dlg{box-shadow:0 30px 80px rgba(18,32,54,.24),0 0 0 1px rgba(16,24,40,.07)}
/* Opacity only — deliberately no `transform`.

   Any transform on .dlg, even briefly, makes it the containing block for its
   position:fixed descendants. The row action menus in Setup's list panes are
   placed by app.js as position:fixed using the trigger's VIEWPORT offset
   (`data-position="fixed"`), so a transformed .dlg silently re-bases those
   coordinates and the menu lands off-screen — the gear then looks dead. It was
   not enough to change the fill-mode: inside the map's Setup child modal the
   dialog is inserted while display:none, so the animation sits at 0% and the
   `from` keyframe's transform is held indefinitely. Removing transform from the
   keyframes is the only version that cannot regress. The old app has no .dlg
   wrapper at all, which is why the gear always worked there.
   Trade-off: the dialog fades in without the 10px rise / .99 scale. */
@keyframes trakzo-pop{from{opacity:0}to{opacity:1}}
body.trakzo .mh{display:flex;align-items:center;gap:11px;padding:17px 20px 15px;flex:none}
body.trakzo .mh h2{font-size:17px;font-weight:700;letter-spacing:-.02em}
body.trakzo .mh p{font-size:12.5px;color:var(--fg3);margin-top:1px}
body.trakzo .mh .x{margin-left:auto;width:32px;height:32px;border:0;border-radius:var(--r);background:transparent;color:var(--fg3);cursor:pointer;display:grid;place-items:center;text-decoration:none}
body.trakzo .mh .x:hover{background:var(--hover);color:var(--fg)}
body.trakzo .mf{display:flex;align-items:center;gap:9px;padding:14px 20px;box-shadow:inset 0 1px 0 var(--line);flex:none}
body.trakzo .mf .sp{flex:1}

body.trakzo .btn{display:inline-flex;align-items:center;gap:7px;border:0;border-radius:var(--r);padding:10px 18px;
  cursor:pointer;font:13.5px var(--sans);font-weight:650;text-decoration:none}
body.trakzo .btn.pri{background:var(--lime);color:#fff}
body.trakzo .btn.pri:hover{filter:brightness(1.08)}
body.trakzo .btn.gh{background:var(--raised);color:var(--fg2)}
body.trakzo .btn.gh:hover{background:var(--hover);color:var(--fg)}
body.trakzo .btn.xs{padding:6px 11px;font-size:12px}

/* split: side-nav + body */
body.trakzo .split{display:grid;grid-template-columns:200px 1fr;flex:1;min-height:0}
body.trakzo .snav{background:var(--raised);padding:10px 8px;overflow-y:auto}
body.trakzo .snav a,body.trakzo .snav button{width:100%;text-align:left;border:0;background:transparent;color:var(--fg2);
  font:13px var(--sans);font-weight:530;padding:9px 11px;border-radius:var(--r);cursor:pointer;display:flex;
  align-items:center;gap:9px;margin-bottom:2px;text-decoration:none}
body.trakzo .snav a:hover,body.trakzo .snav button:hover{background:var(--hover);color:var(--fg)}
body.trakzo .snav a.on,body.trakzo .snav button.on{background:var(--panel);color:var(--fg);font-weight:640;box-shadow:inset 0 0 0 1px var(--line)}
body.trakzo .snav a.on svg,body.trakzo .snav button.on svg{color:var(--lime)}
body.trakzo .snav .grp{font-size:10.5px;font-weight:700;color:var(--fg3);letter-spacing:.05em;padding:11px 11px 5px}
body.trakzo .mb{overflow-y:auto;padding:18px 20px 22px;min-height:0}
body.trakzo .pane{display:none}
body.trakzo .pane.on{display:block}
/* the settings form fills the dialog so the body scrolls and the footer pins */
body.trakzo .dlg .setform{display:flex;flex-direction:column;flex:1;min-height:0}

body.trakzo .sect{font-size:11.5px;font-weight:700;color:var(--fg3);letter-spacing:.04em;margin:20px 0 11px;text-transform:uppercase}
body.trakzo .sect:first-child{margin-top:0}

/* form fields inside the modal: style BOTH design .f and Laravel form-group */
body.trakzo .mb .form-group{margin-bottom:14px}
body.trakzo .mb label{display:block;font-size:12.5px;color:var(--fg2);font-weight:550;margin-bottom:6px}
body.trakzo .mb .form-control,
body.trakzo .mb input[type=text],body.trakzo .mb input[type=password],
body.trakzo .mb select,body.trakzo .mb textarea{width:100%;background:var(--raised);border:1.5px solid transparent;
  border-radius:var(--r);color:var(--fg);font:13.5px var(--sans);padding:10px 11px;transition:.16s;height:auto;box-shadow:none}
body.trakzo .mb textarea{min-height:74px;resize:vertical}
body.trakzo .mb .form-control:hover,body.trakzo .mb select:hover,body.trakzo .mb textarea:hover{background:var(--hover)}
body.trakzo .mb .form-control:focus,body.trakzo .mb select:focus,body.trakzo .mb textarea:focus{outline:none;
  border-color:var(--lime);background:var(--bg);box-shadow:0 0 0 3px color-mix(in srgb, var(--lime) 12%, transparent)}
html[data-theme="light"] body.trakzo .mb .form-control:focus,
html[data-theme="light"] body.trakzo .mb select:focus,
html[data-theme="light"] body.trakzo .mb textarea:focus{background:#fff}
body.trakzo .mb small{font-size:11.5px;color:var(--fg3);display:block;margin-top:5px}
body.trakzo .mb hr{border:0;border-top:1px solid var(--line);margin:18px 0}
body.trakzo .mb h4,body.trakzo .mb h5{font-size:13px;font-weight:700;margin:16px 0 10px}

/* checkboxes inside the modal (Bootstrap .checkbox / .checkbox-inline) */
body.trakzo .mb .checkbox,body.trakzo .mb .checkbox-inline{display:flex;align-items:center;gap:9px;padding:8px 10px;
  background:var(--raised);border-radius:var(--r);cursor:pointer;font-size:13px;margin:0 0 8px}
body.trakzo .mb .checkbox label,body.trakzo .mb .checkbox-inline label{margin:0;font-weight:530;color:var(--fg)}
body.trakzo .mb input[type=checkbox]{width:17px;height:17px;accent-color:var(--lime);flex:none;margin:0}
body.trakzo .mb .dashboard-widget{display:flex;align-items:center;justify-content:space-between;background:var(--raised);
  border-radius:var(--r);padding:8px 10px;margin-bottom:8px}
body.trakzo .mb .dashboard-widget .checkbox-inline{background:transparent;padding:0}
body.trakzo .mb .alert-info{background:var(--lime-s);color:var(--fg2);border:0;border-radius:var(--r);padding:11px 13px;font-size:12.5px}

/* data tables inside modal list-tabs */
body.trakzo table.lt{width:100%;border-collapse:collapse;font-size:13.5px}
body.trakzo table.lt th{text-align:left;font-size:11.5px;font-weight:650;color:var(--fg3);padding:9px 10px;box-shadow:inset 0 -1px 0 var(--line)}
body.trakzo table.lt td{padding:10px;box-shadow:inset 0 -1px 0 var(--line)}
body.trakzo .empty{text-align:center;color:var(--fg3);font-size:13px;padding:28px 0}

/* ============================================================================
   FULL-PAGE SHELL (trakzo_page): top bar + scrolling content
   ============================================================================ */
body.trakzo.trakzo-page{overflow:auto}
body.trakzo .top{position:sticky;top:0;z-index:30;display:flex;align-items:center;gap:16px;height:56px;padding:0 18px;
  background:var(--panel);box-shadow:inset 0 -1px 0 var(--line)}
body.trakzo .top .brand{display:flex;align-items:center;gap:10px;font-weight:750;letter-spacing:-.02em;color:var(--fg);text-decoration:none}
body.trakzo .top .brand .mk{width:32px;height:32px;border-radius:9px;background:var(--lime);color:#fff;display:grid;place-items:center;font-weight:800;font-size:12px}
body.trakzo .top .brand small{display:block;font-size:9px;letter-spacing:.14em;color:var(--fg3);font-weight:700}
body.trakzo .top nav{display:flex;gap:2px;margin-left:8px}
body.trakzo .top nav a{display:flex;align-items:center;gap:7px;color:var(--fg2);font-size:13px;font-weight:560;padding:8px 12px;border-radius:var(--r);text-decoration:none}
body.trakzo .top nav a:hover{background:var(--hover);color:var(--fg)}
/* The standalone pages now carry real navigation, so the bar holds more items.
   Keep each label on one line and let the bar scroll rather than wrap to two rows. */
body.trakzo .top nav a{white-space:nowrap}
body.trakzo .top nav a.on{background:var(--lime-s);color:var(--lime)}
body.trakzo .top{overflow-x:auto;scrollbar-width:none}
body.trakzo .top::-webkit-scrollbar{display:none}
body.trakzo .top nav{flex:0 0 auto}
body.trakzo .top .sp{flex:1}
/* .thm floats top-right on auth/modal pages; inside the top bar it is a normal
   flex item so it stays vertically centered in the bar (no overhang) */
body.trakzo .top .thm{position:static;top:auto;right:auto;z-index:auto;flex:none}

body.trakzo .page{max-width:1180px;margin:0 auto;padding:22px 20px 40px}
body.trakzo .page > .card{background:var(--panel);border-radius:var(--r3);box-shadow:0 0 0 1px var(--line);overflow:hidden}
body.trakzo .ph{display:flex;align-items:flex-start;gap:14px;padding:20px 20px 16px}
body.trakzo .ph h1{font-size:21px;font-weight:750;letter-spacing:-.025em}
body.trakzo .ph p{font-size:13px;color:var(--fg3);margin-top:2px}
body.trakzo .page .mb{padding:16px 20px 20px}

/* list header + summary + progress bars (maintenance and similar list pages) */
body.trakzo .lhead{display:flex;align-items:center;gap:9px;margin-bottom:12px}
body.trakzo .lhead .sp{flex:1}
body.trakzo .lhead .sect{margin:0}
body.trakzo .summary{font:12px var(--mono);color:var(--fg3)}
body.trakzo .wrap{overflow-x:auto}
body.trakzo .note{display:flex;gap:9px;background:rgba(255,84,112,.10);color:var(--coral);border-radius:var(--r2);
  padding:11px 13px;font-size:12.5px;margin-bottom:14px;align-items:flex-start}
body.trakzo .bar{height:6px;border-radius:3px;background:var(--raised);overflow:hidden;min-width:74px}
body.trakzo .bar i{display:block;height:100%;border-radius:3px}
body.trakzo .bar.ok i{background:var(--teal)} body.trakzo .bar.warn i{background:var(--amber)} body.trakzo .bar.bad i{background:var(--coral)}
body.trakzo .pct{font:10.5px var(--mono);color:var(--fg3);margin-top:4px;display:block}
body.trakzo .left{font-family:var(--mono);font-size:12.5px}
body.trakzo .left.exp{color:var(--coral);font-weight:650}
body.trakzo td.dash{color:var(--fg3);text-align:center}
body.trakzo .acts{display:flex;gap:4px;justify-content:flex-end}
body.trakzo .acts a,body.trakzo .acts button{width:28px;height:28px;border:0;border-radius:var(--r);background:transparent;
  color:var(--fg3);cursor:pointer;display:grid;place-items:center;text-decoration:none}
body.trakzo .acts a:hover,body.trakzo .acts button:hover{background:var(--raised);color:var(--lime)}
body.trakzo .acts a.dg:hover,body.trakzo .acts button.dg:hover{color:var(--coral)}

/* ============================================================================
   APP PLUGIN WIDGETS (bootstrap-select etc.) — the app JS transforms plain
   <select>s into these; style them to match since we don't load the theme CSS.
   ============================================================================ */
body.trakzo .bootstrap-select{position:relative;display:block;width:100%}
/* keep the original <select> in the DOM (for form submit) but visually hidden,
   the way bootstrap-select's own CSS does */
body.trakzo .bootstrap-select > select{position:absolute!important;bottom:0;left:50%;display:block!important;
  width:.5px!important;height:100%!important;padding:0!important;opacity:0!important;border:0!important;z-index:0!important;background:transparent!important;box-shadow:none!important}
/* the wrapper also gets .form-control from the plugin — neutralize that so the
   inner button carries the field styling, not the wrapper */
body.trakzo .bootstrap-select.form-control{padding:0;border:0;background:transparent;height:auto;box-shadow:none}
body.trakzo .bootstrap-select > .btn,body.trakzo .bootstrap-select > .dropdown-toggle{
  width:100%;text-align:left;background:var(--raised);border:1.5px solid transparent;border-radius:var(--r);
  color:var(--fg);font:13.5px var(--sans);padding:10px 11px;display:flex;align-items:center;gap:8px;box-shadow:none;cursor:pointer}
body.trakzo .bootstrap-select > .btn:hover{background:var(--hover)}
/* `.open` is NOT `focused` — see the FOCUS OWNERSHIP block in trakzo-map.css.
   This used to paint the trigger lime purely because the panel was expanded,
   which made the trigger and the focused search field read as two focused
   controls at once. Open now only darkens the surface. */
body.trakzo .bootstrap-select.open > .btn{background:var(--bg)}
body.trakzo .bootstrap-select .filter-option{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
body.trakzo .bootstrap-select .bs-caret{margin-left:auto}
body.trakzo .bootstrap-select .caret{width:0;height:0;border-left:4px solid transparent;border-right:4px solid transparent;
  border-top:5px solid var(--fg3)}
body.trakzo .bootstrap-select .dropdown-menu{display:none;position:absolute;top:100%;left:0;z-index:100;min-width:100%;
  margin-top:4px;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:0 12px 30px rgba(0,0,0,.4);list-style:none;padding:6px;max-height:260px;overflow:auto}
body.trakzo .bootstrap-select.open .dropdown-menu{display:block}
body.trakzo .bootstrap-select .dropdown-menu li{list-style:none}
body.trakzo .bootstrap-select .dropdown-menu li a{text-decoration:none;cursor:pointer}
/* (rows, hover and selected: assets/css/trakzo-dropdown.css) */
/* bootstrap-select nests an inner <ul.dropdown-menu.inner> that holds the options;
   the generic .dropdown-menu{position:absolute} above collapses it to one row, so
   reset the INNER list to a normal scrollable in-flow list. */
body.trakzo .bootstrap-select .dropdown-menu.inner{position:static;display:block;float:none;width:auto;min-width:0;
  margin:0;padding:0;border:0;box-shadow:none;background:transparent;max-height:240px;overflow-y:auto}
body.trakzo .bootstrap-select .dropdown-menu.inner > li{display:block}
body.trakzo .bootstrap-select .dropdown-menu .bs-searchbox input{width:100%}
/* (the search field and the empty state: assets/css/trakzo-dropdown.css) */
/* generic BS3 dropdown menus (My account, dashboard block Options, etc.) opened by the app.
   The theme CSS that hides these until open isn't loaded, so replicate BS3's toggle here. */
body.trakzo .dropdown,body.trakzo .btn-group{position:relative}
/* behaviour only — BS3's own show/hide, which the theme CSS would normally
   supply. The panel's looks are the component's (assets/css/trakzo-dropdown.css) */
body.trakzo .dropdown-menu{display:none;position:absolute;top:100%;left:0;z-index:1000;min-width:180px;list-style:none}
body.trakzo .dropdown-menu.dropdown-menu-right{left:auto;right:0}
body.trakzo .open > .dropdown-menu,body.trakzo .dropdown.open .dropdown-menu,body.trakzo .btn-group.open .dropdown-menu{display:block}
body.trakzo .dropdown-menu > li > a{text-decoration:none}
/* dashboard block Options panel content (radios/checkboxes inside the dropdown) */
body.trakzo .dropdown-menu label{color:var(--fg2);font-size:12.5px;display:flex;align-items:center;gap:7px;padding:4px 8px;margin:0}
body.trakzo .dropdown-menu input[type=radio],body.trakzo .dropdown-menu input[type=checkbox]{accent-color:var(--lime)}

/* small BS3 float utils (theme CSS not loaded) */
body.trakzo .pull-left{float:left}
body.trakzo .pull-right{float:right}
body.trakzo .clearfix:after,body.trakzo .panel-title:after,body.trakzo .row:after{content:"";display:table;clear:both}

/* ============================================================================
   DASHBOARD — page + block grid + block frame (all 7 blocks share Blocks/layout)
   ============================================================================ */
body.trakzo #dashboard{padding:22px 20px 40px;max-width:1400px;margin:0 auto}
body.trakzo .dash-head{margin-bottom:16px}
body.trakzo .dash-head h1{font-size:21px;font-weight:750;letter-spacing:-.025em}
body.trakzo #dashboard_blocks{display:grid;grid-template-columns:repeat(12,1fr);gap:16px}
body.trakzo .dashboard-block{grid-column:span 3;min-width:0}
body.trakzo .dashboard-block.auto-height,
body.trakzo .dashboard-block[class*="col-md-12"],
body.trakzo .dashboard-block[class*="col-lg-12"]{grid-column:1 / -1}
body.trakzo .dashboard-block[class*="col-md-6"]{grid-column:span 6}
@media (max-width:1200px){ body.trakzo .dashboard-block{grid-column:span 4} }
@media (max-width:992px){ body.trakzo .dashboard-block{grid-column:span 6} }
@media (max-width:768px){ body.trakzo .dashboard-block{grid-column:1 / -1} }

body.trakzo .dashboard-block .panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--r2);overflow:hidden;height:100%;margin:0}
body.trakzo .dashboard-block .panel-heading{padding:12px 14px;box-shadow:inset 0 -1px 0 var(--line);background:transparent}
body.trakzo .dashboard-block .panel-title{font-size:13px;font-weight:650;color:var(--fg);margin:0}
body.trakzo .dashboard-block .panel-title a{color:var(--fg);text-decoration:none}
body.trakzo .dashboard-block .panel-title .icon{margin-right:6px;color:var(--lime)}
body.trakzo .dashboard-block .panel-body,body.trakzo .dashboard-block .dashboard-content{padding:14px;color:var(--fg2)}
body.trakzo .dashboard-block .panel-body b,body.trakzo .dashboard-block .panel-body strong{color:var(--fg)}
body.trakzo .dashboard-block table{width:100%;font-size:13px}
body.trakzo .dashboard-block table td,body.trakzo .dashboard-block table th{padding:7px 8px;box-shadow:inset 0 -1px 0 var(--line)}
body.trakzo .dashboard-block .btn.icon.options{color:var(--fg3);cursor:pointer}

/* dashboard block CONTENT (loaded via block_content) — style the pieces the block
   templates emit so numbers are visible, tiles lay out, and bars render on dark */
/* invisible count fix: templates hardcode inline `color:#000` on the count <b> */
body.trakzo .dashboard-content [style*="color:#000"],
body.trakzo .dashboard-content [style*="color: #000"],
body.trakzo .dashboard-block b[style*="#000"]{color:var(--fg) !important}
/* intra-block BS3 grid → flex (theme grid not loaded), so col-* don't collapse */
body.trakzo .dashboard-content .row{display:flex;flex-wrap:wrap;margin:0 -6px}
body.trakzo .dashboard-content .row > [class*="col-"]{padding:0 6px;flex:1 1 auto;min-width:0}
body.trakzo .dashboard-content .row-status{display:flex;flex-wrap:wrap;gap:8px;margin:0}
body.trakzo .dashboard-content .row-status > [class*="col-"],
body.trakzo .dashboard-content .row-status > .statuss{flex:1 1 110px;padding:0;min-width:0}
/* status tiles (colored boxes keep their inline status bg) */
body.trakzo .stat-box{display:block;border-radius:var(--r2);padding:12px 10px;text-align:center;color:#fff;text-decoration:none}
body.trakzo .stat-box .count{font-size:22px;font-weight:800;line-height:1.1}
body.trakzo .stat-box .title{font-size:11px;font-weight:600;opacity:.95;margin-top:2px}
/* progress bars (maintenance uses .bar; blocks/reports use BS3 .progress) */
body.trakzo .progress{background:var(--raised);border-radius:99px;height:10px;overflow:hidden;margin:0}
body.trakzo .progress-bar{height:100%;background:var(--lime);color:#fff;font-size:10px;line-height:10px;text-align:center}
body.trakzo .progress-bar-success{background:var(--teal)}
body.trakzo .progress-bar-warning{background:var(--amber)}
body.trakzo .progress-bar-danger{background:var(--coral)}

/* modalmanager (jQuery plugin) positioning/state classes the theme normally provides */
body.trakzo.modal-open{overflow:hidden}
body.trakzo .modal-scrollable{position:fixed;inset:0;z-index:1045;overflow:auto}
body.trakzo .modal .contents{display:block}
/* neutralize the plugin's old top:50% centering assumption; we center via margin */
body.trakzo .modal.modal-overflow .modal-dialog,body.trakzo .modal .modal-dialog{top:auto}
/* tabs + grid inside app modals */
body.trakzo .modal-body .tab-content{margin-top:12px}
body.trakzo .modal-body .tab-pane{display:none}
body.trakzo .modal-body .tab-pane.active{display:block}
body.trakzo .modal-body .row{display:flex;flex-wrap:wrap;margin:0 -6px}
body.trakzo .modal-body .row > [class*="col-"]{padding:0 6px;flex:1 1 auto;min-width:0}

/* ============================================================================
   DASHBOARD v2 — modern custom design (demo-dashboard.html). Blocks keep their
   real data; content templates render the design markup + inline-SVG charts.
   ============================================================================ */
body.trakzo #dashboard .n,body.trakzo .dash-num{font-family:var(--mono);font-variant-numeric:tabular-nums}
/* modern block grid: Blocks/layout renders .dashboard-block > .panel */
body.trakzo #dashboard_blocks{display:grid;grid-template-columns:repeat(12,1fr);gap:12px;align-items:stretch}
body.trakzo #dashboard .dashboard-block{min-width:0}
body.trakzo #dashboard .dashboard-block{grid-column:span 3}
body.trakzo #dashboard .dashboard-block[class*="col-md-12"],
body.trakzo #dashboard .dashboard-block[class*="col-lg-12"],
body.trakzo #dashboard .dashboard-block.auto-height{grid-column:1 / -1}
@media (max-width:1200px){ body.trakzo #dashboard .dashboard-block{grid-column:span 6} }
@media (max-width:760px){ body.trakzo #dashboard .dashboard-block{grid-column:1 / -1} }
/* the block card */
body.trakzo #dashboard .dashboard-block .panel{background:var(--panel);border:0;border-radius:var(--r2);
  display:flex;flex-direction:column;min-height:0;height:100%;box-shadow:none;overflow:hidden}
body.trakzo #dashboard .panel-heading{padding:14px 16px 12px;background:transparent;box-shadow:none;border:0}
body.trakzo #dashboard .panel-title{font-size:13.5px;font-weight:650;letter-spacing:-.01em;color:var(--fg);margin:0;position:relative;min-height:20px}
body.trakzo #dashboard .panel-title a{color:var(--fg);text-decoration:none}
body.trakzo #dashboard .panel-title .icon{color:var(--lime);margin-right:7px}
/* Title Case for multi-word block/section titles (Device Overview, Distance Travelled, ...) */
body.trakzo #dashboard .panel-title,
body.trakzo #dashboard .ovh,
body.trakzo #dashboard .ovbox .link,
body.trakzo #dashboard .ovh .link{text-transform:capitalize}
body.trakzo #dashboard .panel-title .pull-left{float:none;display:block;padding-right:34px}
body.trakzo #dashboard .panel-title .pull-right{position:absolute;right:0;top:-3px;margin:0}
body.trakzo #dashboard .btn.icon.options,body.trakzo #dashboard .btn.icon.filter{color:var(--fg3);cursor:pointer;width:28px;height:28px;
  display:inline-grid;place-items:center;border-radius:var(--r);font-size:15px}
body.trakzo #dashboard .btn.icon.options:hover,body.trakzo #dashboard .btn.icon.filter:hover{background:var(--hover);color:var(--fg)}
body.trakzo #dashboard .panel-body,body.trakzo #dashboard .dashboard-content{padding:0 16px 16px;flex:1}

/* mini table (Alerts/Devices/Tasks) */
body.trakzo #dashboard table.table{width:100%;border-collapse:collapse;font-size:13.5px;margin:0}
body.trakzo #dashboard table.table td{padding:8px 0;box-shadow:inset 0 -1px 0 var(--line);border:0;color:var(--fg2)}
body.trakzo #dashboard table.table tr:last-child td{box-shadow:none}
body.trakzo #dashboard table.table td.text-right,body.trakzo #dashboard table.table .text-right{text-align:right;font-family:var(--mono);color:var(--fg)}
body.trakzo #dashboard table.table td.text-right b,body.trakzo #dashboard table.table b{color:var(--fg);font-weight:650}
body.trakzo #dashboard table.table a{color:var(--fg) !important;text-decoration:none}
body.trakzo #dashboard table.table a b{color:var(--fg) !important}
/* Alerts count. The list is as long as the user has alert types, so it is capped
   — otherwise one busy account sets the height of the entire top row of the
   grid. The cap is on the .tz-scroll INSIDE the block, not on the block's own
   padding box: capping the padding box cut the last row in half against the card
   edge with no scrollbar in sight, which was reported as a rendering fault.
   .tz-scroll fades its bottom edge so the cut reads as deliberate. */
body.trakzo #dashboard #block_latest_events .tz-scroll{max-height:236px}
/* donut sits vertically centered in its (stretched) equal-height card */
body.trakzo #dashboard #block_device_activity .dashboard-content{display:flex;align-items:center}
body.trakzo #dashboard .ev{display:flex;align-items:center;gap:10px;padding:9px 0;box-shadow:inset 0 -1px 0 var(--line)}
body.trakzo #dashboard .ev:last-child{box-shadow:none}
body.trakzo #dashboard .ev .ic{width:26px;height:26px;border-radius:8px;display:grid;place-items:center;flex:none}
body.trakzo #dashboard .ev .ic svg{width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
body.trakzo #dashboard .ev .t{font-size:13px;font-weight:560;color:var(--fg)}
body.trakzo #dashboard .ev .c{margin-left:auto;font-family:var(--mono);font-weight:650;color:var(--fg2);font-size:12.5px;white-space:nowrap}

/* KPI tiles (over_view) — dark cards, colored mono number, left accent bar */
body.trakzo #dashboard .row-status{display:grid;grid-template-columns:repeat(6,1fr);gap:10px;margin:0;align-content:start;grid-auto-rows:min-content}
@media (max-width:1200px){ body.trakzo #dashboard .row-status{grid-template-columns:repeat(3,1fr)} }
@media (max-width:600px){ body.trakzo #dashboard .row-status{grid-template-columns:repeat(2,1fr)} }
body.trakzo #dashboard .row-status .statuss{padding:0;min-width:0;align-self:start}
body.trakzo #dashboard .row-status .stat-box{position:relative;overflow:hidden;background:var(--raised);border-radius:var(--r2);
  padding:14px 16px;cursor:pointer;display:flex;flex-direction:column;justify-content:center;gap:6px;line-height:1;
  text-align:left;text-decoration:none;color:var(--fg);height:84px}
body.trakzo #dashboard .row-status .stat-box:hover{background:var(--hover);opacity:1;filter:none}
body.trakzo #dashboard .row-status .stat-box:after{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--fg3)}
body.trakzo #dashboard .row-status .stat-box .count{font:29px/1 var(--mono);font-weight:600;letter-spacing:-.045em;color:var(--fg);text-align:left}
body.trakzo #dashboard .row-status .stat-box .title{font-size:12.5px;color:var(--fg3);margin-top:0;font-weight:500;text-align:left}
body.trakzo #dashboard .row-status .stat-box .link{display:none}
body.trakzo #dashboard .stat-box.mv .count{color:var(--teal)}   body.trakzo #dashboard .stat-box.mv:after{background:var(--teal)}
body.trakzo #dashboard .stat-box.id .count{color:var(--amber)}  body.trakzo #dashboard .stat-box.id:after{background:var(--amber)}
body.trakzo #dashboard .stat-box.st .count{color:var(--coral)}  body.trakzo #dashboard .stat-box.st:after{background:var(--coral)}
body.trakzo #dashboard .stat-box.of .count{color:var(--slate)}  body.trakzo #dashboard .stat-box.of:after{background:var(--slate)}
body.trakzo #dashboard .stat-box.in .count{color:var(--violet)} body.trakzo #dashboard .stat-box.in:after{background:var(--violet)}

/* Device overview — solid colored boxes + split (events | pie) */
body.trakzo #dashboard .ovboxes{display:grid;grid-template-columns:repeat(6,1fr);gap:9px;margin:0 0 16px}
body.trakzo #dashboard .ovbox{border-radius:var(--r);padding:11px 12px;color:#12151A;text-decoration:none;display:block}
body.trakzo #dashboard .ovbox:hover{filter:brightness(1.05)}
body.trakzo #dashboard .ovbox .title{font-size:11.5px;font-weight:650;opacity:.78;color:#12151A}
body.trakzo #dashboard .ovbox .count{font-size:23px;font-weight:700;line-height:1.2;color:#12151A;font-family:var(--mono)}
body.trakzo #dashboard .ovbox .link{font-size:10.5px;font-weight:600;opacity:.72;text-decoration:underline;color:#12151A;display:inline-block;margin-top:2px}
body.trakzo #dashboard .ovsplit{display:grid;grid-template-columns:1fr 1fr;gap:12px;align-items:stretch}
/* Recent events and the pie each sit in their own bordered sub-panel (like the
   original app), so they read as two separate cards, not one flat block. */
body.trakzo #dashboard .ovsplit > div{background:var(--bg);border-radius:var(--r2);padding:14px 16px;box-shadow:inset 0 0 0 1px var(--line)}
html[data-theme="light"] body.trakzo #dashboard .ovsplit > div{background:var(--raised)}
@media (max-width:980px){ body.trakzo #dashboard .ovboxes{grid-template-columns:repeat(3,1fr)} body.trakzo #dashboard .ovsplit{grid-template-columns:1fr} }
body.trakzo #dashboard .ovh{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:600;color:var(--fg2);margin-bottom:8px}
body.trakzo #dashboard .ovh .sp{flex:1}
body.trakzo #dashboard .ovh a,body.trakzo #dashboard .ovh .link{color:var(--lime);text-decoration:none;font-size:11.5px}
/* recent-events table + transparent inner panel */
  padding:5px 11px;border-radius:99px;background:var(--lime-s);color:var(--lime);border:1px solid transparent;
  text-decoration:none;font-size:11.5px;font-weight:650;white-space:nowrap;line-height:1}
body.trakzo #dashboard #block_device_overview table.table-list,body.trakzo #dashboard .ovtbl{width:100%;border-collapse:collapse;font-size:12.5px;border:0}
body.trakzo #dashboard #block_device_overview table.table-list th,body.trakzo #dashboard .ovtbl th{text-align:left;font-size:11px;font-weight:600;color:var(--fg3);padding:6px 8px;box-shadow:inset 0 -1px 0 var(--line);border:0;background:transparent}
body.trakzo #dashboard #block_device_overview table.table-list td,body.trakzo #dashboard .ovtbl td{padding:6px 8px;box-shadow:inset 0 -1px 0 var(--line);border:0;color:var(--fg2)}
body.trakzo #dashboard #block_device_overview table.table-list .no-data{text-align:center;color:var(--fg3)}
/* ============================================================================
   CHARTS — the three Chart.js canvases and the legend they share (2026-09-24)

   A <canvas> has no intrinsic size: Chart.js measures the element's PARENT and
   then writes width/height onto the canvas itself in device pixels. So every
   chart sits in a .tz-canvas box whose size is set HERE, and the canvas is
   forced to fill it with !important — otherwise Chart.js's own inline pixel
   sizes win and the chart stops being responsive.

   Colours are NOT set here. Chart.js paints pixels, so the chart reads the
   tokens itself through assets/js/trakzo-charts.js. This file owns the box; that
   file owns the ink. Do not try to restyle a drawn chart from CSS.
   ============================================================================ */
body.trakzo #dashboard .tz-canvas{position:relative;min-width:0}
body.trakzo #dashboard .tz-canvas > canvas{display:block;width:100% !important;height:100% !important}
body.trakzo #dashboard .tz-canvas-donut{width:134px;height:134px;flex:none}
body.trakzo #dashboard .tz-canvas-ovpie{width:170px;max-width:100%;height:170px;flex:none}
body.trakzo #dashboard .tz-canvas-line{height:218px}

/* ---- the legend, one definition for all three charts ----
   Built as real elements by trakzoLegend() rather than drawn on the canvas, so
   it can be styled here and so a click can hide a series — which is the only
   way to read the distance chart when ten vehicles overlap. `.off` is that
   hidden state. */
body.trakzo #dashboard .tzl{display:flex;flex-direction:column;gap:7px;min-width:0}
body.trakzo #dashboard .tzl-item{display:flex;align-items:center;gap:9px;min-width:0;
  font-size:12.5px;color:var(--fg2);cursor:pointer;user-select:none;
  border-radius:8px;transition:color .14s ease,opacity .14s ease}
body.trakzo #dashboard .tzl-item:hover{color:var(--fg)}
body.trakzo #dashboard .tzl-item.off{opacity:.4}
body.trakzo #dashboard .tzl-item.off .tzl-name{text-decoration:line-through}
body.trakzo #dashboard .tzl-item > i{width:9px;height:9px;border-radius:3px;flex:none}
body.trakzo #dashboard .tzl-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
body.trakzo #dashboard .tzl-val{margin-left:auto;padding-left:16px;font-family:var(--mono);font-weight:650;color:var(--fg)}
/* the distance chart's legend runs along the bottom instead of down the side */
body.trakzo #dashboard .tzl-row{flex-direction:row;flex-wrap:wrap;gap:7px 16px;margin-top:14px}
body.trakzo #dashboard .tzl-row .tzl-item{font-size:12px}
body.trakzo #dashboard .tzl-row .tzl-item > i{width:14px;height:3px;border-radius:2px}
body.trakzo #dashboard .tzl-row .tzl-name{flex:none}

/* ---- Devices (percents): donut beside its legend ---- */
body.trakzo #dashboard .trk-donut{display:flex;align-items:center;gap:20px;flex-wrap:wrap}
body.trakzo #dashboard .trk-legend{flex:1;min-width:120px}

/* ---- Device overview: donut beside its legend, centred in the taller column ---- */
body.trakzo #dashboard .ovsplit > div:last-child{display:flex;flex-direction:column}
body.trakzo #dashboard .ovsplit > div:last-child .ovpie{flex:1}
body.trakzo #dashboard .ovpie{display:flex;align-items:center;justify-content:center;gap:30px;flex-wrap:wrap}
body.trakzo #dashboard .ovpie .lg{font-size:13px;min-width:172px;gap:9px}
body.trakzo #dashboard .ovpie .lg .tzl-item > i{width:10px;height:10px}

/* ============================================================================
   STAT ROWS — one component, four blocks (2026-09-24)

   Alerts Count, Devices, Tasks and anything else that is "a label, a number,
   and that number's share of a total" all used to render as a bare two-column
   table: a word on the left, a digit on the right, repeated. Nothing on the
   card told you whether 11 was most of the fleet or a corner of it, so every
   row had to be read and compared by hand.

   These rows carry that comparison in a bar. The bar is not decoration — its
   width is the row's value over the block's own total, so the shape of the card
   IS the shape of the data.

   Markup (the block templates write it):
       <div class="tz-stats">
         <a class="tz-stat" style="--_c:VALUE">      (a <div> when there is no link)
           <span class="tz-stat-k">Online</span>
           <span class="tz-stat-v">4</span>
           <span class="tz-stat-bar"><i style="width:25%"></i></span>
         </a>
       </div>

   --_c is the row's accent. Set it on the row; everything else follows.
   ============================================================================ */
body.trakzo #dashboard .tz-stats{display:flex;flex-direction:column;gap:2px}
body.trakzo #dashboard .tz-stat{--_c:var(--slate);
  display:grid;grid-template-columns:1fr auto;grid-template-areas:"k v" "bar bar";
  gap:7px 12px;padding:9px 8px 10px;margin:0 -8px;border-radius:10px;
  text-decoration:none;color:inherit;transition:background .14s ease}
body.trakzo #dashboard a.tz-stat{cursor:pointer}
body.trakzo #dashboard a.tz-stat:hover{background:var(--hover)}
body.trakzo #dashboard .tz-stat-k{grid-area:k;font-size:13px;font-weight:500;color:var(--fg2);
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
body.trakzo #dashboard a.tz-stat:hover .tz-stat-k{color:var(--fg)}
body.trakzo #dashboard .tz-stat-v{grid-area:v;font:650 14px/1 var(--mono);font-variant-numeric:tabular-nums;
  letter-spacing:-.02em;color:var(--fg);white-space:nowrap}
/* a row whose value is zero should not shout; it is the absence of a thing */
body.trakzo #dashboard .tz-stat.is-zero .tz-stat-v{color:var(--fg2);font-weight:500;opacity:.75}
/* the unit after a count ("Times") is a caption, not part of the number */
body.trakzo #dashboard .tz-stat-v small{font:500 10.5px/1 var(--sans);color:var(--fg3);
  margin-left:3px;letter-spacing:.03em;text-transform:lowercase}

/* the track is always drawn, so the rows line up whether or not there is a bar
   in them — an empty track reads as "none", a missing one reads as broken */
body.trakzo #dashboard .tz-stat-bar{grid-area:bar;height:4px;border-radius:99px;
  background:color-mix(in srgb, var(--fg3) 16%, transparent);overflow:hidden}
body.trakzo #dashboard .tz-stat-bar > i{display:block;height:100%;border-radius:99px;
  background:linear-gradient(90deg, color-mix(in srgb, var(--_c) 62%, transparent), var(--_c));
  min-width:0;transition:width .5s cubic-bezier(.22,1,.36,1)}
@media (prefers-reduced-motion:reduce){
  body.trakzo #dashboard .tz-stat-bar > i{transition:none}
}

/* ---- the headline number a block's rows are shares OF ---- */
body.trakzo #dashboard .tz-kpi{display:flex;align-items:baseline;gap:9px;
  padding:2px 0 14px;margin-bottom:10px;box-shadow:inset 0 -1px 0 var(--tz-hair)}
body.trakzo #dashboard .tz-kpi-v{font:600 34px/1 var(--mono);letter-spacing:-.045em;color:var(--fg)}
body.trakzo #dashboard .tz-kpi-k{font-size:11.5px;font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;color:var(--fg3)}

/* ---- a short secondary list under the rows (Tasks: the five most recent) ---- */
body.trakzo #dashboard .tz-sub{margin-top:14px;padding-top:12px;box-shadow:inset 0 1px 0 var(--tz-hair)}
body.trakzo #dashboard .tz-sub-h{font-size:10.5px;font-weight:650;letter-spacing:.07em;
  text-transform:uppercase;color:var(--fg3);margin-bottom:7px}
body.trakzo #dashboard .tz-sub-row{display:flex;align-items:center;gap:12px;padding:6px 0;font-size:12.5px}
body.trakzo #dashboard .tz-sub-k{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;color:var(--fg2)}
body.trakzo #dashboard .tz-sub-v{color:var(--fg3);font-size:11.5px;white-space:nowrap}

/* ---- "nothing happened in this period" ----
   Blocks that count events have periods with no events in them, and that is
   ordinary, not an error. Said once, quietly, instead of a column of zeroes. */
body.trakzo #dashboard .tz-none{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:5px;text-align:center;padding:26px 12px;color:var(--fg3)}
body.trakzo #dashboard .tz-none-badge{width:38px;height:38px;border-radius:50%;display:grid;place-items:center;
  background:var(--lime-s);color:var(--lime);margin-bottom:3px}
body.trakzo #dashboard .tz-none-badge svg{width:19px;height:19px;fill:none;stroke:currentColor;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
body.trakzo #dashboard .tz-none-title{font-size:13px;font-weight:600;color:var(--fg2)}
body.trakzo #dashboard .tz-none-sub{font-size:11.5px;color:var(--fg3)}

/* ---- a scrolling list should fade out, not be guillotined ----
   Alerts Count is capped so one long list cannot set the height of the whole
   grid row. Capped alone, the cap fell through the middle of a row and looked
   like a rendering fault (reported). The mask makes the cut deliberate. */
body.trakzo #dashboard .tz-scroll{overflow-y:auto;scrollbar-width:thin;
  scrollbar-color:var(--line-strong) transparent;
  -webkit-mask-image:linear-gradient(180deg,#000 calc(100% - 26px),transparent 100%);
          mask-image:linear-gradient(180deg,#000 calc(100% - 26px),transparent 100%)}
body.trakzo #dashboard .tz-scroll::-webkit-scrollbar{width:6px}
body.trakzo #dashboard .tz-scroll::-webkit-scrollbar-thumb{background:var(--line-strong);border-radius:99px}
body.trakzo #dashboard .tz-scroll::-webkit-scrollbar-track{background:transparent}
/* nothing to scroll: no cut, so no fade */
body.trakzo #dashboard .tz-scroll.is-short{-webkit-mask-image:none;mask-image:none}

/* ---- Distance travelled: the chart, and what stands in for it when every
   vehicle read zero all week. The empty state covers the canvas box only, and
   it is the whole block in that case — with no lines drawn there is nothing for
   a legend to switch on and off, so the script does not build one. */
/* The chart area takes the slack in the card, so an empty state sits in the
   middle of the panel instead of at the top with a void under it, and a drawn
   chart grows into the space rather than leaving one. */
body.trakzo #dashboard #block_device_distance .trk-chart{flex:1;display:flex;flex-direction:column}
body.trakzo #dashboard #block_device_distance .tz-canvas-line{flex:1;min-height:180px;height:auto}
/* Only the MESSAGE centres in the spare room. Centring the whole chart area
   pushed the panel's own subtitle down the card with it, which looked like a
   second, floating heading. */
body.trakzo #dashboard .trk-chart.is-empty .trk-empty{flex:1;justify-content:center;padding:0 12px}
body.trakzo #dashboard #block_device_distance .phead-row{flex:none}
body.trakzo #dashboard .trk-chart{position:relative}
body.trakzo #dashboard .trk-empty{display:none}
/* nothing to plot: drop the chart box and the legend, and let the card shrink to
   the message. A 400px frame around one sentence is what made an ordinary quiet
   week look like a broken screen. */
body.trakzo #dashboard .trk-chart.is-empty .tz-canvas,
body.trakzo #dashboard .trk-chart.is-empty .tzl{display:none}
body.trakzo #dashboard .trk-chart.is-empty .trk-empty{display:flex;
  flex-direction:column;align-items:center;justify-content:center;gap:4px;text-align:center;padding:30px 12px 22px}
body.trakzo #dashboard .trk-empty-badge{width:48px;height:48px;border-radius:50%;display:grid;place-items:center;
  background:var(--lime-s);color:var(--lime);margin-bottom:8px}
body.trakzo #dashboard .trk-empty-badge svg{width:24px;height:24px}
body.trakzo #dashboard .trk-empty-title{font-size:14px;font-weight:650;color:var(--fg2)}
body.trakzo #dashboard .trk-empty-sub{font-size:12px;color:var(--fg3)}

/* ============================================================================
   APP MODALS (Bootstrap-3 / bootstrap-modal) — the app opens these via data-modal;
   the theme CSS that normally styles them is not loaded, so style them here so
   edit/delete/test-sms/etc. render as proper dark dialogs (not raw boxes).
   NB: .modal here is the app's BS3 modal; our own page dialog uses .modal-wrap.
   ============================================================================ */
body.trakzo .modal{position:fixed;inset:0;z-index:1050;overflow-x:hidden;overflow-y:auto;outline:0}
body.trakzo .modal-backdrop{position:fixed;inset:0;z-index:1040;background:rgba(4,5,6,.72)}
html[data-theme="light"] body.trakzo .modal-backdrop{background:rgba(20,28,40,.42)}
body.trakzo .modal-dialog{position:relative;width:auto;max-width:600px;margin:40px auto;z-index:1050}
body.trakzo .modal-dialog.modal-lg{max-width:900px}
body.trakzo .modal-dialog.modal-sm{max-width:420px}
body.trakzo .modal-content{background:var(--panel);border:1px solid var(--line);border-radius:var(--r3);
  box-shadow:0 40px 100px rgba(0,0,0,.6);overflow:visible}
html[data-theme="light"] body.trakzo .modal-content{box-shadow:0 30px 80px rgba(18,32,54,.24)}
body.trakzo .modal-header{display:flex;align-items:center;gap:11px;padding:16px 20px;box-shadow:inset 0 -1px 0 var(--line);border-radius:var(--r3) var(--r3) 0 0}
body.trakzo .modal-header .modal-title{font-size:16px;font-weight:700;color:var(--fg);margin:0;flex:1;order:1}
body.trakzo .modal-header .close{order:2;margin-left:auto;background:transparent;border:0;color:var(--fg3);
  font-size:22px;line-height:1;cursor:pointer;opacity:.85;padding:0 4px}
body.trakzo .modal-header .close:hover{color:var(--fg);opacity:1}
body.trakzo .modal-body{padding:18px 20px;color:var(--fg2);overflow:visible}
body.trakzo .modal-body .form-group{margin-bottom:14px}
body.trakzo .modal-body label{color:var(--fg2);font-size:12.5px;font-weight:550;margin-bottom:6px;display:block}
body.trakzo .modal-body .form-control,body.trakzo .modal-body input[type=text],body.trakzo .modal-body input[type=password],
body.trakzo .modal-body input[type=email],body.trakzo .modal-body input[type=number],body.trakzo .modal-body select,
body.trakzo .modal-body textarea{width:100%;background:var(--raised);border:1.5px solid transparent;border-radius:var(--r);
  color:var(--fg);font:13.5px var(--sans);padding:10px 11px;height:auto;box-shadow:none}
body.trakzo .modal-body .form-control:hover,body.trakzo .modal-body select:hover,body.trakzo .modal-body textarea:hover{background:var(--hover)}
body.trakzo .modal-body .form-control:focus,body.trakzo .modal-body select:focus,body.trakzo .modal-body textarea:focus{outline:none;border-color:var(--lime);background:var(--bg)}
body.trakzo .modal-body .checkbox,body.trakzo .modal-body .radio{display:flex;align-items:center;gap:9px;margin:0 0 8px}
body.trakzo .modal-body input[type=checkbox],body.trakzo .modal-body input[type=radio]{accent-color:var(--lime)}
body.trakzo .modal-body hr{border:0;border-top:1px solid var(--line);margin:14px 0}
body.trakzo .modal-body table{width:100%;font-size:13px}
body.trakzo .modal-body table td,body.trakzo .modal-body table th{padding:8px;box-shadow:inset 0 -1px 0 var(--line)}
body.trakzo .modal-footer{display:flex;justify-content:flex-end;gap:8px;padding:14px 20px;box-shadow:inset 0 1px 0 var(--line);border-radius:0 0 var(--r3) var(--r3)}
body.trakzo .modal-footer .buttons{display:flex;gap:8px;margin-left:auto}
body.trakzo .modal .btn{display:inline-flex;align-items:center;gap:7px;border:1px solid transparent;border-radius:var(--r);
  padding:10px 18px;cursor:pointer;font:13.5px var(--sans);font-weight:650;text-decoration:none}
body.trakzo .modal .btn-action,body.trakzo .modal .btn-primary,body.trakzo .modal .btn-success{background:var(--lime);color:#fff}
/* light mode: the lime token is a darker olive (#5F8A08) — white text reads far
   better on it than the dark ink used in dark mode. */
html[data-theme="light"] body.trakzo .modal .btn-action,
html[data-theme="light"] body.trakzo .modal .btn-primary,
html[data-theme="light"] body.trakzo .modal .btn-success{color:#fff}
body.trakzo .modal .btn-action:hover,body.trakzo .modal .btn-primary:hover{filter:brightness(1.08)}
body.trakzo .modal .btn-default{background:var(--raised);color:var(--fg2);border-color:var(--line)}
body.trakzo .modal .btn-default:hover{background:var(--hover);color:var(--fg)}
body.trakzo .modal .btn-danger{background:var(--coral);color:#fff}
body.trakzo .modal .nav-tabs{display:flex;gap:2px;box-shadow:inset 0 -1px 0 var(--line);margin-bottom:14px;padding:0;list-style:none;flex-wrap:wrap}
body.trakzo .modal .nav-tabs > li > a{display:block;padding:9px 13px;color:var(--fg3);font-size:13px;font-weight:560;border-radius:var(--r) var(--r) 0 0;text-decoration:none}
body.trakzo .modal .nav-tabs > li.active > a{color:var(--fg);box-shadow:inset 0 -2px 0 var(--lime)}

/* ============================================================================
   LOOKUP TABLES (Objects Move/Idle/Stop/Offline/Inactive/Unreach, Events, ...)
   Teztrak redesign of the yajra DataTables page. Look: demo-lookup.html.
   The table markup/classes are generated by DataTables JS; we only style them.
   ============================================================================ */
body.trakzo .lookup-page{max-width:1340px}
body.trakzo .lookup-page .card{position:relative}
body.trakzo .lookup-page .ph{padding:16px 20px;align-items:center}
body.trakzo .lookup-body{padding:0}
/* NB: DataTables wraps its filter row in a div.top — same class as our top bar.
   Float it up into the header row (top-right, aligned with the title) so the
   header is one compact strip instead of a tall empty band. Reset the leaked
   top-bar styles too. */
body.trakzo .lookup-page .dataTables_wrapper .top{position:absolute;top:28px;right:20px;height:auto;width:auto;
  background:transparent;box-shadow:none;padding:0;margin:0;display:flex;align-items:center;gap:10px;justify-content:flex-end;z-index:3}
@media (max-width:640px){ body.trakzo .lookup-page .dataTables_wrapper .top{position:static;padding:12px 16px;box-shadow:inset 0 -1px 0 var(--line)} }
body.trakzo .lookup-page .dataTables_wrapper .bottom{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding:12px 16px;box-shadow:inset 0 1px 0 var(--line)}

/* search box */
body.trakzo .lookup-page .dataTables_filter{margin:0}
body.trakzo .lookup-page .dataTables_filter label{font-size:0;margin:0;color:transparent;position:relative;display:block}
body.trakzo .lookup-page .dataTables_filter input{font:13px var(--sans);min-width:250px;background-color:var(--raised);
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='%23646C79' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='7'/><path d='m20 20-3.5-3.5'/></svg>");
  background-repeat:no-repeat;background-position:12px center;
  border:1.5px solid var(--line);border-radius:var(--r);color:var(--fg);padding:9px 12px 9px 36px;margin:0}
body.trakzo .lookup-page .dataTables_filter input::placeholder{color:var(--fg3)}
body.trakzo .lookup-page .dataTables_filter input:hover{background-color:var(--hover)}
body.trakzo .lookup-page .dataTables_filter input:focus{outline:none;border-color:var(--lime);background-color:var(--bg);box-shadow:0 0 0 3px var(--lime-s)}
html[data-theme="light"] body.trakzo .lookup-page .dataTables_filter input:focus{background-color:#fff}

/* per-page length select */
body.trakzo .lookup-page .dataTables_length{margin:0;order:-1}
body.trakzo .lookup-page .dataTables_length label{font-size:0;color:transparent;margin:0;display:flex;align-items:center;gap:6px}
body.trakzo .lookup-page .dataTables_length select{font:12.5px var(--mono);background:var(--raised);border:1.5px solid transparent;
  border-radius:var(--r);color:var(--fg);padding:8px 30px 8px 12px;cursor:pointer;min-width:70px;
  -webkit-appearance:none;-moz-appearance:none;appearance:none;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23646C79' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M6 9l6 6 6-6'/></svg>");
  background-repeat:no-repeat;background-position:right 10px center}
body.trakzo .lookup-page .dataTables_length select:hover{background-color:var(--hover)}
body.trakzo .lookup-page .dataTables_length select:focus{outline:none;border-color:var(--lime)}
body.trakzo .lookup-page .dataTables_length option{background:var(--panel);color:var(--fg)}

/* the table */
body.trakzo .lookup-page .table-responsive{overflow-x:auto}
body.trakzo .lookup-page table.dataTable{width:100% !important;border-collapse:collapse;font-size:13.5px;margin:0;color:var(--fg2)}
body.trakzo .lookup-page table.dataTable thead th{text-align:left;font-size:11px;font-weight:650;color:var(--fg3);
  padding:11px 14px;box-shadow:inset 0 -1px 0 var(--line);white-space:nowrap;border:0;background:transparent;cursor:pointer;position:relative}
body.trakzo .lookup-page table.dataTable thead th:hover{color:var(--fg2)}
body.trakzo .lookup-page table.dataTable thead th.sorting_asc,
body.trakzo .lookup-page table.dataTable thead th.sorting_desc{color:var(--lime)}
/* replace DataTables' default sort background-image arrows with clean caret glyphs */
body.trakzo .lookup-page table.dataTable thead th.sorting,
body.trakzo .lookup-page table.dataTable thead th.sorting_asc,
body.trakzo .lookup-page table.dataTable thead th.sorting_desc{background-image:none !important}
body.trakzo .lookup-page table.dataTable thead th.sorting_asc:after{content:"▲";font-size:8px;margin-left:6px;color:var(--lime)}
body.trakzo .lookup-page table.dataTable thead th.sorting_desc:after{content:"▼";font-size:8px;margin-left:6px;color:var(--lime)}
body.trakzo .lookup-page table.dataTable tbody td{padding:11px 14px;box-shadow:inset 0 -1px 0 var(--line);border:0;white-space:nowrap;color:var(--fg2)}
body.trakzo .lookup-page table.dataTable tbody tr:hover td{background:var(--hover);color:var(--fg)}
body.trakzo .lookup-page table.dataTable tbody td a{color:var(--lime);text-decoration:none}
body.trakzo .lookup-page table.dataTable tbody td a:hover{text-decoration:underline}
body.trakzo .lookup-page table.dataTable .no-data,
body.trakzo .lookup-page table.dataTable td.dataTables_empty{text-align:center;color:var(--fg3);padding:40px 0}

/* row action buttons/icons */
body.trakzo .lookup-page table.dataTable .btn-group .btn,
body.trakzo .lookup-page table.dataTable .icon{color:var(--fg3);background:transparent;border:0}
body.trakzo .lookup-page table.dataTable .btn-group .btn:hover{color:var(--lime)}

/* info + pagination */
body.trakzo .lookup-page .dataTables_info{color:var(--fg3);font:11.5px var(--mono);padding:0;margin:0}
body.trakzo .lookup-page .dataTables_paginate{margin-left:auto;display:flex;gap:4px}
body.trakzo .lookup-page .dataTables_paginate .paginate_button{min-width:30px;height:30px;display:inline-flex;align-items:center;
  justify-content:center;padding:0 9px;border:0 !important;border-radius:var(--r);background:transparent;color:var(--fg2) !important;
  font:12.5px var(--sans);font-weight:600;cursor:pointer}
body.trakzo .lookup-page .dataTables_paginate .paginate_button:hover{background:var(--hover) !important;color:var(--fg) !important}
body.trakzo .lookup-page .dataTables_paginate .paginate_button.current{background:var(--lime-s) !important;color:var(--lime) !important}
body.trakzo .lookup-page .dataTables_paginate .paginate_button.disabled{opacity:.3;cursor:default}
body.trakzo .lookup-page .dataTables_paginate .paginate_button.disabled:hover{background:transparent !important}

/* processing loader */
body.trakzo .lookup-page .dataTables_processing{background:var(--panel);color:var(--fg2);border-radius:var(--r);
  box-shadow:0 0 0 1px var(--line)}

/* ============================================================================
   FLEET OVERVIEW — palette, page header and summary cards (2026-09-24)

   The dashboard is the only screen on this palette. Everything here is scoped
   under #dashboard (and the page shell under .trakzo-page:has(#dashboard)), so
   the map, the admin and every other screen keep the product's blue accent. The
   tokens are REDECLARED on #dashboard rather than changed at :root, which is
   what makes that containment true rather than merely intended.

   THE ACCENT IS THE PRODUCT'S, NOT THE DASHBOARD'S (changed 2026-09-24).
   This screen was briefly given its own emerald. It now DERIVES every colour
   from the tokens the rest of the app already uses, so the dashboard follows
   Signal Azure — and follows it automatically if that accent is ever changed,
   because nothing here restates a hex.

     --em        the accent            = --lime  (the product accent)
     --em-deep   the one dark card     = that accent mixed toward ink
     --em-soft   the accent's tint     = --lime-s
     --off       offline               = --coral
     --unreach   never connected       = --violet

   Statuses keep their own colours because they carry meaning: offline is not
   "the accent, but sadder". Every one of them is also labelled in words, since
   colour alone is not readable for everyone.

   The names stay `--em*` so the panels and the chart helper do not need to know
   what the accent happens to be. Do NOT set `--lime` from `--em` here: --em is
   defined FROM --lime, and doing both makes the pair circular and both invalid.
   ============================================================================ */
body.trakzo #dashboard{
  --em:var(--lime);
  --em-soft:var(--lime-s);
  /* The dark summary card. Derived from the accent rather than picked, so it
     stays a deep version of whatever the accent is. */
  --em-deep:color-mix(in srgb, var(--lime) 42%, #04121F);
  --off:var(--coral);
  --unreach:var(--violet);
}

/* the page ground: soft grey in light, unchanged near-black in dark */
html[data-theme="light"] body.trakzo.trakzo-page:has(#dashboard){background:#F2F5F7}

/* ---- page header ---- */
body.trakzo #dashboard .dash-head{display:block;margin-bottom:18px}
body.trakzo #dashboard .dash-eyebrow{font:600 10.5px/1 var(--mono);letter-spacing:.18em;
  text-transform:uppercase;color:var(--em);opacity:.9;margin:0 0 9px}
body.trakzo #dashboard .dash-head h1{font-size:24px;font-weight:750;letter-spacing:-.03em;
  line-height:1.05;margin:0;color:var(--fg)}

/* ---- the four summary cards ----
   One row on desktop, two on tablet, one column on a phone. auto-fit rather
   than a fixed count so the row cannot produce a lone stranded card. */
body.trakzo #dashboard .fleet-cards{display:grid;gap:14px;margin-bottom:16px;
  grid-template-columns:repeat(4, minmax(0, 1fr))}
@media (max-width:1100px){ body.trakzo #dashboard .fleet-cards{grid-template-columns:repeat(2, minmax(0,1fr))} }
@media (max-width:620px){  body.trakzo #dashboard .fleet-cards{grid-template-columns:1fr} }

body.trakzo #dashboard .fcard{display:block;background:var(--panel);border:1px solid var(--line);
  border-radius:16px;padding:18px 20px 16px;text-decoration:none;color:inherit;min-width:0;
  transition:border-color .16s ease, box-shadow .16s ease, transform .16s ease}
html[data-theme="light"] body.trakzo #dashboard .fcard{box-shadow:0 1px 2px rgba(16,32,52,.04)}
body.trakzo #dashboard a.fcard:hover{border-color:var(--em);transform:translateY(-1px)}
body.trakzo #dashboard a.fcard:focus-visible{outline:2px solid var(--em);outline-offset:2px}

body.trakzo #dashboard .fcard-top{display:flex;align-items:flex-start;gap:12px;margin-bottom:10px}
body.trakzo #dashboard .fcard-title{flex:1;min-width:0;font-size:14px;font-weight:650;color:var(--fg);
  letter-spacing:-.01em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
body.trakzo #dashboard .fcard-ic{flex:none;width:38px;height:38px;border-radius:12px;display:grid;
  place-items:center;background:var(--raised);color:var(--fg2)}
body.trakzo #dashboard .fcard-ic svg{width:18px;height:18px;fill:none;stroke:currentColor;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
body.trakzo #dashboard .fcard-ic.is-warn{background:color-mix(in srgb, var(--off) 16%, transparent);color:var(--off)}

body.trakzo #dashboard .fcard-n{font:700 36px/1 var(--mono);letter-spacing:-.05em;color:var(--fg);
  padding-bottom:14px;margin-bottom:12px;box-shadow:inset 0 -1px 0 var(--line);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
body.trakzo #dashboard .fcard-n em{font-style:normal;font-size:20px;font-weight:600;color:var(--fg3)}
body.trakzo #dashboard .fcard-foot{font-size:12.5px;color:var(--fg3);display:flex;align-items:center;
  gap:5px;flex-wrap:wrap;min-height:18px}
body.trakzo #dashboard .fsep{opacity:.5}
body.trakzo #dashboard .fdot{width:7px;height:7px;border-radius:50%;flex:none;background:var(--fg3)}
body.trakzo #dashboard .fdot.is-offline{background:var(--off)}
body.trakzo #dashboard .fdot.is-unreach{background:var(--unreach)}

/* ---- the accent card. Its own colours, because it is the only thing on the
   page that sits on a dark fill in BOTH themes — so it cannot inherit tokens
   that flip with the theme without going unreadable in one of them. ---- */
body.trakzo #dashboard .fcard.is-accent{background:var(--em-deep);border-color:transparent}
html[data-theme="light"] body.trakzo #dashboard .fcard.is-accent{
  box-shadow:0 6px 18px -10px color-mix(in srgb, var(--lime) 55%, transparent)}
body.trakzo #dashboard .fcard.is-accent .fcard-title{color:#EAF6F1}
body.trakzo #dashboard .fcard.is-accent .fcard-n{color:#fff;box-shadow:inset 0 -1px 0 rgba(255,255,255,.16)}
body.trakzo #dashboard .fcard.is-accent .fcard-n em{color:rgba(255,255,255,.62)}
body.trakzo #dashboard .fcard.is-accent .fcard-foot{color:rgba(255,255,255,.72)}
body.trakzo #dashboard .fcard.is-accent .fcard-ic{background:var(--em);color:#fff}
body.trakzo #dashboard a.fcard.is-accent:hover{border-color:transparent;
  background:color-mix(in srgb, var(--lime) 52%, #04121F)}

/* ============================================================================
   DASHBOARD "SIGNAL" UPGRADE (2026-09-03). UI ONLY — supersedes the flat v2
   styling above. Adds depth (gradient panels + soft shadow), makes the lime
   behave like light (glow on live values), sets data in mono, gives charts an
   area fill + glowing endpoint, refines the donut, and turns the Overview KPIs
   into an instrument strip. No markup/flow changes; blocks + AJAX untouched.
   ============================================================================ */

/* ---- page shell: a calmer, blue-biased ground + a real page header ---- */
body.trakzo.trakzo-page:has(#dashboard){background:
  radial-gradient(120% 70% at 88% -8%, var(--lime-s), transparent 46%), var(--bg)}
body.trakzo #dashboard{padding:26px 22px 56px;max-width:1360px}
/* The eyebrow above the H1 used to be a CSS `content:` string. It is real text in
   index.blade.php now — a generated one could not be selected, copied, read by a
   screen reader or translated, and it printed twice the moment the markup gained
   its own. The .dash-head rules live in the FLEET OVERVIEW section above. */

/* ---- panel layout ----------------------------------------------------------
   The reference is two wide/narrow rows: distance beside connectivity, then
   recent events beside tasks. DOM order cannot deliver that — the blocks are
   emitted in whatever order the user's settings list them — so each block is
   placed by id, with `order` for the sequence and a span for the width.

   Every block is optional (Setup can switch any of them off), so nothing is
   positioned relative to a neighbour: turn one off and the rest simply close
   up. That is also why there is no fixed row count here.
   ---------------------------------------------------------------------------- */
/* #dashboard_blocks carries Bootstrap's `row` class, and the theme gives
   `.row` a clearfix: `.row:after{content:""; display:table}`. On a float layout
   that pseudo-element is invisible. On a GRID it is a grid item — so it took
   row 1, column 1, and every panel was pushed one column to the right, leaving
   the first row looking oddly indented with a gap at its end. Killing the
   content removes the item; the clearfix has nothing to clear here anyway. */
body.trakzo #dashboard #dashboard_blocks::after{content:none}
body.trakzo #dashboard #dashboard_blocks{grid-auto-flow:row;align-items:stretch}
/* PANELS IN A ROW ARE THE SAME HEIGHT. This was briefly changed to natural
   height, to stop Task overview growing to match the events table beside it.
   That was the wrong cure: a short card then ENDED partway down the row and the
   page background showed through beneath it, which reads as a rendering fault
   rather than as spare room. Equal heights, aligned bottoms.

   The real fix for "too tall" is to take the height out of the CONTENT — the
   donut and its legend are smaller, the events header is one row instead of
   two, and a short panel centres what it has in the space it is given (see
   .dashboard-content below) rather than stranding it at the top. */
body.trakzo #dashboard .dashboard-block .panel{height:100%;display:flex;flex-direction:column}
body.trakzo #dashboard .dashboard-block .panel-body,
body.trakzo #dashboard .dashboard-block .dashboard-content{flex:1;display:flex;flex-direction:column}
body.trakzo #dashboard #block_device_distance     {order:1; grid-column:span 8}
body.trakzo #dashboard #block_device_activity     {order:2; grid-column:span 4}
body.trakzo #dashboard #block_device_overview     {order:3; grid-column:span 8}
body.trakzo #dashboard #block_latest_tasks        {order:4; grid-column:span 4}
/* The two device-status panels share a row so they read as one band: the
   filter strip, and the lifecycle totals that belong with it. Alerts then gets
   the full width to itself, because its rows are label + bar + count and cramming
   them into half a row is what made the labels truncate. */
body.trakzo #dashboard #block_over_view           {order:5; grid-column:1 / -1}
/* The Devices panel is hidden, not deleted: every figure it showed — total,
   online, offline, unreachable and expired — is inside the Device status panel
   above it now, and showing both was the duplication this merge removes. The
   block still loads (one small request); switch "Devices" off in Setup to stop
   even that. Nothing about the block itself was changed, so turning it back on
   after removing this line restores it exactly. */
body.trakzo #dashboard #block_device_status_counts{display:none}
body.trakzo #dashboard #block_latest_events       {order:7; grid-column:1 / -1}
/* Tablet: the wide/narrow pairing stops working below roughly 1100px — the
   narrow column gets too tight for a donut and a legend side by side. */
@media (max-width:1100px){
  body.trakzo #dashboard #dashboard_blocks > .dashboard-block{grid-column:span 6 !important}
}
@media (max-width:760px){
  body.trakzo #dashboard #dashboard_blocks > .dashboard-block{grid-column:1 / -1 !important}
}

/* ---- Distance: the figure above the chart and the two footnotes under it ---- */
body.trakzo #dashboard .trk-foot{display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;
  margin-top:12px;padding-top:11px;box-shadow:inset 0 1px 0 var(--tz-hair);
  font-size:11.5px;color:var(--fg3)}
body.trakzo #dashboard .trk-chart.is-empty .trk-foot,
body.trakzo #dashboard .trk-chart.is-empty .phead-row .ptotal{display:none}

/* ---- Fleet connectivity: donut over its legend, both centred ----
   NB the block's own content box is a flex row from an older layout. A flex item
   is shrink-to-fit, and `.ovpie` also declares `container-type:inline-size`,
   which tells the browser to size it WITHOUT looking at its contents — the two
   together resolved to a width of zero and the donut disappeared entirely. The
   content box is a plain block again, and the container query that needed
   `container-type` belonged to the old side-by-side arrangement and is gone. */
body.trakzo #dashboard #block_device_activity .dashboard-content{display:block}
body.trakzo #dashboard .ovpie{display:flex;flex-direction:column;align-items:stretch;gap:0;
  container-type:normal;width:100%}
body.trakzo #dashboard .ovpie{flex:1;justify-content:center}
body.trakzo #dashboard .ovpie .tz-canvas-ovpie{align-self:center;margin:0 0 14px}
body.trakzo #dashboard .tzl-conn{gap:0}
body.trakzo #dashboard .tzl-conn .tzl-item{padding:9px 0;box-shadow:inset 0 -1px 0 var(--tz-hair);
  border-radius:0;font-size:13px}
body.trakzo #dashboard .tzl-conn .tzl-item:last-child{box-shadow:none}
body.trakzo #dashboard .tzl-conn .tzl-val{font-variant-numeric:tabular-nums;white-space:pre}

/* (The .statgrid/.statcell rules that used to sit here are gone with the markup
   they styled. Device status is one merged panel now — see the
   "DEVICE STATUS — the merged panel" section below.) */

/* ---- Devices: the compact lifecycle strip ----
   Counts, not bars. These figures are not comparable with each other — the first
   is the total the rest are drawn from — so they are laid out as a row of
   equals-in-shape rather than as a chart that invites comparison. Each is a link
   to its own lookup table; Expired is only reachable from here. */
body.trakzo #dashboard .lifegrid{display:grid;gap:8px;
  grid-template-columns:repeat(auto-fit, minmax(96px, 1fr))}
body.trakzo #dashboard .lifecell{--_c:var(--fg3);display:flex;flex-direction:column;gap:3px;
  padding:11px 12px;border-radius:12px;background:var(--raised);border:1px solid var(--line);
  text-decoration:none;color:inherit;min-width:0;
  transition:border-color .15s ease, background .15s ease}
body.trakzo #dashboard a.lifecell:hover{background:var(--hover);border-color:var(--_c)}
body.trakzo #dashboard a.lifecell:focus-visible{outline:2px solid var(--_c);outline-offset:2px}
body.trakzo #dashboard .lifecell-n{font:600 22px/1 var(--mono);letter-spacing:-.04em;color:var(--_c);
  font-variant-numeric:tabular-nums}
/* Wrap, do not truncate. "Unreacha…" is no more readable than "Unreach" was. */
body.trakzo #dashboard .lifecell-k{font-size:11.5px;color:var(--fg2);min-width:0;
  line-height:1.3;overflow-wrap:anywhere}

/* ---- Fleet connectivity legend: three aligned columns ----
   The count and the percentage used to be one string in one cell, so neither
   lined up down the list. They are separate columns now, both tabular, both
   right-aligned, with the label taking the slack. */
body.trakzo #dashboard .tzl-conn .tzl-item{display:grid;
  grid-template-columns:10px minmax(0,1fr) 3.5ch 4.5ch;align-items:center;gap:10px;
  padding:8px 0;box-shadow:inset 0 -1px 0 var(--tz-hair);border-radius:0;font-size:13px}
body.trakzo #dashboard .tzl-conn .tzl-item:last-child{box-shadow:none}
body.trakzo #dashboard .tzl-conn .tzl-name{flex:none;color:var(--fg2)}
body.trakzo #dashboard .tzl-conn .tzl-val{display:contents}
body.trakzo #dashboard .tzl-conn .tzl-count,
body.trakzo #dashboard .tzl-conn .tzl-pct{font:600 12.5px/1 var(--mono);font-variant-numeric:tabular-nums;
  text-align:right;white-space:nowrap}
body.trakzo #dashboard .tzl-conn .tzl-count{color:var(--fg)}
body.trakzo #dashboard .tzl-conn .tzl-pct{color:var(--fg3)}

/* ---- Recent events ----------------------------------------------------------
   The table used to sit inside a second, nested card (.panel.panel-transparent)
   with its own padding and border, inside the block's card — a box in a box,
   which is where most of the wasted width and the cramped columns came from.
   The nested card is gone; the header strip and the table are direct children of
   the block's content box now.

   COLUMN WIDTHS. Device, Type and Time are sized to their content and never
   wrap; Message takes whatever is left and wraps. That is the only column whose
   length is unpredictable, so it is the only one allowed to reflow — sizing the
   others from content is what stops a long message from squeezing a timestamp
   into two lines.
   ---------------------------------------------------------------------------- */
body.trakzo #dashboard #block_device_overview .evhead{display:flex;align-items:center;gap:10px;
  margin:0 0 12px}
body.trakzo #dashboard #block_device_overview .evhead{align-items:flex-start;margin:0 0 14px}
body.trakzo #dashboard #block_device_overview .evhead-sub{flex:1;min-width:0;margin:0;
  font-size:13px;color:var(--fg2);line-height:1.45}
/* the table fills what is left of the card, and its rows breathe into it */
body.trakzo #dashboard #block_device_overview .dashboard-content{min-height:0}
body.trakzo #dashboard #block_device_overview .evwrap{flex:1}
body.trakzo #dashboard #block_device_overview .evhead .link{display:inline-flex;align-items:center;gap:5px;
  padding:6px 12px;border-radius:99px;background:var(--em-soft);color:var(--em);
  text-decoration:none;font-size:11.5px;font-weight:650;white-space:nowrap;line-height:1;flex:none}
body.trakzo #dashboard #block_device_overview .evhead .link:after{content:"→";font-weight:600}
body.trakzo #dashboard #block_device_overview .evhead .link:hover{background:var(--em);color:#fff}
body.trakzo #dashboard #block_device_overview .evhead .link:focus-visible{outline:2px solid var(--em);outline-offset:2px}
body.trakzo #dashboard #block_device_overview .evhead .btn.icon.filter{flex:none;color:var(--fg3);
  width:30px;height:30px;display:inline-grid;place-items:center;border-radius:9px;cursor:pointer}
body.trakzo #dashboard #block_device_overview .evhead .btn.icon.filter:hover{background:var(--hover);color:var(--fg)}
body.trakzo #dashboard #block_device_overview .evhead .btn.icon.filter:focus-visible{outline:2px solid var(--em);outline-offset:2px}

/* the scroller. Only this box ever scrolls sideways, and only when it must. */
body.trakzo #dashboard #block_device_overview .evwrap{overflow-x:auto;max-width:100%}
body.trakzo #dashboard .evtable{width:100%;border-collapse:collapse;font-size:13px;table-layout:auto}
body.trakzo #dashboard .evtable th{text-align:left;font:650 10.5px/1 var(--sans);letter-spacing:.07em;
  text-transform:uppercase;color:var(--fg3);padding:0 12px 9px;border:0;background:transparent;
  box-shadow:inset 0 -1px 0 var(--line);white-space:nowrap}
body.trakzo #dashboard .evtable td{padding:11px 12px;border:0;box-shadow:inset 0 -1px 0 var(--tz-hair);
  color:var(--fg2);vertical-align:top}
body.trakzo #dashboard .evtable tbody tr:last-child td{box-shadow:none}
body.trakzo #dashboard .evtable tbody tr:hover td{background:var(--hover)}
/* first and last columns sit flush with the card's padding, not inset again */
body.trakzo #dashboard .evtable th:first-child,
body.trakzo #dashboard .evtable td:first-child{padding-left:0}
body.trakzo #dashboard .evtable th:last-child,
body.trakzo #dashboard .evtable td:last-child{padding-right:0}

body.trakzo #dashboard .evtable .ev-device{color:var(--fg);font-weight:600;white-space:nowrap}
body.trakzo #dashboard .evtable .ev-type{white-space:nowrap;color:var(--fg2)}
/* the only column that may wrap, and the only one that absorbs spare width */
body.trakzo #dashboard .evtable .ev-msg{width:100%;white-space:normal;overflow-wrap:anywhere;
  color:var(--fg3);min-width:110px}
/* Times are tabular and right-aligned so the column reads as a column. It never
   wraps and never touches the card edge — the row's own padding-right is 0, so
   the gap comes from the cell before it. */
body.trakzo #dashboard .evtable .ev-time{text-align:right;white-space:nowrap;
  font:500 12px/1.5 var(--mono);font-variant-numeric:tabular-nums;color:var(--fg2);
  padding-left:20px}
body.trakzo #dashboard .evtable th:last-child{text-align:right;padding-left:20px}
body.trakzo #dashboard .evtable .no-data{text-align:center;color:var(--fg3);padding:34px 0;
  box-shadow:none}

/* On a phone the table is wider than the card whatever we do, so it scrolls
   inside .evwrap. Letting the timestamp WRAP as well was the worst of both: the
   rows grew to two lines each AND the column still sat off the right edge. The
   timestamp stays on one line; the container does the scrolling. */
@media (max-width:520px){
  body.trakzo #dashboard .evtable td,
  body.trakzo #dashboard .evtable th{padding:9px 8px}
  body.trakzo #dashboard .evtable .ev-time{padding-left:14px}
  body.trakzo #dashboard .evtable th:last-child{padding-left:14px}
  body.trakzo #dashboard .evtable .ev-msg{min-width:80px}
}

/* Tasks share a row with the events table, which is taller. Rather than leave
   the four bars stranded at the top of the card with a void beneath them, the
   list takes the spare room and centres itself in it. The bars keep their own
   spacing — the card is simply balanced, not stretched. */
body.trakzo #dashboard #block_latest_tasks .tz-stats{flex:1;justify-content:center}
body.trakzo #dashboard #block_latest_tasks .tz-sub{margin-top:auto}

/* Tasks: the reference puts the bars edge to edge under the total */
/* The label column was a fixed 96px and truncated every status but the first
   ("Task in progr…"). It sizes to the longest label now — max-content, capped so
   a long translation cannot squeeze the bar out of existence. */
body.trakzo #dashboard #block_latest_tasks .tz-stat{
  grid-template-columns:minmax(0, max-content) minmax(60px, 1fr) auto;
  grid-template-areas:"k bar v";align-items:center;gap:12px}
body.trakzo #dashboard #block_latest_tasks .tz-stat-k{max-width:150px}
body.trakzo #dashboard #block_latest_tasks .tz-stat-bar{height:8px}
@media (max-width:420px){
  body.trakzo #dashboard #block_latest_tasks .tz-stat{grid-template-columns:1fr auto;
    grid-template-areas:"k v" "bar bar";align-items:start;gap:7px 12px}
}

/* ---- panel heading: a title, and under it a line saying what you are looking
   at. The subtitle is written by the block's CONTENT template, because only the
   content knows the period or the device count it is describing — so it arrives
   with the AJAX payload and updates with it. ---- */
body.trakzo #dashboard .panel-title{font:650 15px/1.2 var(--sans);letter-spacing:-.015em;
  color:var(--fg);text-transform:none}
body.trakzo #dashboard .panel-heading{padding:18px 20px 0}
body.trakzo #dashboard .dashboard-content{padding:4px 20px 20px}
/* Subtitles were --fg3 at 12.5px, which is the same treatment as a disabled
   control. They carry real information (the period, the device count, what the
   ordering means), so they read at --fg2 and a size that survives a laptop
   screen. */
body.trakzo #dashboard .psub{font-size:13px;color:var(--fg2);margin:2px 0 16px;line-height:1.45}
/* a figure the panel leads with, shown to the right of its title */
body.trakzo #dashboard .phead-row{display:flex;align-items:flex-end;gap:16px;
  justify-content:space-between;margin:2px 0 14px;flex-wrap:wrap}
body.trakzo #dashboard .phead-row .psub{margin:0}
body.trakzo #dashboard .ptotal{text-align:right;line-height:1.1;min-width:0}
body.trakzo #dashboard .ptotal b{display:block;font:700 26px/1 var(--mono);letter-spacing:-.04em;color:var(--fg)}
body.trakzo #dashboard .ptotal span{display:block;font-size:11.5px;color:var(--fg3);margin-top:4px}

/* ---- the block card: depth via gradient + hairline + soft shadow ---- */
body.trakzo #dashboard #dashboard_blocks{gap:14px}
body.trakzo #dashboard .dashboard-block .panel{
  background:linear-gradient(180deg, var(--raised), var(--panel)) !important;
  border:1px solid var(--line) !important;border-radius:16px !important;
  box-shadow:0 1px 0 rgba(255,255,255,.02) inset, 0 14px 34px -22px rgba(0,0,0,.55) !important}
body.trakzo #dashboard .dashboard-block .panel:hover{border-color:var(--line-strong) !important}
/* (The panel heading, its title and the content padding are declared once, in
   the FLEET OVERVIEW section above. They used to be restated here as a small
   uppercase grey label, which quietly overrode it.) */
body.trakzo #dashboard .panel-title .icon{color:var(--em)}

/* ---- all numeric data reads as telemetry (mono, tabular) ---- */
body.trakzo #dashboard .count,body.trakzo #dashboard .n,body.trakzo #dashboard .dash-num,
body.trakzo #dashboard table td:last-child,body.trakzo #dashboard .val,body.trakzo #dashboard b{
  font-family:var(--mono);font-variant-numeric:tabular-nums;letter-spacing:-.02em}

/* ---- Devices (percents) DONUT ----
   The ring's size, thickness and colour are Chart.js's now (see the CHARTS
   section above and assets/js/trakzo-charts.js), so the drop-shadow and
   stroke-width rules that used to live here are gone: they addressed an <svg>
   that no longer exists, and a canvas ignores both. The one layout rule this
   block still needs (centre the donut in its stretched card) is declared once,
   further up in this file — it is not restated here. */

/* ---- DEVICE STATUS — the merged panel (2026-09-24) -------------------------
   One card: connectivity on the left, activity on the right. It replaces two
   adjacent panels that between them said the same thing twice.

   The block still ALSO emits .row-status, because that markup is the map
   sidebar's data source (see the block template's header). It is hidden here —
   the dashboard reads this panel instead.
   ---------------------------------------------------------------------------- */
body.trakzo #dashboard #block_over_view .row-status{display:none !important}

/* the block's own card furniture gets out of the way; .dstat is the card */
body.trakzo #dashboard #block_over_view .panel-heading{display:none}
body.trakzo #dashboard #block_over_view .dashboard-content{padding:0}

body.trakzo #dashboard .dstat{display:flex;flex-direction:column;height:100%}
body.trakzo #dashboard .dstat-head{display:flex;align-items:flex-start;gap:14px;
  padding:20px 22px 18px;box-shadow:inset 0 -1px 0 var(--line)}
body.trakzo #dashboard .dstat-head-t{flex:1;min-width:0}
body.trakzo #dashboard .dstat-head h3{margin:0;font:700 22px/1.2 var(--sans);letter-spacing:-.025em;color:var(--fg)}
body.trakzo #dashboard .dstat-head p{margin:5px 0 0;font-size:13px;color:var(--fg2)}
body.trakzo #dashboard .dstat-all{flex:none;display:inline-flex;align-items:center;gap:7px;
  padding:9px 15px;border-radius:10px;border:1px solid var(--line);background:var(--raised);
  color:var(--fg);font:600 12.5px/1 var(--sans);text-decoration:none;white-space:nowrap;
  transition:border-color .15s ease,background .15s ease}
body.trakzo #dashboard .dstat-all:hover{border-color:var(--em);background:var(--hover)}
body.trakzo #dashboard .dstat-all:focus-visible{outline:2px solid var(--em);outline-offset:2px}
body.trakzo #dashboard .dstat-badge{flex:none;min-width:34px;height:34px;padding:0 9px;border-radius:50px;
  display:grid;place-items:center;background:var(--raised);color:var(--fg2);
  font:600 13px/1 var(--mono);font-variant-numeric:tabular-nums}

/* two halves, divided by a hairline. One column below 900px — the donut and the
   bars each need their own width to stay readable. */
body.trakzo #dashboard .dstat-body{flex:1;display:grid;grid-template-columns:1fr 1fr}
body.trakzo #dashboard .dstat-body > section{padding:20px 22px 18px;min-width:0}
body.trakzo #dashboard .dstat-body > section + section{box-shadow:inset 1px 0 0 var(--line)}
@media (max-width:900px){
  body.trakzo #dashboard .dstat-body{grid-template-columns:1fr}
  body.trakzo #dashboard .dstat-body > section + section{box-shadow:inset 0 1px 0 var(--line)}
}
body.trakzo #dashboard .dstat-body h4{margin:0;font:650 17px/1.2 var(--sans);letter-spacing:-.02em;color:var(--fg)}
body.trakzo #dashboard .dstat-sub{margin:4px 0 18px;font-size:12.5px;color:var(--fg3)}

/* ---- connectivity ---- */
body.trakzo #dashboard .dstat-conn-row{display:flex;align-items:center;gap:26px;flex-wrap:wrap}
body.trakzo #dashboard .dstat-donut{width:172px;height:172px;flex:none}
body.trakzo #dashboard .dstat-legend{flex:1;min-width:190px;list-style:none;margin:0;padding:0}
body.trakzo #dashboard .dstat-legend li + li{box-shadow:inset 0 1px 0 var(--tz-hair)}
body.trakzo #dashboard .dstat-leg{--_c:var(--fg3);width:100%;display:grid;
  grid-template-columns:auto 1fr 3.5ch 4.5ch;align-items:center;gap:12px;
  padding:11px 6px;margin:0 -6px;background:none;border:0;border-radius:9px;
  color:inherit;font:inherit;text-align:left;cursor:pointer;transition:background .14s ease}
body.trakzo #dashboard .dstat-leg:hover:not(:disabled){background:var(--hover)}
body.trakzo #dashboard .dstat-leg:focus-visible{outline:2px solid var(--_c);outline-offset:-2px}
body.trakzo #dashboard .dstat-leg:disabled{cursor:default}
body.trakzo #dashboard .dstat-dot{width:11px;height:11px;border-radius:50%;background:var(--_c);flex:none}
body.trakzo #dashboard .dstat-leg-k{font-size:14px;color:var(--fg2);min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
body.trakzo #dashboard .dstat-leg-n{font:700 17px/1 var(--mono);font-variant-numeric:tabular-nums;
  color:var(--fg);text-align:right}
body.trakzo #dashboard .dstat-leg-p{font:500 13px/1 var(--mono);font-variant-numeric:tabular-nums;
  color:var(--fg3);text-align:right}
body.trakzo #dashboard .dstat-note{display:flex;align-items:center;gap:9px;margin:20px 0 0;
  font-size:13px;color:var(--fg2)}

/* ---- activity ---- */
body.trakzo #dashboard .dstat-bars{list-style:none;margin:0;padding:0;display:flex;
  flex-direction:column;gap:2px}
body.trakzo #dashboard .dstat-bar{--_c:var(--fg2);width:100%;display:grid;
  grid-template-columns:1fr auto;grid-template-areas:"k n" "bar bar";gap:8px 12px;
  padding:9px 6px 11px;margin:0 -6px;background:none;border:0;border-radius:9px;
  color:inherit;font:inherit;text-align:left;cursor:pointer;transition:background .14s ease}
body.trakzo #dashboard .dstat-bar:hover{background:var(--hover)}
body.trakzo #dashboard .dstat-bar:focus-visible{outline:2px solid var(--_c);outline-offset:-2px}
body.trakzo #dashboard .dstat-bar-k{grid-area:k;font-size:14px;color:var(--fg2)}
body.trakzo #dashboard .dstat-bar-n{grid-area:n;font:700 17px/1 var(--mono);
  font-variant-numeric:tabular-nums;color:var(--fg)}
body.trakzo #dashboard .dstat-bar.is-zero .dstat-bar-n{color:var(--fg2);opacity:.7;font-weight:500}
/* the track is always drawn, so the rows line up whether or not there is a bar
   in them — an empty track reads as "none", a missing one reads as broken */
body.trakzo #dashboard .dstat-bar-track{grid-area:bar;height:7px;border-radius:99px;
  background:color-mix(in srgb, var(--fg3) 18%, transparent);overflow:hidden}
body.trakzo #dashboard .dstat-bar-track > i{display:block;height:100%;border-radius:99px;
  background:var(--_c);transition:width .5s cubic-bezier(.22,1,.36,1)}
@media (prefers-reduced-motion:reduce){
  body.trakzo #dashboard .dstat-bar-track > i{transition:none}
}
/* the scale the bars are drawn against, stated once */
body.trakzo #dashboard .dstat-axis{display:flex;justify-content:space-between;
  margin-top:10px;padding-top:9px;box-shadow:inset 0 1px 0 var(--tz-hair);
  font:500 11.5px/1 var(--mono);color:var(--fg3)}
body.trakzo #dashboard .dstat-hint{display:flex;align-items:flex-start;gap:8px;margin:14px 0 0;
  font-size:12px;line-height:1.45;color:var(--fg3)}
body.trakzo #dashboard .dstat-hint svg{width:15px;height:15px;flex:none;margin-top:1px;
  fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round}

/* ---- footer ---- */
body.trakzo #dashboard .dstat-foot{display:flex;align-items:center;justify-content:space-between;
  gap:16px;flex-wrap:wrap;padding:14px 22px;box-shadow:inset 0 1px 0 var(--line)}
body.trakzo #dashboard .dstat-exp{display:inline-flex;align-items:center;gap:9px;
  color:var(--fg2);text-decoration:none;font-size:13px;padding:5px 9px;margin:-5px -9px;
  border-radius:9px;transition:background .14s ease,color .14s ease}
body.trakzo #dashboard .dstat-exp:hover{background:var(--hover);color:var(--fg)}
body.trakzo #dashboard .dstat-exp:focus-visible{outline:2px solid var(--em);outline-offset:2px}
body.trakzo #dashboard .dstat-exp svg{width:16px;height:16px;flex:none;fill:none;
  stroke:currentColor;stroke-width:1.6;stroke-linecap:round}
body.trakzo #dashboard .dstat-exp b{font:600 13px/1 var(--mono);color:var(--fg);
  padding:3px 8px;border-radius:50px;background:var(--raised)}
body.trakzo #dashboard .dstat-foot-hint{font-size:12px;color:var(--fg3)}

/* ---- Distance chart ---- */
body.trakzo #dashboard #block_device_distance .dashboard-content{position:relative}

/* ---- Alerts / Tasks / Devices tables: quiet rhythm, mono values ---- */
body.trakzo #dashboard table.table td,body.trakzo #dashboard table.table th{box-shadow:inset 0 -1px 0 var(--line);border:0}
body.trakzo #dashboard table.table td:last-child{color:var(--fg);text-align:right}
body.trakzo #dashboard .ev{padding:10px 0}
body.trakzo #dashboard .ev .ic{border-radius:9px}

/* ---- Progress bars anywhere on the dashboard get the glow treatment ---- */
body.trakzo #dashboard .progress{background:var(--raised);height:7px;border-radius:99px}
body.trakzo #dashboard .progress-bar{background:var(--lime);box-shadow:0 0 8px color-mix(in srgb, var(--lime) 40%, transparent)}

/* ---- Light mode: soften the glows so they read as tint, not haze ---- */
html[data-theme="light"] body.trakzo #dashboard .row-status .stat-box.all{background:color-mix(in srgb, var(--lime) 12%, transparent) !important}
html[data-theme="light"] body.trakzo #dashboard .progress-bar{box-shadow:0 0 6px rgba(95,138,8,.28)}

/* ---- Device Overview .ovbox tiles: replace the bright saturated fills with
   refined dark tinted cards (accent by position: Move/Idle/Stop/Offline/Inactive/Unreach) ---- */
body.trakzo #dashboard #block_device_overview .ovboxes{gap:10px}
body.trakzo #dashboard #block_device_overview .ovbox{--_c:var(--slate);
  background:linear-gradient(180deg,var(--raised),var(--panel)) !important;border:1px solid var(--line);
  border-radius:12px;padding:13px 14px 12px !important;position:relative;overflow:hidden;transition:border-color .13s,background .13s}
body.trakzo #dashboard #block_device_overview .ovbox:hover{filter:none;border-color:var(--line-strong);background:var(--hover) !important}
body.trakzo #dashboard #block_device_overview .ovbox::before{content:"";position:absolute;top:0;left:0;right:0;height:2px;background:var(--_c);opacity:.7}
body.trakzo #dashboard #block_device_overview .ovbox .title{color:var(--fg3) !important;opacity:1;font:600 10.5px/1.2 var(--sans) !important;letter-spacing:.04em;text-transform:uppercase}
body.trakzo #dashboard #block_device_overview .ovbox .count{color:var(--_c) !important;font:600 24px/1.1 var(--mono) !important;letter-spacing:-.03em;margin-top:5px}
body.trakzo #dashboard #block_device_overview .ovbox .link{color:var(--fg3) !important;opacity:1;text-decoration:none;font:600 10px/1 var(--sans) !important;margin-top:6px;text-transform:uppercase;letter-spacing:.05em}
body.trakzo #dashboard #block_device_overview .ovbox .link:hover{color:var(--lime) !important}
/* accent per position (order is fixed: Move Idle Stop Offline Inactive Unreach) */
body.trakzo #dashboard #block_device_overview .ovbox:nth-child(1){--_c:var(--teal)}
body.trakzo #dashboard #block_device_overview .ovbox:nth-child(2){--_c:var(--amber)}
body.trakzo #dashboard #block_device_overview .ovbox:nth-child(3){--_c:var(--coral)}
body.trakzo #dashboard #block_device_overview .ovbox:nth-child(4){--_c:var(--slate)}
body.trakzo #dashboard #block_device_overview .ovbox:nth-child(5){--_c:var(--violet)}
body.trakzo #dashboard #block_device_overview .ovbox:nth-child(6){--_c:var(--slate)}

/* ---- Recent Events / Device-status split: the two sub-panels were styled as
   nested light cards (bg + radius + inset border) INSIDE the already-carded block
   — a box-in-a-box that read as unfinished. Flatten them: transparent, no radius,
   no border; separate the two columns with a single hairline; let the block card
   be the only frame. Content gets clean inner padding, dividers stay inside. ---- */
body.trakzo #dashboard #block_device_overview .ovsplit{gap:0}
/* flat sub-panels (no nested card), but with consistent top padding so content
   never touches the card walls. Horizontal padding is added per-column below. */
body.trakzo #dashboard #block_device_overview .ovsplit > div{background:transparent !important;
  border-radius:0 !important;box-shadow:none !important;padding:14px 0 6px !important}
/* the inner .panel-transparent wrapper carries no frame of its own */
body.trakzo #dashboard #block_device_overview .ovsplit .panel-transparent{background:transparent !important;
  border:0 !important;box-shadow:none !important;padding:0 !important}
/* stacked (narrow): each column gets side padding so it clears the card walls */
body.trakzo #dashboard #block_device_overview .ovsplit > div{padding-left:2px !important;padding-right:2px !important}
/* on wide layout the two columns sit side by side with a divider between them */
@media (min-width:981px){
  body.trakzo #dashboard #block_device_overview .ovsplit{grid-template-columns:1.35fr 1fr}
  body.trakzo #dashboard #block_device_overview .ovsplit > div:first-child{padding-right:22px !important;padding-left:0 !important}
  body.trakzo #dashboard #block_device_overview .ovsplit > div:last-child{padding-left:22px !important;padding-right:0 !important;box-shadow:inset 1px 0 0 var(--line) !important}
}
/* the events table itself */
body.trakzo #dashboard #block_device_overview table.table{width:100%}
body.trakzo #dashboard #block_device_overview table.table td,
body.trakzo #dashboard #block_device_overview table.table th{padding:10px 8px}
body.trakzo #dashboard #block_device_overview table.table td:first-child,
body.trakzo #dashboard #block_device_overview table.table th:first-child{padding-left:0}
body.trakzo #dashboard #block_device_overview table.table td:last-child,
body.trakzo #dashboard #block_device_overview table.table th:last-child{padding-right:0}
/* the recent-events header row (title + View details) gets space below */
body.trakzo #dashboard #block_device_overview .ovh{margin-bottom:12px}
/* column headers quieter, uppercase */
body.trakzo #dashboard #block_device_overview table.table thead th{color:var(--fg3);font:600 10.5px/1 var(--sans);
  letter-spacing:.05em;text-transform:uppercase;padding-bottom:10px}

/* ---- more breathing room inside the big Device Overview card so content
   doesn't sit tight against the top/left/right walls (user feedback) ---- */
body.trakzo #dashboard #block_device_overview .dashboard-content{padding:8px 22px 22px !important}
body.trakzo #dashboard #block_device_overview .panel-heading{padding:18px 22px 6px !important}
body.trakzo #dashboard #block_device_overview .ovboxes{margin-bottom:20px}
/* the recent-events / donut split sits a touch lower, clear of the tiles */
body.trakzo #dashboard #block_device_overview .ovsplit{margin-top:4px;padding-top:16px;box-shadow:inset 0 1px 0 var(--line)}


/* ============================================================================
   DASHBOARD — THE SOFT PASS (2026-09-24)

   The page was correct but hard: hairlines at full strength, edges meeting at
   right angles, numbers shouting, nothing moving. Every rule below lowers
   contrast or rounds an edge. None of them changes a layout, a size or a
   number, so nothing here can move content or hide it.

   Scoped to #dashboard throughout — the map and the admin keep the crisper
   treatment they were designed with.
   ============================================================================ */

/* ---- dividers at about half strength. The dashboard is made almost entirely
   of rows and cells, so at full strength the grid lines compete with the data
   sitting in them. --tz-hair is that softer line, derived from --line rather
   than picked, so it still follows the theme. ---- */
body.trakzo #dashboard{--tz-hair:color-mix(in srgb, var(--line) 62%, transparent)}
body.trakzo #dashboard table.table td,
body.trakzo #dashboard table.table th,
body.trakzo #dashboard .ev,
body.trakzo #dashboard #block_device_overview table.table-list td,
body.trakzo #dashboard #block_device_overview table.table-list th,
body.trakzo #dashboard .ovtbl td,
body.trakzo #dashboard .ovtbl th{box-shadow:inset 0 -1px 0 var(--tz-hair)}
body.trakzo #dashboard table.table tr:last-child td,
body.trakzo #dashboard .ev:last-child{box-shadow:none}

/* ---- rounder cards, and a shadow that falls rather than outlines. The card
   is the largest shape on the page, so its corner radius sets how soft the
   whole thing reads. ---- */
body.trakzo #dashboard .dashboard-block .panel{border-radius:20px !important;
  box-shadow:0 1px 0 rgba(255,255,255,.025) inset,
             0 2px 6px -3px rgba(0,0,0,.35),
             0 22px 48px -30px rgba(0,0,0,.6) !important;
  transition:border-color .2s ease, box-shadow .2s ease, transform .2s ease}
html[data-theme="light"] body.trakzo #dashboard .dashboard-block .panel{
  box-shadow:0 1px 0 rgba(255,255,255,.7) inset,
             0 2px 5px -3px rgba(19,33,55,.12),
             0 20px 44px -30px rgba(19,33,55,.3) !important}
/* the lift is 1px: enough to say the card answered, not enough to reflow it */
body.trakzo #dashboard .dashboard-block .panel:hover{transform:translateY(-1px)}
@media (prefers-reduced-motion:reduce){
  body.trakzo #dashboard .dashboard-block .panel{transition:none}
  body.trakzo #dashboard .dashboard-block .panel:hover{transform:none}
}

/* ---- headings ---- (size and colour: FLEET OVERVIEW section) */
body.trakzo #dashboard .panel-title a{color:inherit}
body.trakzo #dashboard .panel-title a:hover{color:var(--fg2)}
body.trakzo #dashboard .panel-title .icon{opacity:.85}

/* ---- rounder tiles, and hover that warms instead of jumping ---- */
body.trakzo #dashboard .row-status .stat-box,
body.trakzo #dashboard #block_device_overview .ovbox{border-radius:14px;
  transition:background .18s ease, border-color .18s ease}
body.trakzo #dashboard #block_device_overview .ovbox::before{opacity:.5;height:2px}
body.trakzo #dashboard #block_device_overview .ovbox:hover::before{opacity:.9}

/* ---- rounder chips and badges ---- */
body.trakzo #dashboard .ev .ic{border-radius:10px}
body.trakzo #dashboard .btn.icon.options,
body.trakzo #dashboard .btn.icon.filter{border-radius:10px;transition:background .15s ease, color .15s ease}

/* ---- the donut column: when the Device Overview card is narrow the ring and
   its legend cannot sit side by side, and the pair wraps into a tall stack that
   pushes the card out of line with its neighbours. A container query on the
   pair itself — not on the window, which says nothing about the width of this
   column — shrinks the ring instead, down to the point where stacking really is
   the only option. ---- */
/* (The container queries that used to shrink this donut when it sat BESIDE its
   legend are gone with that arrangement — the donut is above the legend now and
   has the full panel width to itself.) */


/* ============================================================================
   POP-UPS WITHOUT A THEME UNTIL NOW (2026-09-05) — same tokens as everything else
   ============================================================================ */
/* toastr notifications: panel card, type shown as a coloured left rail + icon tint */
body.trakzo #toast-container > .toast,
body.trakzo #toast-container > .toast:hover{
  opacity:1 !important;background-color:var(--panel) !important;color:var(--fg) !important;
  border:1px solid var(--line) !important;border-left-width:4px !important;border-radius:12px !important;
  box-shadow:0 18px 44px -20px rgba(0,0,0,.45), 0 1px 2px rgba(0,0,0,.06) !important;
  padding:14px 18px 14px 52px !important;font:500 13px/1.45 var(--sans) !important;width:340px !important;
  background-size:20px !important;background-position:18px 50% !important}
body.trakzo #toast-container > .toast-success{border-left-color:var(--teal) !important}
body.trakzo #toast-container > .toast-error{border-left-color:var(--coral) !important}
body.trakzo #toast-container > .toast-warning{border-left-color:var(--amber) !important}
body.trakzo #toast-container > .toast-info{border-left-color:var(--lime) !important}
body.trakzo #toast-container .toast-title{font-weight:700 !important;color:var(--fg) !important;margin-bottom:2px}
body.trakzo #toast-container .toast-message{color:var(--fg2) !important}
body.trakzo #toast-container .toast-message a{color:var(--lime) !important}
body.trakzo #toast-container .toast-close-button{color:var(--fg3) !important;text-shadow:none !important;opacity:1 !important;font-weight:400 !important;top:-2px !important;right:-6px !important}
body.trakzo #toast-container .toast-close-button:hover{color:var(--fg) !important}
body.trakzo #toast-container .toast-progress{background:var(--lime) !important;opacity:.35 !important}
/* bootstrap-colorpicker pop-up (vehicle tail colour etc.) */
body.trakzo .colorpicker{
  background:var(--panel) !important;color:var(--fg) !important;border:1px solid var(--line) !important;
  border-radius:12px !important;padding:10px !important;
  box-shadow:0 18px 44px -20px rgba(0,0,0,.45), 0 1px 2px rgba(0,0,0,.06) !important;z-index:1060 !important}
body.trakzo .colorpicker::before,body.trakzo .colorpicker::after{display:none !important}
body.trakzo .colorpicker-saturation,body.trakzo .colorpicker-hue,body.trakzo .colorpicker-alpha{border-radius:6px !important;overflow:hidden}
body.trakzo .colorpicker-color,body.trakzo .colorpicker-color div{border-radius:6px !important}
body.trakzo .colorpicker input.form-control{height:32px !important;font:600 12.5px var(--mono) !important}

/* ============================================================================
   DIALOG AUDIT FIXES (2026-09-05) — found by a scripted sweep of every dialog
   ============================================================================ */
/* Bootstrap label/badge colours → tokens (e.g. the Alerts list's "Active" toggles were #5cb85c) */
body.trakzo .label{border-radius:999px !important;padding:4px 10px !important;font:600 11px/1 var(--sans) !important;letter-spacing:.02em !important}
body.trakzo .label.label-success,body.trakzo .alert-status-toggle.label-success{background:var(--teal) !important;color:#fff !important}
body.trakzo .label.label-danger,body.trakzo .alert-status-toggle.label-danger{background:var(--coral) !important;color:#fff !important}
body.trakzo .label.label-warning{background:var(--amber) !important;color:#fff !important}
body.trakzo .label.label-info,body.trakzo .label.label-primary{background:var(--lime) !important;color:#fff !important}
body.trakzo .label.label-default,body.trakzo .alert-status-toggle.label-default{background:var(--raised) !important;color:var(--fg2) !important}
/* pagination (Camera / Media and every paged list): stock white links in both themes */
body.trakzo .pagination > li > a,body.trakzo .pagination > li > span,body.trakzo .page-link{
  background:var(--panel) !important;color:var(--fg2) !important;border:1px solid var(--line) !important;
  font:500 13px/1.2 var(--sans) !important;padding:7px 12px !important;box-shadow:none !important}
body.trakzo .pagination > li > a:hover,body.trakzo .page-link:hover{background:var(--hover) !important;color:var(--fg) !important}
body.trakzo .pagination > .active > a,body.trakzo .pagination > .active > span,body.trakzo .page-item.active .page-link{
  background:var(--lime) !important;border-color:var(--lime) !important;color:#fff !important}
body.trakzo .pagination > .disabled > span,body.trakzo .pagination > .disabled > a,body.trakzo .page-item.disabled .page-link{
  background:var(--raised) !important;color:var(--fg3) !important;border-color:var(--line) !important}
body.trakzo .pagination > li:first-child > a,body.trakzo .pagination > li:first-child > span{border-radius:10px 0 0 10px !important}
body.trakzo .pagination > li:last-child > a,body.trakzo .pagination > li:last-child > span{border-radius:0 10px 10px 0 !important}
/* scrollbars inside dialogs: thin, token thumb (a 10px dark thumb was showing under wide tables) */
body.trakzo .modal ::-webkit-scrollbar{width:6px !important;height:6px !important;background:transparent !important}
body.trakzo .modal ::-webkit-scrollbar-track{background:transparent !important;border:0 !important}
body.trakzo .modal ::-webkit-scrollbar-thumb{background:var(--line-strong) !important;border:0 !important;border-radius:3px !important}
/* Setup screen (trakzo_modal_page): the base skin's generic `.on{color:green!important}` leaked into
   the active nav button and the active pane (and everything inside it) */
body.trakzo-modal-page .pane.on,body.trakzo .trakzo-modal-page .pane.on{color:var(--fg) !important}
body.trakzo-modal-page .navbtn.on,body.trakzo .trakzo-modal-page .navbtn.on{color:var(--lime) !important}
/* Bootstrap panels inside dialogs (Chat): stock #f9f9f9 footer / #f5f5f5 heading in both themes */
body.trakzo .modal .panel{background:var(--panel) !important;border:1px solid var(--line) !important;border-radius:12px !important;box-shadow:none !important}
body.trakzo .modal .panel-heading{background:var(--raised) !important;color:var(--fg) !important;border-bottom:1px solid var(--line) !important;border-radius:12px 12px 0 0 !important}
body.trakzo .modal .panel-footer{background:var(--raised) !important;border-top:1px solid var(--line) !important;border-radius:0 0 12px 12px !important}
body.trakzo .modal .panel-body{color:var(--fg) !important}
/* list dialogs (Geofences / Routes / POI / Alerts): `table-layout:fixed` + break-word split the
   checkbox and colour columns to a few px and stacked their headers one letter per line */
body.trakzo .modal .modal-body table.table-list{table-layout:auto !important;word-break:normal !important}
body.trakzo .modal .modal-body table.table-list thead th{white-space:nowrap !important;word-break:normal !important;vertical-align:middle !important}
body.trakzo .modal .modal-body table.table-list th.table-checkbox,body.trakzo .modal .modal-body table.table-list td.table-checkbox{width:44px !important;min-width:44px !important}
/* striped tables inside dialogs (Weekly Activity etc.): stock #eee / #f9f9f9 stripes in both themes */
body.trakzo .modal .table > tbody > tr{background:transparent !important}
body.trakzo .modal .table-striped > tbody > tr:nth-of-type(odd),body.trakzo .modal .table-striped > tbody > tr:nth-of-type(odd) > td{background:var(--raised) !important}
body.trakzo .modal .table-hover > tbody > tr:hover,body.trakzo .modal .table-hover > tbody > tr:hover > td,body.trakzo .modal .table > tbody > tr:hover > td{background:var(--hover) !important}
body.trakzo .modal .table > thead > tr > th{background:var(--raised) !important;color:var(--fg3) !important;border-bottom:1px solid var(--line) !important;font:600 11px/1.3 var(--sans) !important;letter-spacing:.06em !important;text-transform:uppercase !important}
body.trakzo .modal .table > tbody > tr > td{border-top:1px solid var(--line) !important;color:var(--fg) !important}
/* Activity log page: the two date inputs were the only square-cornered controls left */
body.trakzo-page .form-control.datepicker,body.trakzo .trakzo-page .form-control.datepicker{border-radius:10px !important}
/* small icon buttons in dialog tables (vehicle Edit → Sensors "delete"): stock #eee */
body.trakzo .modal .btn.icon{background:var(--raised) !important;color:var(--fg2) !important;border:0 !important;border-radius:8px !important;box-shadow:none !important}
body.trakzo .modal .btn.icon:hover{background:var(--hover) !important;color:var(--fg) !important}
body.trakzo .modal .btn.icon.delete:hover{background:var(--coral) !important;color:#fff !important}
/* the Setup screen is a regular .modal on the map page (no trakzo-modal-page body class), so the
   `.on{color:green!important}` leak needs the plain selectors */
body.trakzo .pane.on{color:var(--fg) !important}
body.trakzo .navbtn.on{color:var(--lime) !important}

/* ============================================================================
   MODAL SINGLE-SELECT DROPDOWNS — stop the modal clipping the option list.
   (2026-09-17)

   Measured in the live DOM (Add custom address → "Match radius", 20 options):
     .modal-body   top  84, height 354  → bottom 438, overflow-y:auto
     .dropdown-menu top 395, height 220 → bottom 615
   The menu is absolutely positioned inside .bootstrap-select, which sits inside
   .modal-body — and .modal-body is a scroll container. So everything below y=438
   was clipped away and exactly one option row stayed visible. The menu itself was
   never short: it held all 20 <li> and scrolled internally. Same root cause the
   DataTables page-size select hit earlier (see its dropup fix in trakzo-map.css).

   Fix: while a single-select menu is open, lift the clip on the two ancestors
   that impose it. The JS in trakzo-select-fix.js adds .dv-select-unclipped, and
   only when the form is not itself scrolling — unclipping a scrolled modal would
   spill its fields and reset its scroll position.
   ============================================================================ */
/* both axes, and specific enough to beat `body.trakzo .modal .modal-body`:
   that rule sets overflow-x:hidden !important, and a box with one axis hidden
   computes the other from visible back to auto — so the clip would survive. */
body.trakzo .modal .modal-body.dv-select-unclipped,
body.trakzo-admin .modal .modal-body.dv-select-unclipped,
.modal .modal-body.dv-select-unclipped{
  overflow:visible !important;overflow-x:visible !important;overflow-y:visible !important}
.modal .modal-content:has(> .modal-body.dv-select-unclipped){overflow:visible !important}
/* with the clip lifted the list is no longer limited to the room below the field,
   so raise the open-state cap (~8 rows); longer lists still scroll inside it */
body.trakzo .modal .modal-body.dv-select-unclipped .bootstrap-select.open > .dropdown-menu,
.modal .modal-body.dv-select-unclipped .bootstrap-select.open > .dropdown-menu{
  max-height:320px !important}

/* ============================================================================
   SIDEBAR DROPDOWNS vs THE COLLAPSE HANDLE (2026-09-17)

   Measured on History -> the export button beside "Show history": the open menu
   was painted over by the sidebar's round collapse handle. The menu's own
   z-index was never the deciding number -- proved by raising it to 2000 and
   the group around it to 9999, which changed nothing, and by dropping the
   handle to 1, which also changed nothing.

   #sidebar has exactly two children: the handle (position:absolute, z-index:5)
   and .sidebar-content, which was unpositioned. Everything in the form therefore
   painted below the one positioned sibling, whatever z-index it carried, because
   it had no stacking context of its own to be measured in.

   Giving .sidebar-content a stacking context of its own puts the whole form --
   every dropdown in it -- above the handle. Verified: the handle now disappears
   behind the open menu. #sidebar itself keeps z-index 1020, so nothing changes
   for the map or the dock.
   ============================================================================ */
body.trakzo #sidebar .sidebar-content,
body.trakzo-admin #sidebar .sidebar-content{position:relative;z-index:10}

/* ============================================================================
   MUTED / HELP TEXT (2026-09-18)

   updated.css sets .text-muted to #EEEEEE. On the dark theme that lands on a
   near-black panel and reads fine, which is why it went unnoticed; in light mode
   it is near-white on white — measured contrast 1.16 against the 4.5 that small
   text needs, i.e. effectively invisible. The Add custom address dialog's help
   paragraph is the case that surfaced it, but the rule is global, so every
   .text-muted string in the app has the same problem in light mode.

   Mapped onto the theme's own secondary text token, which is legible on both
   surfaces and still reads as subordinate to the body text.
   ============================================================================ */
body.trakzo .text-muted,
body.trakzo-admin .text-muted{color:var(--fg2) !important}
body.trakzo .text-muted.small,
body.trakzo-admin .text-muted.small{color:var(--fg2) !important}

/* ============================================================================
   STATUS PILLS — READABLE TEXT ON THE FILL (2026-09-21)

   The dialog-audit pass above moved Bootstrap's label colours onto the theme's
   tokens but kept white text on them. Measured against each fill, white gives:

       dark    teal 1.81 · amber 1.71 · lime 2.91 · coral 3.11
       light   teal 3.40 · amber 3.76 · lime 4.58 · coral 4.68

   Small text needs 4.5, so the Alerts list's "Active" pill and every other
   status pill was washed out — worst on teal and amber, which are nearly as
   bright as the white sitting on them.

   The fills stay; the ink changes. In the dark theme the fills are vivid, so a
   near-black ink reads (9.2 / 9.8 / 6.1 / 6.0). In the light theme the same
   tokens are much darker, so white is the right side — except teal and amber,
   which are darkened for the pill alone (4.98 / 6.04). The tokens themselves are
   untouched: charts, bars and alerts keep their colours.
   ============================================================================ */
body.trakzo .label.label-success,body.trakzo .alert-status-toggle.label-success{color:#06231A !important}
body.trakzo .label.label-danger,body.trakzo .alert-status-toggle.label-danger{color:#2B0410 !important}
body.trakzo .label.label-warning{color:#2A1B00 !important}
body.trakzo .label.label-info,body.trakzo .label.label-primary{color:#05192B !important}

html[data-theme="light"] body.trakzo .label.label-success,
html[data-theme="light"] body.trakzo .alert-status-toggle.label-success{background:#0B7F5F !important;color:#fff !important}
html[data-theme="light"] body.trakzo .label.label-warning{background:#8A5802 !important;color:#fff !important}
html[data-theme="light"] body.trakzo .label.label-danger,
html[data-theme="light"] body.trakzo .alert-status-toggle.label-danger,
html[data-theme="light"] body.trakzo .label.label-info,
html[data-theme="light"] body.trakzo .label.label-primary{color:#fff !important}

/* ============================================================
   bootstrap-datepicker calendar (1.9.0)
   ------------------------------------------------------------
   The popup picked up .dropdown-menu for its panel and nothing else, so every
   cell inside it rendered at --fg2: the day numbers you click, the Su/Mo/Tu
   column headers and the « » arrows were all the same grey, and the selected
   date had no highlight at all. Themed here rather than per page so every
   .datepicker in the app gets it.
   ============================================================ */
body.trakzo .datepicker-dropdown{
  padding:10px;min-width:0;   /* z-index is set inline by the plugin (parent + 10) — leave it to that */
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r2);
  box-shadow:0 18px 44px -20px rgba(0,0,0,.55),0 1px 2px rgba(0,0,0,.06);
  font-family:var(--sans)
}
body.trakzo .datepicker-dropdown table{border-collapse:separate;border-spacing:2px;margin:0}
body.trakzo .datepicker-dropdown td,
body.trakzo .datepicker-dropdown th{border:0;background:transparent;text-align:center;font-weight:500}

/* month / year bar */
body.trakzo .datepicker-dropdown .datepicker-switch{
  font-size:12.5px;font-weight:700;color:var(--fg);padding:6px 4px;cursor:pointer;border-radius:var(--r)
}
body.trakzo .datepicker-dropdown .prev,
body.trakzo .datepicker-dropdown .next{
  width:30px;height:30px;line-height:1;font-size:15px;color:var(--fg2);
  cursor:pointer;border-radius:var(--r);user-select:none
}
body.trakzo .datepicker-dropdown .datepicker-switch:hover,
body.trakzo .datepicker-dropdown .prev:hover,
body.trakzo .datepicker-dropdown .next:hover{background:var(--hover);color:var(--fg)}

/* Su Mo Tu … — a label row, so it must read quieter than the dates */
body.trakzo .datepicker-dropdown th.dow{
  font-size:10px;font-weight:700;color:var(--fg3);
  text-transform:uppercase;letter-spacing:.06em;padding:6px 0 4px
}

/* the dates themselves */
body.trakzo .datepicker-dropdown td.day{
  width:32px;height:30px;font-size:12.5px;color:var(--fg);
  cursor:pointer;border-radius:var(--r)
}
body.trakzo .datepicker-dropdown td.day:hover{background:var(--hover)}
body.trakzo .datepicker-dropdown td.day.old,
body.trakzo .datepicker-dropdown td.day.new{color:var(--fg3)}
body.trakzo .datepicker-dropdown td.day.today{
  color:var(--lime);font-weight:700;box-shadow:inset 0 0 0 1px var(--lime)
}
body.trakzo .datepicker-dropdown td.day.active,
body.trakzo .datepicker-dropdown td.day.active:hover,
body.trakzo .datepicker-dropdown td.range-start,
body.trakzo .datepicker-dropdown td.range-end{
  background:var(--lime) !important;color:#fff !important;font-weight:700;box-shadow:none
}
body.trakzo .datepicker-dropdown td.range{background:var(--lime-s);border-radius:0}
body.trakzo .datepicker-dropdown td.disabled,
body.trakzo .datepicker-dropdown td.disabled:hover{color:var(--fg3);opacity:.45;cursor:default;background:transparent}

/* month / year / decade views */
body.trakzo .datepicker-dropdown span.month,
body.trakzo .datepicker-dropdown span.year,
body.trakzo .datepicker-dropdown span.decade{
  display:inline-block;width:25%;height:34px;line-height:34px;margin:1px 0;
  font-size:12.5px;color:var(--fg);cursor:pointer;border-radius:var(--r)
}
body.trakzo .datepicker-dropdown span.month:hover,
body.trakzo .datepicker-dropdown span.year:hover,
body.trakzo .datepicker-dropdown span.decade:hover{background:var(--hover)}
body.trakzo .datepicker-dropdown span.active{background:var(--lime);color:#fff;font-weight:700}
body.trakzo .datepicker-dropdown span.old,
body.trakzo .datepicker-dropdown span.new{color:var(--fg3)}

/* the little caret the plugin draws against the field */
body.trakzo .datepicker-dropdown:before{border-bottom-color:var(--line)}
body.trakzo .datepicker-dropdown:after{border-bottom-color:var(--panel)}
body.trakzo .datepicker-dropdown.datepicker-orient-bottom:before{border-top-color:var(--line)}
body.trakzo .datepicker-dropdown.datepicker-orient-bottom:after{border-top-color:var(--panel)}

/* ============================================================
   QA fixes — Maintenance / list tables
   ------------------------------------------------------------
   These all sat on --fg3, which measures 3.68:1 (light) and 3.40:1 (dark)
   against the card: under the 4.5:1 AA minimum for text this size. --fg3 stays
   as-is for genuinely decorative text; the ones below carry data (the column
   headings, the percentage under each bar, the service count) so they move to
   --fg2, which clears AA in both themes.
   ============================================================ */
body.trakzo table.lt th{color:var(--fg2)}
body.trakzo .pct{color:var(--fg2)}
body.trakzo td.dash{color:var(--fg2)}
body.trakzo .summary{color:var(--fg2)}
body.trakzo .sect{color:var(--fg2)}

/* vehicle ids and the figures beside them were breaking across two lines on a
   narrow viewport, which doubled every row height; the table already scrolls
   inside .wrap, so let it scroll instead of wrapping identifiers */
body.trakzo table.lt td.obj,
body.trakzo table.lt .left,
body.trakzo table.lt .pct,
body.trakzo table.lt td.dash{white-space:nowrap}
body.trakzo table.lt th{white-space:nowrap}
