/* ==========================================================================
   Theme tokens.
   theme.js always resolves the active theme to an explicit data-theme on
   <html> (following the OS when the user has picked "auto"), so the dark
   palette lives in exactly one place below.
   ========================================================================== */
:root {
  --gs-accent: #00A9DF;
  --gs-accent-2: #0090C0;
  --gs-accent-bg: #EFF8FD;
  --gs-on-accent: #FFFFFF;

  --gs-surface: #FFFFFF;
  --gs-surface-2: #FAFAFA;
  --gs-surface-3: #F7F7F7;
  --gs-surface-4: #F0F0F0;
  --gs-surface-5: #EDEDED;

  --gs-border: #F0F0F0;
  --gs-border-2: #E0E0E0;
  --gs-border-3: #D1D5DB;
  --gs-border-4: #B8B8B8;

  --gs-text: #222222;
  --gs-text-2: #444444;
  --gs-text-3: #505050;
  --gs-text-4: #909090;
  --gs-text-disabled: #B8B8B8;

  --gs-ok-bg: #E8F8EE;
  --gs-ok-fg: #1A7A3B;
  --gs-warn-bg: #FFF3CD;
  --gs-warn-fg: #856404;
  --gs-danger-bg: #FEF0F0;
  --gs-danger-fg: #C0392B;

  --gs-shadow: rgba(0, 0, 0, 0.15);
  --gs-scrim: rgba(0, 0, 0, 0.45);
  --gs-map-filter: none;
  color-scheme: light;
}

/* The login screen is always dark, whatever theme the account uses — it is branding, seen
   before any account (and its theme preference) is known. Sharing the selector keeps the
   dark values defined exactly once. */
:root[data-theme="dark"],
#login-screen {
  --gs-accent: #00A9DF;
  --gs-accent-2: #33BCE8;
  --gs-accent-bg: #102C3F;
  --gs-on-accent: #FFFFFF;

  --gs-surface: #162234;
  --gs-surface-2: #1B2A3E;
  --gs-surface-3: #1F3048;
  --gs-surface-4: #243650;
  --gs-surface-5: #2A3F5C;

  --gs-border: #223349;
  --gs-border-2: #2A3E58;
  --gs-border-3: #334A69;
  --gs-border-4: #3F5878;

  --gs-text: #E4F1FB;
  --gs-text-2: #D4E8F7;
  --gs-text-3: #A9C8DE;
  --gs-text-4: #7CA2BE;
  --gs-text-disabled: #5A7B96;

  --gs-ok-bg: #12301F;
  --gs-ok-fg: #5FD68C;
  --gs-warn-bg: #33280C;
  --gs-warn-fg: #F0C14B;
  --gs-danger-bg: #3A1A1A;
  --gs-danger-fg: #FF8A80;

  --gs-shadow: rgba(0, 0, 0, 0.5);
  --gs-scrim: rgba(0, 0, 0, 0.6);
  color-scheme: dark;
}

* { box-sizing: border-box; }
body {
  margin: 0;
  font-family: Roboto, Arial, sans-serif;
  color: var(--gs-text);
  background: var(--gs-surface);
}

/* Map darkening is its own setting, independent of the interface theme (data-map-theme vs
   data-theme). The filter is scoped to the tile pane only, so markers, routes, zones and
   tooltips keep their real colours instead of being inverted along with the basemap. */
:root[data-map-theme="dark"] {
  --gs-map-filter: invert(1) hue-rotate(180deg) brightness(0.94) contrast(0.88) saturate(0.7);
}
.leaflet-tile-pane { filter: var(--gs-map-filter); }

/* Chrome that sits on the map (labels, popups, attribution) follows the MAP theme, not the
   interface one — otherwise a dark basemap keeps glowing white label pills. Literal colours
   rather than tokens, because the interface may legitimately be light at the same time.
   Dark-only overrides, so a light map keeps Leaflet's own styling untouched. */
:root[data-map-theme="dark"] .leaflet-tooltip,
:root[data-map-theme="dark"] .leaflet-popup-content-wrapper,
:root[data-map-theme="dark"] .leaflet-popup-tip {
  background-color: #1B2A3E;
  border-color: #1B2A3E;
  color: #E4F1FB;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}
:root[data-map-theme="dark"] .leaflet-tooltip-left::before { border-left-color: #1B2A3E; }
:root[data-map-theme="dark"] .leaflet-tooltip-right::before { border-right-color: #1B2A3E; }
:root[data-map-theme="dark"] .leaflet-tooltip-top::before { border-top-color: #1B2A3E; }
:root[data-map-theme="dark"] .leaflet-tooltip-bottom::before { border-bottom-color: #1B2A3E; }
:root[data-map-theme="dark"] .leaflet-control-attribution {
  background: rgba(22, 34, 52, 0.8);
  color: #A9C8DE;
}
:root[data-map-theme="dark"] .leaflet-control-attribution a { color: #7CA2BE; }

/* The login screen covers the viewport, but the canvas behind it still comes from body —
   match it so overscroll can't flash white when the account theme is light. */
body:has(#login-screen) { background: #162234; }

#login-screen {
  /* 100vh is WRONG on phones: it means the viewport with the address bar hidden, so while the bar is
     visible the element is taller than the screen and the sign-in button sits below the fold. dvh
     tracks the bar as it collapses and expands. vh first as the fallback for browsers without dvh. */
  height: 100vh;
  height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--gs-surface);
  /* Set explicitly so descendants inherit the dark-scoped text colour rather than body's. */
  color: var(--gs-text);
  position: relative;
}
.login-lang { position: absolute; top: 16px; right: 16px; }
.gs-lang-select { padding: 4px 8px; border: 1px solid var(--gs-border-4); border-radius: 4px; font-size: 11px; background: var(--gs-surface); color: var(--gs-text-2); }
.login-card {
  width: 320px;
  padding: 32px;
  text-align: center;
}
.login-logo { width: 64px; height: 64px; }
.login-title { color: var(--gs-accent); font-size: 19px; margin: 12px 0 24px; }
#login-form { display: flex; flex-direction: column; text-align: left; }
#login-form label { font-size: 11px; color: var(--gs-text-3); margin-top: 12px; }
#login-form input {
  display: block;
  width: 100%;
  padding: 8px;
  margin-top: 4px;
  border: 1px solid var(--gs-border-4);
  border-radius: 4px;
  font-size: 13px;
  color: var(--gs-text-2);
  background: var(--gs-surface-3);
}
.login-error { color: var(--gs-danger-fg); font-size: 11px; margin-top: 12px; }
.login-ok { color: var(--gs-ok-fg); font-size: 11px; margin-top: 12px; line-height: 1.5; }
.login-hint { color: var(--gs-text-3); font-size: 11px; line-height: 1.5; margin-bottom: 4px; }
/* Secondary action under the button — deliberately quiet, so it reads as a way out rather than
   competing with Login for attention. */
.login-link {
  display: block;
  margin-top: 14px;
  text-align: center;
  font-size: 11px;
  color: var(--gs-text-3);
  text-decoration: none;
}
.login-link:hover { color: var(--gs-accent); text-decoration: underline; }
.btn-primary {
  margin-top: 20px;
  padding: 10px;
  background: var(--gs-accent);
  color: var(--gs-on-accent);
  border: none;
  border-radius: 4px;
  font-size: 13px;
  cursor: pointer;
}
.btn-primary:hover { opacity: 0.9; }

/* Same reason as #login-screen: on a phone 100vh exceeds the visible area whenever the address bar is
   showing, which pushed the bottom of the app off-screen and made the layout jump as the bar moved. */
#main-shell { height: 100vh; height: 100dvh; display: flex; flex-direction: column; }
#impersonate-banner {
  height: 34px;
  background: #E67E22;
  color: var(--gs-on-accent);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 14px;
  font-size: 11px;
  flex-shrink: 0;
}
.ib-actions { display: flex; gap: 8px; align-items: center; }
.ib-btn {
  cursor: pointer;
  padding: 4px 10px;
  border-radius: 4px;
  background: rgba(255,255,255,0.15);
  white-space: nowrap;
}
.ib-btn:hover { background: rgba(255,255,255,0.28); }
.ib-btn-primary { background: rgba(0,0,0,0.25); font-weight: bold; }
.ib-btn-primary:hover { background: rgba(0,0,0,0.35); }
#nav-bar {
  height: 30px;
  background: #1a2a3a;
  color: #cfd8e3;
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 0 14px;
  font-size: 11px;
  flex-shrink: 0;
  overflow-x: auto;         /* stat chips + impersonation label can exceed a narrow width — scroll, don't clip */
  scrollbar-width: none;    /* keep the 30px bar clean (Firefox) */
}
#nav-bar::-webkit-scrollbar { display: none; }  /* keep the 30px bar clean (Chromium/Safari) */
.nav-user { display: flex; align-items: center; gap: 4px; cursor: pointer; white-space: nowrap; }
.nav-user:hover { color: var(--gs-on-accent); }
.nav-caret { font-size: 9px; opacity: 0.7; }
.nav-stat { display: flex; align-items: center; gap: 4px; white-space: nowrap; opacity: 0.9; }
.nav-stat-icon { font-size: 11px; }
.nav-stat-value { font-weight: bold; }
.nav-side { margin-left: auto; }
#nav-account { margin-left: 4px; }
.tb-brand {
  color: var(--gs-accent);
  font-weight: 700;
  font-size: 12px;
  text-decoration: none;
  padding: 0 10px;
  letter-spacing: 0.4px;
  white-space: nowrap;
  flex-shrink: 0;
}
.gs-conn-status {
  align-items: center;
  gap: 6px;
  background: var(--gs-warn-bg);
  color: var(--gs-warn-fg);
  padding: 4px 10px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: bold;
  margin-right: 4px;
  white-space: nowrap;
}
.gs-hist-popup { min-width: 190px; font-size: 11px; line-height: 1.5; }
.gs-hp-addr { font-weight: 600; margin-bottom: 5px; color: var(--gs-text); word-break: break-word; }
.gs-hp-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; border-top: 1px solid var(--gs-border); }
.gs-hp-row > span { color: var(--gs-text-3); white-space: nowrap; }
.gs-hp-row > strong { text-align: right; }
.gs-hp-row > a { color: var(--gs-accent); text-decoration: none; font-size: 10px; }
.gs-hp-row > a:hover { text-decoration: underline; }
.top-bar-logout { margin-left: 16px; color: var(--gs-text-3); text-decoration: none; font-size: 12px; }
.top-bar-logout:hover { color: var(--gs-text-2); }
#status-line { padding: 16px; color: var(--gs-text-3); font-size: 12px; }

#toolbar {
  height: 56px;
  flex-shrink: 0;
  background: var(--gs-surface-2);
  border-bottom: 1px solid var(--gs-border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 8px;
  overflow-x: auto;
  scrollbar-width: none;    /* a scrollbar across a 56px bar reads as a defect — keep scroll, hide the bar */
}
#toolbar::-webkit-scrollbar { display: none; }
.tb-side { display: flex; align-items: center; }
.tb-sep-v { width: 1px; height: 30px; background: var(--gs-surface-5); margin: 0 4px; flex-shrink: 0; }
/* Auto width (min 64px) so longer translated labels (nl "Bestuurdersinformatie", de …) widen their
   own item instead of overflowing onto the neighbour; the label itself is still capped + ellipsized
   below as a hard stop, so no language can ever make items collide. */
.tb-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: relative;
  width: auto;
  min-width: 64px;
  padding: 0 7px;
  height: 52px;
  cursor: pointer;
  border-radius: 4px;
  flex-shrink: 0;
  box-sizing: border-box;
}
.tb-item:hover:not(.disabled) { background: var(--gs-surface-5); }
.tb-item.active { background: #00A9DF14; }
.tb-item.active .tb-icon, .tb-item.active .tb-label { color: var(--gs-accent); }
.tb-item.disabled { opacity: 0.35; cursor: default; }
.tb-icon { font-size: 17px; color: var(--gs-text-3); line-height: 1; }
/* The theme icon is now drawn (SVG) rather than typed (☾/☀), because a text glyph's ink cannot be
   sized reliably across fonts. On DESKTOP it is held at the same visual weight the glyph had, so the
   bar looks exactly as it did; the phone rules below size it against Logout instead. */
#tb-theme .tb-icon { display: inline-flex; align-items: center; justify-content: center; }
#tb-theme .tb-icon svg { display: block; width: 17px; height: 17px; }
.tb-icon-exit {
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border: 2px solid currentColor;
  border-radius: 50%;
  font-size: 10px !important;
  box-sizing: border-box;
}
.tb-item:hover:not(.disabled) .tb-icon { color: var(--gs-text-2); }
.tb-label { font-size: 9px; color: var(--gs-text-3); margin-top: 4px; text-align: center; white-space: nowrap; max-width: 118px; overflow: hidden; text-overflow: ellipsis; }
/* Small count badge over a toolbar icon (e.g. Unused devices) — top-right of the icon, red so a
   nonzero count reads as "needs attention" at a glance without widening the fixed-width item. */
.gs-dlg-resize-handle {
  position: absolute;
  bottom: 0;
  right: 0;
  width: 18px;
  height: 18px;
  cursor: se-resize;
  background: linear-gradient(135deg, transparent 40%, rgba(0,0,0,0.18) 40%, rgba(0,0,0,0.18) 55%, transparent 55%, transparent 65%, rgba(0,0,0,0.18) 65%, rgba(0,0,0,0.18) 80%, transparent 80%);
  border-radius: 0 0 4px 0;
  z-index: 10;
}
.ed-modal { position: relative; }
/* Live fleet count under the Objects button ("1809/2225"). Its own line rather than a badge: the
   badge is a red disc sized for a one- or two-digit alert count, and a ratio would be unreadable in
   one. Accent-coloured and tabular so the digits do not jitter as the count changes. */
.tb-count {
  display: block;
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  margin-top: 3px;
  color: var(--gs-accent);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.2px;
  white-space: nowrap;
}
/* On a phone the labels are hidden and the items are ~30px wide — a ratio has nowhere to go. */
@media (max-width: 640px) { .tb-count { display: none; } }

/* Sized to match .tb-count above — at 9px this was legible only if you already knew a number was
   there, which defeats the point of an alert. Stays a red disc rather than becoming a plain line:
   unlike the Objects ratio, this one is a count of things that need attention. */
.tb-badge {
  position: absolute;
  top: 1px;
  right: 4px;
  background: #e74c3c;
  color: #FFFFFF;
  border-radius: 10px;
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  padding: 3px 6px;
  min-width: 12px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 2px var(--gs-surface-2);
}

#main-body { flex: 1; display: flex; min-height: 0; }
#map-area { flex: 1; display: flex; flex-direction: column; min-width: 0; position: relative; }

#sidebar {
  width: 300px;
  flex-shrink: 0;
  background: var(--gs-surface);
  border-right: 1px solid var(--gs-border);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
#sidebar-resize-handle {
  width: 5px;
  flex-shrink: 0;
  cursor: col-resize;
  background: transparent;
  transition: background 0.15s;
  user-select: none;
  position: relative;
}
#sidebar-resize-handle:hover,
#sidebar-resize-handle.dragging {
  background: rgba(0, 169, 223, 0.25);
}
#sidebar-resize-handle::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 4px;
  height: 32px;
  background-image: radial-gradient(circle, var(--gs-border-4) 1.5px, transparent 1.5px);
  background-size: 4px 8px;
  background-repeat: repeat-y;
  pointer-events: none;
  transition: background-image 0.15s;
}
#sidebar-resize-handle:hover::after,
#sidebar-resize-handle.dragging::after {
  background-image: radial-gradient(circle, rgba(0, 169, 223, 0.9) 1.5px, transparent 1.5px);
}
body.sidebar-resizing { cursor: col-resize !important; user-select: none !important; }
body.sidebar-resizing iframe, body.sidebar-resizing #map { pointer-events: none; }
#device-list { display: flex; flex-direction: column; height: 100%; }
#history-panel { display: flex; flex-direction: column; height: 100%; overflow: hidden; }

.hi-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 12px;
  border-bottom: 1px solid var(--gs-border);
  flex-shrink: 0;
}
.hi-title { font-size: 13px; font-weight: bold; color: var(--gs-text-2); }
.hi-controls { padding: 10px 12px; border-bottom: 1px solid var(--gs-border); flex-shrink: 0; overflow-y: auto; }
.hi-controls .ed-field { margin-bottom: 8px; }
.hi-btn-row { display: flex; gap: 6px; margin-top: 4px; }
.hi-btn-row .ed-btn { flex: 1; padding: 7px 6px; font-size: 11px; }
.hi-info-header {
  padding: 8px 12px;
  font-size: 11px;
  font-weight: bold;
  color: var(--gs-text-3);
  background: var(--gs-surface-5);
  flex-shrink: 0;
}
.hi-info-list { flex: 1; overflow-y: auto; }
.hi-event { border-bottom: 1px solid var(--gs-border); }
.hi-event-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  cursor: pointer;
}
.hi-event-row:hover { background: var(--gs-surface-3); }
.hi-event-icon { font-size: 14px; flex-shrink: 0; }
.hi-event-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.hi-event-label { font-size: 10px; color: var(--gs-text-3); text-transform: uppercase; }
.hi-event-date { font-size: 11px; color: var(--gs-text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hi-event-right { display: flex; flex-direction: column; align-items: flex-end; flex-shrink: 0; gap: 1px; }
.hi-event-dur { font-size: 11px; color: var(--gs-text-2); }
.hi-event-fuel { font-size: 10px; color: var(--gs-accent); }
.hi-event-more { color: var(--gs-text-3); cursor: pointer; padding: 0 4px; flex-shrink: 0; }
.hi-event-more:hover { color: var(--gs-accent); }
.hi-summary {
  background: var(--gs-surface-3);
  border-bottom: 2px solid var(--gs-border-2);
  padding: 8px 12px 6px;
  font-size: 11px;
}
.hi-sum-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }
.hi-sum-row span { color: var(--gs-text-3); }
.hi-sum-row strong { color: var(--gs-text-2); font-weight: 600; }
.hi-detail {
  padding: 4px 12px 10px 35px;
  display: grid;
  grid-template-columns: 1fr;
  gap: 2px;
  font-size: 11px;
}
.hi-detail div { display: flex; justify-content: space-between; gap: 8px; }
.hi-detail span { color: var(--gs-text-3); }
.hi-detail strong { color: var(--gs-text-2); font-weight: normal; }
.hi-detail-addr strong { text-align: right; overflow-wrap: anywhere; }

.gs-history-pin {
  width: 14px;
  height: 14px;
  border-radius: 50% 50% 50% 0;
  transform: rotate(-45deg);
  border: 2px solid #FFFFFF;
  box-shadow: 0 0 2px rgba(0,0,0,0.6);
}
.gs-history-stop-pin {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: #1a73e8;
  border: 3px solid #FFFFFF;
  box-shadow: 0 0 3px rgba(0,0,0,0.6);
  color: var(--gs-on-accent);
  font-size: 21px;
  font-weight: bold;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}
.gs-history-arrow {
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-bottom: 10px solid #FF0000;
  filter: drop-shadow(0 0 1px rgba(0,0,0,0.6));
}

.dl-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 8px;
  border-bottom: 1px solid var(--gs-border);
  flex-shrink: 0;
}
.dl-filter {
  border: 1px solid var(--gs-border-4);
  background: var(--gs-surface);
  color: var(--gs-text-3);
  border-radius: 12px;
  padding: 4px 10px;
  font-size: 11px;
  cursor: pointer;
}
.dl-filter.active { background: var(--gs-accent); border-color: var(--gs-accent); color: var(--gs-on-accent); }
.dl-filter:hover:not(.active) { border-color: var(--gs-accent); color: var(--gs-accent); }

.dl-search {
  margin: 8px;
  padding: 8px;
  border: 1px solid var(--gs-border-4);
  border-radius: 4px;
  font-size: 12px;
  flex-shrink: 0;
}

.dl-group-header {
  padding: 6px 12px;
  font-size: 11px;
  font-weight: bold;
  color: var(--gs-text-3);
  background: var(--gs-surface-5);
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  user-select: none;
  position: sticky;
  top: 0;
  z-index: 1;
}
.dl-group-header:hover { background: var(--gs-surface-5); }
.dl-group-arrow { font-size: 9px; flex-shrink: 0; }
.dl-group-count { font-weight: normal; margin-left: auto; color: var(--gs-text-3); font-size: 10px; }

.dl-rows { overflow-y: auto; flex: 1; }

.dl-row {
  display: flex;
  align-items: center;
  padding: 8px 12px;
  border-bottom: 1px solid var(--gs-border-3);
  cursor: pointer;
  position: relative;
}
.dl-row:hover { background: var(--gs-surface-3); }
/* Selected device stays clearly marked (accent left bar + accent name) so you remember what you're
   working on. Uses inset box-shadow, not background, so it shows even over the per-status row colours. */
.dl-row.selected { box-shadow: inset 4px 0 0 var(--gs-accent); }
.dl-row.selected .dl-row-name { color: var(--gs-accent); font-weight: 700; }

.dl-row-icon { font-size: 17px; line-height: 1; margin-right: 10px; }
.dl-row-icon.status-moving { color: #2ecc71; }
.dl-row-icon.status-stopped { color: #FAB444; }
.dl-row-icon.status-idle { color: #9CC602; }
.dl-row-icon.status-offline { color: var(--gs-text-disabled); }

.dl-row-info { flex: 1; min-width: 0; }
.dl-row-name { font-size: 12px; color: var(--gs-text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dl-row-sub { font-size: 10px; color: var(--gs-text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.dl-row-right { text-align: right; flex-shrink: 0; }
.dl-row-speed { font-size: 11px; color: var(--gs-text-2); }
.dl-row-time { font-size: 10px; color: var(--gs-text-3); }
.dl-stale-fix { color: #E67E22; cursor: help; }

.dl-menu-btn {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  margin-left: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--gs-text-3);
  border-radius: 50%;
}
.dl-menu-btn:hover { background: var(--gs-surface-5); color: var(--gs-text-2); }

.dl-menu {
  position: absolute;
  top: 100%;
  right: 12px;
  z-index: 20;
  background: var(--gs-surface);
  border: 1px solid var(--gs-border);
  border-radius: 4px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.15);
  min-width: 190px;
  padding: 4px 0;
}
.dl-menu-item {
  padding: 8px 14px;
  font-size: 12px;
  color: var(--gs-text-2);
  cursor: pointer;
  white-space: nowrap;
}
.dl-menu-item:hover:not(.disabled) { background: var(--gs-surface-3); }
.dl-menu-item.disabled { color: var(--gs-text-disabled); cursor: default; }

#map { flex: 1; min-width: 0; min-height: 0; }

#map-toolbar {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 500;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.mt-btn {
  width: 32px;
  height: 32px;
  background: var(--gs-surface);
  border-radius: 4px;
  box-shadow: 0 1px 4px rgba(0,0,0,0.3);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  color: var(--gs-text-2);
}
/* Data-points toggle is part of the phone work — desktop already has the same switch as a labelled
   checkbox in the History panel, where there is room for words. */
#mt-points { display: none; }
.mt-btn:hover { background: var(--gs-surface-3); }
.mt-btn.active { background: var(--gs-accent); color: var(--gs-on-accent); }
.mt-btn.active:hover { background: #0090c0; }
.mt-sep { height: 1px; background: rgba(0,0,0,0.1); margin: 2px 4px; }
#mt-measure-readout {
  display: none;
  position: absolute;
  top: 10px;
  left: 52px;
  z-index: 500;
  background: var(--gs-surface);
  padding: 6px 10px;
  border-radius: 4px;
  box-shadow: 0 1px 4px rgba(0,0,0,0.3);
  font-size: 11px;
  color: var(--gs-text-2);
}

@media print {
  #top-bar, #toolbar, #sidebar, #sidebar-resize-handle, #map-toolbar, #mt-measure-readout, #device-panel { display: none !important; }
  #main-body, #map-area, #map { height: 100vh !important; width: 100% !important; }
}

.gs-popup-wrap .leaflet-popup-content-wrapper { border-radius: 4px; }
.gs-popup-title { font-weight: bold; color: var(--gs-accent); margin-bottom: 6px; font-size: 12px; }
.gs-popup-table { border-collapse: collapse; font-size: 11px; }
.gs-popup-table td { padding: 2px 6px 2px 0; vertical-align: top; }
.gs-popup-table td:first-child { color: var(--gs-text-3); white-space: nowrap; }
.gs-popup-table td:last-child { color: var(--gs-text-2); }
.gs-popup-detailed { display: inline-block; margin-top: 6px; color: var(--gs-accent); font-size: 11px; text-decoration: none; }
.gs-popup-detailed:hover { text-decoration: underline; }

#device-panel { flex-shrink: 0; max-height: min(320px, 45vh); overflow-y: auto; background: var(--gs-surface); border-top: 1px solid var(--gs-border); }
.dp-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--gs-surface-5);
  padding: 0 12px;
  height: 34px;
  flex-shrink: 0;
}
.dp-tabs { display: flex; gap: 4px; height: 100%; }
.dp-tab {
  display: flex;
  align-items: center;
  padding: 0 12px;
  font-size: 11px;
  color: var(--gs-text-3);
  cursor: pointer;
  border-bottom: 2px solid transparent;
}
.dp-tab.active { color: var(--gs-accent); border-bottom-color: var(--gs-accent); }
.dp-close { cursor: pointer; color: var(--gs-text-3); font-size: 15px; }
.dp-close:hover { color: var(--gs-text-2); }
.st-panel-checks { display: flex; flex-wrap: wrap; gap: 6px 20px; margin-bottom: 4px; }
.st-panel-check { display: flex; align-items: center; gap: 5px; font-size: 12px; cursor: pointer; white-space: nowrap; }
.dp-body { display: flex; flex-wrap: wrap; gap: 16px; padding: 12px 16px; }
.dp-col { min-width: 160px; flex: 1; }
.dp-col-wide { flex: 2; min-width: 240px; }
.dp-section-title { font-size: 10px; font-weight: bold; color: var(--gs-accent); margin: 8px 0 4px; }
.dp-section-title:first-child { margin-top: 0; }
.dp-kv { display: flex; justify-content: space-between; gap: 8px; font-size: 11px; padding: 3px 0; border-bottom: 1px solid var(--gs-border); }
/* Daily-statistics period selector — swipeable (‹ current ›) with dots. Touch-swipe on phone. */
.dp-period { display: flex; align-items: center; gap: 8px; margin: 0 0 4px; user-select: none; touch-action: pan-y; }
.dp-period-label { flex: 1; text-align: center; font-size: 12px; font-weight: 600; color: var(--gs-text); background: var(--gs-surface-2); border: 1px solid var(--gs-border-4); border-radius: 14px; padding: 5px 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dp-period-arrow { flex: 0 0 auto; width: 28px; height: 28px; border: 1px solid var(--gs-border-4); border-radius: 50%; background: var(--gs-surface-2); color: var(--gs-text-2); font-size: 15px; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; }
.dp-period-arrow:hover:not(:disabled) { border-color: var(--gs-accent); color: var(--gs-accent); }
.dp-period-arrow:disabled { opacity: 0.3; cursor: default; }
.dp-period-dots { display: flex; justify-content: center; gap: 5px; margin: 0 0 8px; }
.dp-period-dots .dp-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--gs-border-4); cursor: pointer; transition: background .12s; }
.dp-period-dots .dp-dot.on { background: var(--gs-accent); }
.dp-kv span:first-child { color: var(--gs-text-3); }
.dp-sensors-tool { cursor: pointer; margin-left: 8px; color: var(--gs-text-3); font-weight: normal; }
.dp-sensors-tool:hover { color: var(--gs-accent); }
.dp-sensor-picker { display: flex; flex-direction: column; gap: 4px; max-height: 200px; overflow-y: auto; }
.dp-kv span:last-child { color: var(--gs-text-2); text-align: right; }
.dp-tacho-issues { font-size: 10px; color: #c0392b; margin-top: 4px; line-height: 1.5; }
.dp-notes { width: 100%; height: 60px; padding: 6px; border: 1px solid var(--gs-border-4); border-radius: 4px; font-size: 11px; margin-bottom: 6px; resize: vertical; }
/* Day timeline — the drives and stops of the chosen period, in order, with clock times.
   Folded away behind its own toggle so the Daily-statistics totals stay the first thing on screen. */
.dp-tl-toggle { display: flex; align-items: center; gap: 6px; margin-top: 8px; padding: 7px 8px; font-size: 11px; font-weight: 600; color: var(--gs-text-2); background: var(--gs-surface-2); border: 1px solid var(--gs-border-3); border-radius: 6px; cursor: pointer; user-select: none; }
.dp-tl-toggle:hover { border-color: var(--gs-accent); color: var(--gs-accent); }
.dp-tl-toggle.open { border-bottom-left-radius: 0; border-bottom-right-radius: 0; border-bottom-color: transparent; }
.dp-tl-caret { color: var(--gs-text-4); font-size: 10px; }
.dp-tl-count { margin-left: auto; font-weight: normal; color: var(--gs-text-4); font-variant-numeric: tabular-nums; }
.dp-tl { border: 1px solid var(--gs-border-3); border-top: none; border-radius: 0 0 6px 6px; max-height: 320px; overflow-y: auto; }
.dp-tl-day { position: sticky; top: 0; z-index: 1; padding: 4px 8px; font-size: 10px; font-weight: bold; color: var(--gs-text-3); background: var(--gs-surface-4); border-bottom: 1px solid var(--gs-border-2); }
.dp-tl-row { display: flex; gap: 8px; padding: 6px 8px; border-bottom: 1px solid var(--gs-border); cursor: pointer; }
.dp-tl-row:last-child { border-bottom: none; }
.dp-tl-row:hover { background: var(--gs-surface-3); }
.dp-tl-row:focus-visible { outline: 2px solid var(--gs-accent); outline-offset: -2px; }
/* The row currently drawn on the map. Marked so that after scrolling the list you can still tell
   which leg the map is showing. */
.dp-tl-row.sel { background: var(--gs-accent-bg); }
.dp-tl-resolving { color: var(--gs-text-disabled); font-style: italic; }

/* Panel folded away so the map has the screen. Height is the bar and nothing else — the map above
   grows into what it gives up (GsMap.invalidateSize is called on both transitions). */
#device-panel.dp-collapsed { max-height: none; height: auto; overflow: hidden; }
.dp-peek {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 12px; height: 30px; box-sizing: border-box;
  font-size: 11px; color: var(--gs-text-2);
  background: var(--gs-surface-2); border-top: 1px solid var(--gs-border-3);
  cursor: pointer; user-select: none; touch-action: pan-y;
}
.dp-peek:hover { background: var(--gs-surface-3); }
.dp-peek:focus-visible { outline: 2px solid var(--gs-accent); outline-offset: -2px; }
.dp-peek-caret { color: var(--gs-accent); font-size: 9px; }
.dp-peek-name { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dp-peek-what { color: var(--gs-text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The affordance. Pushed right so it reads as a hint, not a label on the object. */
.dp-peek-hint { margin-left: auto; color: var(--gs-text-4); font-size: 10px; white-space: nowrap; flex-shrink: 0; }
@media (max-width: 640px) {
  .dp-peek { height: 36px; font-size: 12px; padding: 8px 12px; }
}
/* The state is carried by a colour rail rather than a word, so a long day is scannable at a glance.
   Same hues the device list already uses for moving/stopped, so the two views agree. */
.dp-tl-row.move { border-left: 3px solid #2ecc71; }
.dp-tl-row.stop { border-left: 3px solid #FAB444; }
/* Start over end, so the times form one narrow column the eye can run down. */
.dp-tl-time { flex: 0 0 42px; font-size: 11px; font-variant-numeric: tabular-nums; color: var(--gs-text); line-height: 1.35; }
.dp-tl-time span { display: block; color: var(--gs-text-4); }
.dp-tl-body { flex: 1; min-width: 0; }
.dp-tl-line { display: flex; align-items: baseline; gap: 6px; font-size: 11px; color: var(--gs-text-2); }
.dp-tl-dur { margin-left: auto; color: var(--gs-text-3); font-variant-numeric: tabular-nums; }
.dp-tl-meta { font-size: 10px; color: var(--gs-text-3); margin-top: 1px; font-variant-numeric: tabular-nums; }
.dp-tl-where { font-size: 10px; color: var(--gs-text-4); margin-top: 1px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* On a phone the panel is the whole screen, so the list can have more of it — and the addresses get
   to wrap onto a second line instead of being cut, since there is no column competing for the width. */
@media (max-width: 640px) {
  .dp-tl { max-height: 60vh; }
  .dp-tl-toggle { padding: 10px 8px; font-size: 12px; }
  .dp-tl-row { padding: 8px; }
  .dp-tl-time { flex-basis: 46px; font-size: 12px; }
  .dp-tl-line { font-size: 12px; }
  .dp-tl-meta, .dp-tl-where { font-size: 11px; }
  .dp-tl-where { white-space: normal; }
}

.dp-gauge { position: relative; height: 18px; background: var(--gs-surface-3); border-radius: 9px; overflow: hidden; margin-top: 4px; }
.dp-gauge-fill { height: 100%; background: var(--gs-accent); }
.dp-gauge span { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 10px; color: var(--gs-text-2); }
.dp-fuel-gauge { width: 100%; max-width: 160px; display: block; margin: 6px auto 0; }
.dp-fuel-gauge .gauge-track { stroke: var(--gs-border-2); }
.dp-fuel-gauge .gauge-needle { stroke: var(--gs-text-2); }
.dp-fuel-gauge .gauge-center { fill: var(--gs-text-2); }
.dp-fuel-gauge .gauge-label { fill: var(--gs-text-4); font-family: sans-serif; }
.dp-fuel-gauge .gauge-pct { font-family: sans-serif; }
.st-panel-section { border: 1px solid var(--gs-border-2); border-radius: 4px; margin-bottom: 6px; overflow: hidden; }
.st-panel-section .st-panel-checks { padding: 8px 12px 6px; }
.st-panel-summary { font-weight: 600; font-size: 12px; padding: 7px 10px; cursor: pointer; background: var(--gs-surface-2); user-select: none; list-style: none; display: flex; align-items: center; gap: 6px; }
.st-panel-summary::-webkit-details-marker { display: none; }
.st-panel-summary::before { content: 'â–¶'; font-size: 9px; transition: transform 0.15s; display: inline-block; color: var(--gs-text-3); }
details[open] .st-panel-summary::before { transform: rotate(90deg); }
.st-panel-sensor-hdr { font-size: 11px; color: var(--gs-text-3); padding: 0 12px 4px; }
.dp-sparkline { width: 100%; height: 80px; }
.dp-sparkline-range { display: flex; justify-content: space-between; font-size: 10px; color: var(--gs-text-3); margin-top: 2px; }

.ed-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}
.ed-modal {
  width: min(480px, 94vw);
  max-height: 90vh;
  max-width: 96vw;
  min-width: 320px;
  min-height: 200px;
  background: var(--gs-surface);
  border-radius: 10px;
  box-shadow: 0 16px 48px rgba(0,0,0,0.2), 0 4px 14px rgba(0,0,0,0.1);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* Resize is handled by the custom bottom-right drag handle (tableresize.js). The native `resize: both`
     grip was removed: overflow:hidden + border-radius clipped it, and it collided with the custom handle. */
}
/* Wide (tabbed) dialogs keep ONE constant size while the user moves between tabs/submenus — the
   height is fixed rather than content-driven, so switching from a tall tab to a short one no longer
   makes the box jump around (user request 2026-08-13). The body scrolls inside; a user drag-resize
   (tableresize.js) sets inline width/height, which still overrides this default. */
.ed-modal.ed-modal-wide { width: min(660px, 94vw); height: min(80vh, 720px); }
.ed-header {
  background: var(--gs-accent);
  color: var(--gs-on-accent);
  padding: 13px 18px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
  border-radius: 10px 10px 0 0;
}
.ed-title { font-size: 13px; font-weight: 600; letter-spacing: 0.1px; }
.ed-header-left { display: flex; align-items: center; gap: 10px; min-width: 0; }
.ed-btn-header {
  font-size: 10px;
  padding: 3px 8px;
  border-radius: 4px;
  background: rgba(255,255,255,0.18);
  cursor: pointer;
  white-space: nowrap;
}
.ed-btn-header:hover { background: rgba(255,255,255,0.3); }
.ed-close {
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  opacity: 0.8;
  padding: 3px 7px;
  border-radius: 4px;
  transition: background 0.12s, opacity 0.12s;
}
.ed-close:hover { background: rgba(255,255,255,0.22); opacity: 1; }
.ed-tabs {
  display: flex;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--gs-border);
  background: var(--gs-surface-2);
  flex-shrink: 0;
}
.ed-tab { padding: 8px 12px; font-size: 11px; color: var(--gs-text-3); cursor: pointer; border-bottom: 2px solid transparent; }
.ed-tab:hover:not(.disabled) { color: var(--gs-text-2); }
.ed-tab.active { color: var(--gs-accent); border-bottom-color: var(--gs-accent); }
.ed-tab.disabled { opacity: 0.35; cursor: default; }
.ed-body { padding: 16px; overflow-y: auto; flex: 1; }
/* When a module (Drivers, etc.) is rendered inside a dialog the inner cp-header/cp-title is
   redundant — the modal header already shows the title. Hide it; remaining icons (refresh,
   info tooltip) move to the right so they don't bunch up on the left. */
.ed-body .cp-header { margin-bottom: 10px; justify-content: flex-end; }
.ed-body .cp-title { display: none; }
.ed-section-title { font-size: 11px; font-weight: 600; color: var(--gs-accent); margin: 16px 0 8px; letter-spacing: 0.02em; }
.ed-section-title:first-child { margin-top: 0; }
.ed-field { margin-bottom: 10px; }
.ed-field label { display: block; font-size: 10px; font-weight: 500; color: var(--gs-text-2); margin-bottom: 4px; }
.ed-field input:focus, .ed-field select:focus {
  outline: none;
  border-color: var(--gs-accent);
  box-shadow: 0 0 0 3px rgba(0,169,223,0.12);
}
.ed-field input, .ed-field select {
  width: 100%;
  padding: 7px 10px;
  border: 1px solid var(--gs-border-3);
  border-radius: 5px;
  font-size: 12px;
  color: var(--gs-text-2);
  background: var(--gs-surface-2);
  box-sizing: border-box;
  transition: border-color 0.12s, box-shadow 0.12s;
}
/* Radio added 2026-08-22. `.ed-field input` above sets width:100% for text fields, and only checkbox
   was exempted — so every radio button in the app stretched to the full field width. Browsers centre
   a radio's dot inside its box, so the dot appeared floating mid-row with its label pushed to the far
   right edge, looking like a broken layout. Affects any radio inside .ed-field, not just one dialog. */
.ed-field input[type="checkbox"],
.ed-field input[type="radio"] {
  width: 16px;
  height: 16px;
  padding: 0;
  flex: 0 0 auto;
  border: none;
}
.ed-field-row { display: flex; align-items: flex-end; gap: 8px; flex-wrap: wrap; }
.ed-field-row label { flex-basis: 100%; }
.ed-field-row select { width: auto; flex-shrink: 0; }
.ed-field-row input { flex: 1; min-width: 80px; }
.ed-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 6px; min-height: 22px; }
.ed-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 8px;
  border-radius: 12px;
  background: var(--gs-surface-3);
  border: 1px solid var(--gs-border-3);
  font-size: 11px;
  color: var(--gs-text-2);
}
.ed-tag-x { cursor: pointer; color: var(--gs-text-3); font-weight: bold; line-height: 1; }
.ed-tag-x:hover { color: #c0392b; }
.ed-tag-hint { font-size: 11px; color: var(--gs-text-3); font-style: italic; }
.ed-suggest-wrap:empty { display: none; }
.ed-suggest-wrap {
  border: 1px solid var(--gs-border-3);
  border-radius: 5px;
  background: var(--gs-surface-2);
  margin-top: 2px;
  max-height: 140px;
  overflow-y: auto;
}
.ed-suggest-item { padding: 6px 10px; font-size: 11px; color: var(--gs-text-2); cursor: pointer; }
.ed-suggest-item:hover { background: var(--gs-surface-3); color: var(--gs-text); }
.ed-placeholder { color: var(--gs-text-3); font-size: 12px; padding: 20px 0; text-align: center; }
.ed-loading { padding: 30px; text-align: center; color: var(--gs-text-3); font-size: 12px; }
.ed-error { color: #c0392b; font-size: 11px; margin-bottom: 10px; }
.ed-footer {
  display: flex;
  gap: 8px;
  padding: 12px 16px;
  border-top: 1px solid var(--gs-border);
  flex-shrink: 0;
}
.ed-btn {
  padding: 8px 14px;
  border: 1px solid var(--gs-border-4);
  border-radius: 4px;
  background: var(--gs-surface);
  color: var(--gs-text-2);
  font-size: 12px;
  cursor: pointer;
}
.ed-btn:hover:not(:disabled) { background: var(--gs-surface-3); }
/* Disabled state, added 2026-08-22. Only .ed-btn-danger had one, so every other .ed-btn carrying a
   `disabled` attribute — 22 of them across 9 files — rendered at full strength with a pointer cursor
   and a live hover. An operator clicks it, nothing happens, and the button looks no different from a
   working one: indistinguishable from a broken feature. Found via the bulk-apply dialog, where Apply
   is deliberately gated behind Preview + Snapshot and gave no sign it was waiting for either. */
.ed-btn:disabled,
.ed-btn-primary:disabled { opacity: 0.4; cursor: not-allowed; }
.ed-btn-primary:hover:not(:disabled) { filter: brightness(1.08); }
/* Sensor health pills — a param merely existing in the latest position doesn't mean it works,
   so the Sensors tab grades each one over a window of history. Colour carries the same meaning
   as everywhere else in the app (ok / warn / danger tokens), never a bespoke palette. */
/* Decimal hours shown under each duration — 8 h 30 min is 8.5, which is the figure that actually
   gets typed into payroll and planning sheets, so showing both saves doing the sum by hand. */
.tc-dec { display: block; font-size: 10.5px; color: var(--gs-text-3); font-variant-numeric: tabular-nums; }
/* Number plate shown under a driver card number — a raw 16-digit card means nothing on its own, but
   "the card that drives 2DRH339" is instantly recognisable. */
.drv-plate { display: block; font-size: 10.5px; color: var(--gs-accent-2); }
.drv-search { width: 100%; box-sizing: border-box; margin: 2px 0 4px; }
.drv-owner-tag { display: inline-block; font-size: 10.5px; padding: 1px 7px; border-radius: 10px; background: var(--gs-surface-3, rgba(127,127,127,.16)); color: var(--gs-text-2); white-space: nowrap; }
.cp-dim { color: var(--gs-text-3, var(--gs-text-2)); }
.eu-vis-toggle { cursor: pointer; user-select: none; font-size: 14px; line-height: 1; display: inline-block; }
.eu-vis-toggle:hover { opacity: 0.7; }
/* Destructive action button. Deliberately outlined rather than filled: it sits beside the filled
   primary "Add selected", and two solid buttons of equal weight makes the destructive one too easy
   to hit by reflex. */
.ed-btn-danger {
  border-color: var(--gs-danger-fg);
  color: var(--gs-danger-fg);
  background: var(--gs-danger-bg);
}
.ed-btn-danger:hover:not(:disabled) { background: var(--gs-danger-fg); color: #fff; }
.ed-btn-danger:disabled { opacity: 0.45; cursor: default; }
.tc-totals-row td { border-top: 2px solid var(--gs-border-3); background: var(--gs-surface-3); }
.tc-totals-row .tc-dec { color: var(--gs-accent-2); }
/* Today's row stands out — the company reads today's compliance first. */
.tc-today-row td { background: rgba(0, 169, 223, 0.10); font-weight: 600; }
.tc-today-badge { display: inline-block; margin-left: 6px; padding: 0 6px; border-radius: 8px; font-size: 10px; font-weight: 700; background: var(--gs-accent-2, #00A9DF); color: #fff; vertical-align: middle; }
/* Line the numeric columns up under the totals (km + rest/work/drive/break), payroll-sheet style;
   Date and Beginning stay left-aligned. */
.tc-day-table th:nth-child(2), .tc-day-table td:nth-child(2),
.tc-day-table th:nth-child(n+4), .tc-day-table td:nth-child(n+4) { text-align: right; }

.sn-health { display: inline-block; font-size: 10px; font-weight: 600; padding: 1px 7px; border-radius: 9px; white-space: nowrap; }
.sn-health-live   { background: var(--gs-ok-bg);     color: var(--gs-ok-fg); }
.sn-health-frozen { background: var(--gs-warn-bg);   color: var(--gs-warn-fg); }
.sn-health-zero   { background: var(--gs-danger-bg); color: var(--gs-danger-fg); }
.sn-health-absent { background: var(--gs-surface-3); color: var(--gs-text-3); }
.sn-health-text   { background: var(--gs-accent-bg); color: var(--gs-accent-2); }
.sn-health-load   { background: var(--gs-surface-3); color: var(--gs-text-3); font-style: italic; }

.ed-btn-primary { background: var(--gs-accent); border-color: var(--gs-accent); color: var(--gs-on-accent); }
.ed-btn-primary:hover { opacity: 0.9; }
.ed-attr-row { display: flex; gap: 6px; margin-bottom: 6px; align-items: center; }
.ed-attr-key { width: 40%; padding: 6px; border: 1px solid var(--gs-border-4); border-radius: 4px; font-size: 11px; }
.ed-attr-val { flex: 1; padding: 6px; border: 1px solid var(--gs-border-4); border-radius: 4px; font-size: 11px; }
.ed-attr-del { cursor: pointer; color: var(--gs-text-3); padding: 0 4px; }
.ed-attr-del:hover { color: #c0392b; }
.ed-info-row { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid var(--gs-border); font-size: 12px; }
.ed-info-row span:first-child { color: var(--gs-text-3); }
.ed-field input[type="color"] { width: 60px; padding: 2px; height: 32px; cursor: pointer; }
.ed-icon-picker { display: flex; gap: 8px; }
.ed-icon-choice {
  font-size: 21px;
  padding: 4px 8px;
  border: 2px solid transparent;
  border-radius: 4px;
  cursor: pointer;
}
.ed-icon-choice:hover { background: var(--gs-surface-3); }
.ed-icon-choice.selected { border-color: var(--gs-accent); background: #00A9DF14; }
.ed-preview {
  background: var(--gs-surface-3);
  border-radius: 4px;
  padding: 8px 10px;
  font-size: 11px;
  color: var(--gs-text-2);
  margin-bottom: 12px;
}
.ed-preview-empty { color: var(--gs-text-3); }
.ed-note {
  font-size: 10px;
  color: var(--gs-text-3);
  background: var(--gs-surface-3);
  border-radius: 4px;
  padding: 8px 10px;
  margin-bottom: 12px;
  line-height: 1.5;
}
.ed-field-row input[type="checkbox"] { width: auto; flex-shrink: 0; margin-right: 4px; }

#control-panel { flex: 1; overflow-y: auto; padding: 16px 24px; }
.cp-loading { padding: 30px; text-align: center; color: var(--gs-text-3); font-size: 12px; }
/* Progress affordance for the tacho archive listing, added 2026-09-04. The archive is read off the
   NAS over Tailscale SMB and kerasus's 35,134 files measured 45.17s, so the plain .cp-loading text
   above is not enough on its own -- a motionless page for that long reads as broken. .td-loading
   only ever decorates a .cp-loading div, so the padding/centring/colour above still apply. */
.td-loading { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.td-spin { width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid var(--gs-border-2); border-top-color: var(--gs-accent);
  animation: tdSpin 0.8s linear infinite; }
.td-loading-msg { color: var(--gs-text-2); font-size: 12px; }
.td-loading-sub { color: var(--gs-text-3); font-size: 11px; max-width: 340px; line-height: 1.45; }
/* tabular-nums so the seconds counter does not jitter its own width as it ticks. */
.td-loading-el { color: var(--gs-text-3); font-size: 11px; font-variant-numeric: tabular-nums; }
@keyframes tdSpin { to { transform: rotate(360deg); } }
/* Slowed rather than stopped: a still spinner would defeat the whole point of the affordance. */
@media (prefers-reduced-motion: reduce) { .td-spin { animation-duration: 2.4s; } }
.cp-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; gap: 12px; padding-left: 10px; }
.cp-title-row { display: flex; align-items: center; gap: 6px; }
.cp-title { font-size: 15px; color: var(--gs-text-2); font-weight: bold; }
.cp-info { color: var(--gs-text-disabled); cursor: help; font-size: 13px; }
.cp-info:hover { color: var(--gs-text-3); }
.cp-search {
  padding: 7px 10px;
  border: 1px solid var(--gs-border-4);
  border-radius: 4px;
  font-size: 12px;
  width: 220px;
  flex-shrink: 0;
}
.cp-table-wrap { overflow-x: auto; border: 1px solid var(--gs-border-2); border-radius: 6px; }
.cp-table { width: 100%; border-collapse: collapse; font-size: 12px; }
/* Many-narrow-column raw-data tables (e.g. the raw attribute dump) look wrong stretched to 100% —
   width:100% with table-layout:auto forces the browser to spread a few dozen short numeric cells
   evenly across the full container, leaving huge dead gaps between values. Let this variant size to
   its own content instead; .cp-table-wrap's overflow-x:auto still scrolls it if that's wider than
   the panel. */
.cp-table-natural { width: auto; }
.cp-table-natural td, .cp-table-natural th { padding: 8px 10px; }
.cp-table th {
  position: relative;
  text-align: left;
  padding: 10px 14px;
  background: var(--gs-surface-3);
  color: var(--gs-text-2);
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  overflow: hidden;
}
.cp-table td { padding: 4px 10px; border-top: 1px solid var(--gs-border); color: var(--gs-text-2); }
.cp-table tr:hover td { background: var(--gs-accent-bg); }

/* Columns must fit the panel, and every value stays on ONE line — wrapping a 16-char driver card id
   into a stack of letters is worse than not seeing it. So keep nowrap and let anything too long for
   its column end in an ellipsis instead. Which column gets how much space is decided at runtime by
   tableresize.js's fitToContainer(): it measures each column's natural content width and shares the
   panel out in proportion, so short columns (ID, ACTIVE) stay narrow and hand their space to the
   long ones (E-MAIL, IP, REG. TIME) rather than every column being squeezed equally. */
/* Data cells: strictly one line, ellipsis if it doesn't fit. */
.cp-table td { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Headers: also one line, and never truncated — a header is the only clue to what a column holds,
   so it has to stay whole. Dropping the letter-spacing and tightening the padding buys back a lot
   of the width these uppercase labels were claiming. When even that isn't enough, tableresize.js
   steps the whole table's font down until every header and value fits on its line, rather than
   wrapping or ellipsising anything. */
.cp-table th {
  white-space: nowrap;
  letter-spacing: 0;
  padding: 8px 8px;
  overflow: hidden;
}
/* Descendants carry their own sizing; without this an inner span keeps its full width and spills
   past the ellipsised cell instead of being clipped with it. */
.cp-table td > * { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
/* The content-sized raw-dump variant keeps its natural column widths but may no longer exceed the
   panel — with the wrapping above it compresses rather than scrolling. */
.cp-table-natural { max-width: 100%; }

/* Rather than reintroduce a scrollbar when the space genuinely runs out, step the type and padding
   down. Reaching for the layout's own breathing room first keeps every column reachable. */
@media (max-width: 1200px) {
  .cp-table { font-size: 11px; }
  .cp-table th { padding: 8px 8px; }
  .cp-table td { padding: 4px 8px; }
}
@media (max-width: 900px) {
  .cp-table { font-size: 10px; }
  .cp-table th { padding: 6px 5px; letter-spacing: 0; }
  .cp-table td { padding: 3px 5px; }
  /* The action column is NOT narrowed here. It was, and that is what cut the icons off on a narrow
     window: four icons need 69px and this was handing them 58px. Text columns can give up space
     because text ellipsises; icons just disappear. */
}
/* Drag handle added by tableresize.js to every <th> — sits over the header's right edge. Only the
   dragged column's own width is ever changed (see tableresize.js) — no table-wide layout switch —
   so cells are deliberately NOT given overflow:hidden here: some (like row actions) hold several
   icon buttons side by side, and clipping overflow would hide the extra ones instead of just
   truncating text. Headers are always plain single-line labels, so ellipsis stays safe on those. */
.cp-col-resize-handle {
  position: absolute;
  top: 0; right: -5px; bottom: 0;
  width: 11px;
  cursor: col-resize;
  -webkit-user-select: none;
  user-select: none;
  z-index: 2;
}
/* A thin always-visible line at the actual column boundary (the handle's own hit area is centered
   on it, extending 5px either side) so the resize point is discoverable without first having to
   hover blindly along a header — especially on wide columns with mostly empty space, like the
   Driver card logbook's Time column, where the boundary can be far from the visible label text. */
.cp-col-resize-handle::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0; left: 5px;
  width: 1px;
  background: var(--gs-surface-5);
}
.cp-col-resize-handle:hover, .cp-col-resize-handle:active { background: rgba(0, 0, 0, 0.18); }
.cp-col-id { color: var(--gs-text-4); width: 1%; }
/* The column chooser sits inline in the title row, so it must not stretch like the full-width
   multi-selects inside dialog fields do. */
.cp-colchooser { display: inline-block; margin-left: 6px; vertical-align: middle; }
.cp-colchooser .gs-ms { width: 150px; }
.cp-colchooser .gs-ms-trigger { padding: 3px 8px; font-size: 11px; }
.cp-colchooser .gs-ms-panel { left: auto; right: 0; min-width: 190px; }
.cp-col-dim { color: var(--gs-text-4); }
.cp-col-center { text-align: center; }
.cp-col-actions { width: 1%; }
.cp-pagination { display: flex; align-items: center; gap: 8px; padding: 5px 12px; font-size: 11px; border-top: 1px solid var(--gs-border); }
.cp-pag-btn { background: none; border: 1px solid var(--gs-border-3); border-radius: 4px; cursor: pointer; padding: 1px 9px; font-size: 14px; line-height: 1.5; color: var(--gs-text-2); }
.cp-pag-btn:disabled { opacity: 0.35; cursor: default; }
.cp-pag-btn:not(:disabled):hover { background: var(--gs-surface-3); }
.cp-pag-info { flex: 1; color: var(--gs-text-2); text-align: center; }
.cp-pag-rows { border: 1px solid var(--gs-border-3); border-radius: 4px; padding: 2px 6px; font-size: 11px; color: var(--gs-text-2); background: var(--gs-surface); cursor: pointer; }
.cp-title-row .rp-icon-btn { font-size: 15px; padding: 0 6px; }
.cp-col-check { width: 1%; text-align: center; }
.cp-col-check input, .cp-col-check input[type="checkbox"] { width: 15px; height: 15px; }
.gs-tooltip.leaflet-tooltip {
  pointer-events: auto;
  -webkit-user-select: text;
  -moz-user-select: text;
  -ms-user-select: text;
  user-select: text;
  cursor: text;
}
.cp-sortable { cursor: pointer; user-select: none; }
.cp-sortable:hover { color: var(--gs-text-2); }
.cp-sort-arrow { font-size: 8px; }
.cp-selected-count { font-size: 11px; color: var(--gs-text-3); align-self: center; }
.obj-signal.online { filter: hue-rotate(80deg) saturate(3); }
.obj-signal.offline { opacity: 0.35; filter: grayscale(1); }
/* Sized to hold the widest action row whole, measured rather than estimated:
   5 icons x 28px + 4 gaps x 4px + 2 x 10px cell padding = 176. Was 144 for four icons; the
   save-as-template icon made it five, and an under-sized column here is what crushed the icons
   last time rather than wrapping them. Re-measure this if another action is ever added. */
.cp-table th:last-child, .cp-table td:last-child { width: 176px; text-align: center; }
/* The tacho day table's last column is the "Total" DATA column, not an actions column — keep it
   right-aligned and content-sized, overriding the actions-oriented rule above. */
.tc-day-table th:last-child, .tc-day-table td:last-child { width: auto; text-align: right; }
/* Trips report Summary is a key/value table, not an actions table — keep the value column content-
   width and left-aligned next to its label instead of the 176px centered actions box. */
.cp-table.rp-summary-table td:first-child { width: 1%; white-space: nowrap; }
.cp-table.rp-summary-table td:last-child { width: auto; text-align: left; }
/* Numeric report columns read right-aligned with tabular figures so digits line up by place value. */
.cp-table th.rp-num, .cp-table td.rp-num { text-align: right; font-variant-numeric: tabular-nums; }
/* Drives & stops grouped layout (GPS-Server style): one section per object with an Object/Period
   header block; a stop's position+address spans the numeric columns; coordinates link in green. */
.rp-obj-section { margin-bottom: 26px; }
.rp-obj-section + .rp-obj-section { border-top: 2px solid var(--gs-border-4); padding-top: 14px; }
.rp-obj-head {
  display: flex; gap: 28px; flex-wrap: wrap; align-items: baseline;
  background: var(--gs-surface-3); border: 1px solid var(--gs-border-4); border-radius: 6px;
  padding: 9px 14px; margin: 10px 0; font-size: 11.5px;
}
.rp-obj-head span { color: var(--gs-text-3); margin-right: 4px; }
.rp-trips-table td.rp-pos { white-space: normal; max-width: 420px; }
.rp-pos-link { color: #27ae60; text-decoration: none; font-variant-numeric: tabular-nums; }
.rp-pos-link:hover { text-decoration: underline; }
.rp-row-stop td { background: rgba(39, 174, 96, 0.06); }
/* Send-message template row: dropdown grows, save/delete buttons keep their size */
.msg-tpl-row { display: flex; gap: 6px; align-items: center; }
.msg-tpl-row select { flex: 1; min-width: 0; }
/* Device-list per-object "show on map" checkbox (left of the status dot) + its select-all header row. */
.dl-row .dl-vis { flex: 0 0 auto; margin: 0 4px 0 6px; cursor: pointer; }
.dl-vis-all { display: flex; align-items: center; gap: 6px; padding: 5px 12px; font-size: 11px; color: var(--gs-text-3); cursor: pointer; user-select: none; }
.dl-vis-all input { cursor: pointer; }
/* The vendor Leaflet layers.png is missing (Caddy serves index.html in its place), so the base-map
   switcher rendered as a blank white box that clashed with the dark theme. Theme it via the app's
   variables + give it an on-brand layers icon (inline SVG, no file dependency). */
.leaflet-control-layers { background: var(--gs-surface) !important; color: var(--gs-text) !important; border: 1px solid var(--gs-border-3) !important; border-radius: 6px !important; box-shadow: 0 1px 5px rgba(0,0,0,0.28) !important; }
.leaflet-control-layers-toggle { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2300A9DF' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolygon points='12 2 2 7 12 12 22 7 12 2'/%3E%3Cpolyline points='2 17 12 22 22 17'/%3E%3Cpolyline points='2 12 12 17 22 12'/%3E%3C/svg%3E") !important; background-size: 22px 22px !important; background-position: center !important; background-repeat: no-repeat !important; }
.leaflet-control-layers-expanded { padding: 8px 12px 8px 10px !important; }
.leaflet-control-layers label { color: var(--gs-text) !important; }
.leaflet-control-layers-separator { border-top: 1px solid var(--gs-border-3) !important; }
.cp-actions { display: flex; gap: 4px; justify-content: center; }
/* Action icons are controls, not text — they must never be squeezed. Default flex-shrink let the row
   crush them from their intended 28px down to 12px (and the link icon to 7px), which is what drew a
   half-finished key. flex:0 0 auto pins each at its real size; the column above is sized to hold the
   whole set, so they stay whole instead of compressing as more icons appear on a row. */
.cp-actions > * { flex: 0 0 auto; }
/* .cp-access-link belongs here too — it sits in the same actions row but was left out of this rule,
   so it rendered at its bare glyph width (15px) beside four 28px buttons and had no hover target. */
.cp-edit, .cp-delete, .cp-loginas, .cp-del-history, .cp-access-link {
  cursor: pointer;
  color: var(--gs-text-3);
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 5px;
  font-size: 14px;
  transition: background 0.12s, color 0.12s;
}
.cp-loginas:hover { background: var(--gs-warn-bg); color: var(--gs-warn-fg); }
.cp-edit:hover { background: var(--gs-accent-bg); color: var(--gs-accent); }
.cp-delete:hover { background: var(--gs-danger-bg); color: var(--gs-danger-fg); }
.cp-del-history:hover { background: var(--gs-warn-bg); color: var(--gs-warn-fg); }
.cp-bottom-toolbar { display: flex; gap: 8px; align-items: center; margin-top: 10px; }

.tc-detail-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.tc-detail-toolbar select, .tc-detail-toolbar input[type="date"] {
  padding: 7px 10px;
  border: 1px solid var(--gs-border-4);
  border-radius: 4px;
  font-size: 12px;
}
.tc-preset-group { display: flex; gap: 4px; }
.tc-preset-btn {
  padding: 6px 10px;
  border: 1px solid var(--gs-border-4);
  background: var(--gs-surface);
  color: var(--gs-text-2);
  border-radius: 4px;
  font-size: 11px;
  cursor: pointer;
}
.tc-preset-btn:hover { background: var(--gs-surface-3); }
.tc-preset-btn.active { background: var(--gs-accent); border-color: var(--gs-accent); color: var(--gs-on-accent); }
.tc-summary-bar { display: flex; gap: 20px; align-items: center; flex-wrap: wrap; padding: 10px 12px; background: var(--gs-surface-5); border-radius: 4px; font-size: 12px; color: var(--gs-text-2); margin-bottom: 12px; }
.tc-legend { display: flex; align-items: center; gap: 6px; }
.tc-activity-cell { font-size: 17px; text-align: center; }
.tc-swatch { display: inline-block; width: 12px; height: 12px; border-radius: 2px; }
.tc-timeline { display: flex; flex-direction: column; gap: 6px; }
.tc-day-row { display: flex; align-items: center; gap: 10px; }
.tc-day-label { width: 118px; flex-shrink: 0; font-size: 11px; color: var(--gs-text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tc-day-track { position: relative; flex: 1; height: 22px; background: var(--gs-surface-3); border-radius: 3px; overflow: hidden; }
.tc-seg { position: absolute; top: 0; bottom: 0; cursor: default; }
.tc-seg-drive { background: var(--gs-accent); }
.tc-seg-work { background: #f39c12; }
.tc-seg-avail { background: #9b59b6; }
.tc-seg-break { background: #9b59b6; }
.tc-seg-rest { background: var(--gs-surface-3); border-right: 1px solid var(--gs-border-4); }

#reports, #maintenance, #expenses, #tasks, #logbook, #dtc, #tacho, #objects-page, #drivers-page { flex: 1; overflow-y: auto; padding: 16px 24px; }
.rp-list-toolbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.rp-search input {
  padding: 7px 10px;
  border: 1px solid var(--gs-border-4);
  border-radius: 4px;
  font-size: 12px;
  width: 220px;
}
.rp-list-actions { display: flex; gap: 8px; }
.rp-icon-btn {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  background: var(--gs-surface-4);
  color: var(--gs-text-2);
  cursor: pointer;
  font-size: 14px;
  transition: background 0.12s, color 0.12s;
}
.rp-icon-btn:hover { background: var(--gs-accent); color: var(--gs-on-accent); }
.rp-run { cursor: pointer; color: var(--gs-text-3); }
.rp-run:hover { color: var(--gs-accent); }

.rp-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 4px 24px; align-items: start; }
.ed-field-inline { margin-bottom: 8px; }
.ed-field-inline label { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--gs-text-2); cursor: pointer; }

.eu-apikey-row { display: flex; gap: 6px; }
.eu-apikey-row input { flex: 1; min-width: 0; }
.eu-apikey-row .ed-btn { flex-shrink: 0; padding: 8px 12px; }
input.mono, .mono { font-family: ui-monospace, "SF Mono", Consolas, monospace; font-size: 11.5px; }
.eu-apikey-status { font-size: 10px; font-weight: bold; padding: 2px 8px; border-radius: 10px; margin-left: 6px; text-transform: uppercase; letter-spacing: 0.03em; }
.eu-apikey-status-active { background: var(--gs-ok-bg); color: var(--gs-ok-fg); }
.eu-apikey-status-inactive { background: var(--gs-surface-5); color: var(--gs-text-3); }
.eu-apikey-status-expired { background: var(--gs-warn-bg); color: var(--gs-warn-fg); }
.eu-apikey-status-none { background: var(--gs-surface-5); color: var(--gs-text-3); }
.cp-api-count { font-size: 10.5px; color: var(--gs-text-3); margin-top: 3px; white-space: nowrap; }
.sms-rule-msg { white-space: normal; max-width: 260px; }

.rp-objects {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 6px 12px;
  max-height: 130px;
  overflow-y: auto;
  border: 1px solid var(--gs-border);
  border-radius: 4px;
  padding: 8px;
}
.rp-obj-check { font-size: 12px; color: var(--gs-text-2); display: flex; align-items: center; gap: 6px; min-width: 0; }
.rp-obj-check input { flex: 0 0 auto; }

/* Control-panel objects: multi-select + bulk-actions gear menu */
.eu-obj-toolbar { display: flex; align-items: center; gap: 12px; margin: 6px 0 4px; flex-wrap: wrap; }
.eu-obj-selall-lbl { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--gs-text-2); }
.eu-obj-count { font-size: 11px; color: var(--gs-text-4); }
.eu-gear-wrap { position: relative; margin-left: auto; }
.eu-gear-btn { padding: 5px 12px; }
.eu-gear-menu { position: absolute; right: 0; top: calc(100% + 4px); min-width: 220px; background: var(--gs-surface); border: 1px solid var(--gs-border-3); border-radius: 8px; box-shadow: 0 10px 30px var(--gs-shadow); z-index: 20; padding: 4px; }
.eu-gear-menu[hidden] { display: none; }
.eu-gear-item { display: block; width: 100%; text-align: left; padding: 8px 12px; background: none; border: none; color: var(--gs-text); border-radius: 6px; cursor: pointer; font-size: 12px; }
.eu-gear-item:hover { background: var(--gs-surface-4); }
.eu-gear-danger { color: var(--gs-danger-fg); }
.eu-obj-hint { margin: 2px 0 6px; }
.eu-obj-item { display: inline-flex; align-items: center; gap: 3px; min-width: 0; }
.eu-obj-sel { flex: 0 0 auto; margin: 0 1px 0 0; cursor: pointer; }
.eu-obj-off { color: var(--gs-warn-fg); font-size: 10px; }
.ed-input { width: 100%; padding: 8px 10px; background: var(--gs-surface); border: 1px solid var(--gs-border-3); border-radius: 6px; color: var(--gs-text); font: inherit; box-sizing: border-box; }
.ed-input:focus { outline: none; border-color: var(--gs-accent); }
/* Account-editor tabs */
.eu-tabnav { display: flex; gap: 2px; border-bottom: 1px solid var(--gs-border-2); margin: 0 0 14px; flex-wrap: wrap; }
.eu-tabbtn { padding: 8px 14px; background: none; border: none; border-bottom: 2px solid transparent; color: var(--gs-text-3); cursor: pointer; font-size: 12px; }
.eu-tabbtn:hover { color: var(--gs-text); }
.eu-tabbtn.active { color: var(--gs-accent); border-bottom-color: var(--gs-accent); font-weight: 600; }
.eu-obj-tablewrap { max-height: 340px; overflow: auto; }
/* Tacho page: two-button sub-menu switching the Driver cards / Vehicles panels. */
.td-submenu { display: flex; gap: 6px; margin: 4px 0 12px; flex-wrap: wrap; }
.td-tab { background: var(--gs-surface-3); color: var(--gs-text-2); border: 1px solid var(--gs-border-2);
  border-radius: 6px; padding: 7px 14px; font-size: 12px; font-weight: 600; cursor: pointer;
  display: flex; align-items: center; gap: 7px; }
.td-tab:hover { background: var(--gs-surface-4); }
.td-tab-n { background: var(--gs-surface-5); color: var(--gs-text-3); border-radius: 10px;
  padding: 1px 7px; font-size: 11px; font-weight: 400; }
.td-tab-archive { margin-left: auto; background: transparent; border-style: dashed; color: var(--gs-text-3); }
.td-tab-archive:hover { background: var(--gs-surface-4); color: var(--gs-text); }
.td-tab-on { background: var(--gs-accent); border-color: var(--gs-accent); color: var(--gs-on-accent); }
.td-tab-on .td-tab-n { background: rgba(255,255,255,0.22); color: var(--gs-on-accent); }
.td-tab:focus-visible { outline: 2px solid var(--gs-accent); outline-offset: 2px; }

/* Tacho page: card-lane strip, shown only when an account holds more than one company card. Kept
   visually lighter than .td-tab so the page reads as "which card" above "which kind of file". */
.td-lanes { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 10px 0 4px; }
.td-lanes-cap { font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--gs-text-3); margin-right: 2px; }
.td-lane { background: transparent; color: var(--gs-text-2); border: 1px solid var(--gs-border-2);
  border-radius: 999px; padding: 4px 12px; font-size: 12px; font-weight: 600; cursor: pointer; }
.td-lane:hover { background: var(--gs-surface-4); color: var(--gs-text); }
.td-lane-on { background: var(--gs-surface-5); border-color: var(--gs-accent); color: var(--gs-text); }
.td-lane:focus-visible { outline: 2px solid var(--gs-accent); outline-offset: 2px; }

.ao-idrow { display: flex; gap: 6px; margin-top: 8px; }
.ao-idrow .ed-input { flex: 1; }
.ao-idresult { font-size: 11px; margin-top: 6px; }
.ao-idhit { color: var(--gs-ok-fg); }
.ao-idalready { color: var(--gs-text-2); }
.ao-idmiss { color: var(--gs-danger-fg); }
.ao-list { max-height: 340px; overflow-y: auto; border: 1px solid var(--gs-border-2); border-radius: 6px; padding: 8px; margin-top: 8px; }
.ao-item { cursor: pointer; }
/* Assign-object picker: Select-all bar + collapsible per-group sections. The 2-column object grid now
   lives inside each group body; the list itself is a plain block so group headers span full width. */
.ao-selall-row { display: flex; align-items: center; gap: 6px; margin-top: 10px; font-size: 12px; font-weight: 600; color: var(--gs-text); cursor: pointer; user-select: none; }
.ao-count { color: var(--gs-text-2); font-weight: 400; }
.ao-group + .ao-group { margin-top: 4px; }
.ao-group-header { display: flex; align-items: center; gap: 6px; cursor: pointer; padding: 4px 6px; font-size: 11px; font-weight: 600; color: var(--gs-text-2); background: var(--gs-surface-4); border-radius: 4px; user-select: none; }
.ao-group-header:hover { background: var(--gs-surface-5); }
.ao-group-arrow { font-size: 9px; transition: transform 0.12s; }
.ao-group.ao-collapsed .ao-group-arrow { transform: rotate(-90deg); }
.ao-group-count { margin-left: auto; color: var(--gs-text-3); font-weight: 400; }
/* Single object per row (a plain list, not a multi-column grid) — matches the app's other pickers. */
.ao-group-body { display: flex; flex-direction: column; gap: 1px; padding: 4px 2px 2px; }
.ao-group-body .ao-item { padding: 3px 6px; border-radius: 4px; }
.ao-group-body .ao-item:hover { background: var(--gs-surface-4); }
.ao-group.ao-collapsed .ao-group-body { display: none; }
/* Object-list top action bar: refresh + a grouped selection toolbar (visibility vs. delete) */
.cp-toolbar { display: flex; align-items: center; gap: 10px; margin: 0 0 8px; flex-wrap: wrap; }
.cp-toolbar-sel { display: inline-flex; align-items: center; gap: 10px; padding: 3px 6px 3px 11px; background: var(--gs-accent-bg); border: 1px solid var(--gs-border-2); border-radius: 8px; }
.cp-toolbar-sel .cp-selected-count { font-weight: 600; color: var(--gs-accent-2); }
.cp-toolbar-group { display: inline-flex; align-items: center; gap: 2px; padding: 2px 5px; background: var(--gs-surface); border: 1px solid var(--gs-border-3); border-radius: 6px; }
.cp-toolbar-danger { border-color: var(--gs-danger-fg); }
.cp-toolbar-danger .rp-icon-btn:hover { background: var(--gs-danger-bg); }
.cp-toolbar-clear { color: var(--gs-text-4); font-size: 17px; line-height: 1; }
.cp-toolbar-clear:hover { color: var(--gs-text); }

/* ── Collapsible multi-select (GsMultiSelect) ────────────────────────────────
   Collapsed it reads like a <select>; open it holds a search box, a select-all row and the
   checkboxes. Everything is driven by the --gs-* tokens, so light and dark both follow the
   theme without a second rule set. */
.gs-ms { position: relative; min-width: 0; }
.gs-ms-trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  padding: 5px 8px;
  font-size: 12px;
  font-family: inherit;
  text-align: left;
  color: var(--gs-text-3);
  background: var(--gs-surface);
  border: 1px solid var(--gs-border-4);
  border-radius: 4px;
  cursor: pointer;
}
.gs-ms-trigger:disabled { cursor: default; opacity: 0.7; }
.gs-ms-trigger:focus-visible { outline: 2px solid var(--gs-accent); outline-offset: 1px; }
.gs-ms.gs-ms-has .gs-ms-summary { color: var(--gs-text-2); }
.gs-ms-summary { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.gs-ms-caret { flex: 0 0 auto; font-size: 10px; color: var(--gs-text-3); }
.gs-ms.gs-ms-open .gs-ms-caret { transform: rotate(180deg); }

.gs-ms-panel {
  position: absolute;
  z-index: 1200;
  top: calc(100% + 2px);
  left: 0;
  right: 0;
  min-width: 210px;
  max-height: 280px;
  display: flex;
  flex-direction: column;
  background: var(--gs-surface);
  border: 1px solid var(--gs-border-3);
  border-radius: 4px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.22);
}
/* MUST outrank the display:flex above. The close path sets the `hidden` attribute, which only works
   through the user-agent rule [hidden]{display:none} — a bare class selector setting display beats
   it on specificity, and the panel stays on screen with hidden="" set. Asserting on the .hidden
   property looks fine in that state; only the computed style tells the truth. */
.gs-ms-panel[hidden] { display: none; }
.gs-ms-panel.gs-ms-up { top: auto; bottom: calc(100% + 2px); }
.gs-ms[data-ms-compact] .gs-ms-panel { max-height: 220px; }
/* Object-data-list chooser: a roomier dropdown than the default — this list carries every section,
   field, stat AND every sensor across the account, so give it more height and width to scroll in. */
.st-dp-picker .gs-ms-panel { min-width: 360px; max-height: min(66vh, 560px); }
.st-dp-picker .gs-ms-list { flex: 1 1 auto; min-height: 0; }
@media (max-width: 480px) { .st-dp-picker .gs-ms-panel { min-width: 0; } }

.gs-ms-search { padding: 6px; border-bottom: 1px solid var(--gs-border); flex: 0 0 auto; }
.gs-ms-search input {
  width: 100%;
  padding: 4px 7px;
  font-size: 11px;
  font-family: inherit;
  color: var(--gs-text-2);
  background: var(--gs-surface-2);
  border: 1px solid var(--gs-border-3);
  border-radius: 3px;
}
.gs-ms-list { overflow-y: auto; padding: 2px 0; }

.gs-ms-item {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 4px 9px;
  font-size: 12px;
  color: var(--gs-text-2);
  cursor: pointer;
  min-width: 0;
}
.gs-ms-item:hover { background: var(--gs-surface-3); }
.gs-ms-item input { flex: 0 0 auto; margin: 0; }
.gs-ms-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.gs-ms-all { border-bottom: 1px solid var(--gs-border); font-weight: 600; flex: 0 0 auto; }
.gs-ms-group {
  padding: 6px 9px 2px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--gs-text-3);
  text-transform: uppercase;
}
.gs-ms-noresult { padding: 8px 9px; font-size: 11px; color: var(--gs-text-3); }

/* ---- Dropdowns: more visible + enlargeable (user request 2026-08-07) ---- */
/* Multi-select pickers (object/device/column/data-list choosers): taller by default, drag-to-enlarge
   from the corner, stronger edge + shadow so they stand off the page, and roomier rows. */
.gs-ms-panel {
  min-width: 240px;
  max-height: 62vh;
  resize: both;
  overflow: hidden;
  border-color: var(--gs-border-4);
  box-shadow: 0 14px 40px var(--gs-shadow), 0 0 0 1px var(--gs-border-3);
}
.gs-ms-list { flex: 1 1 auto; min-height: 0; }   /* fill the (now resizable) panel and scroll inside */
.gs-ms-item { padding: 6px 10px; }               /* larger click targets */
.gs-ms-trigger { border-color: var(--gs-border-4); }
.gs-ms-trigger:hover { border-color: var(--gs-accent); }

/* Gear / actions pop-up menus: stronger edge + shadow, a touch wider. */
.eu-gear-menu { min-width: 240px; border-color: var(--gs-border-4); box-shadow: 0 14px 40px var(--gs-shadow); }

/* Device-panel sensor picker: taller and drag-resizable instead of a fixed 200px cap. */
.dp-sensor-picker {
  max-height: 48vh;
  resize: vertical;
  overflow: auto;
  border: 1px solid var(--gs-border-4);
  border-radius: 6px;
  padding: 6px;
}

/* Native <select> fields: a clear custom caret + hover border so they read plainly as dropdowns. */
.ed-field select {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='7' viewBox='0 0 12 7'><path d='M1 1l5 5 5-5' fill='none' stroke='%239aa7b4' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 10px center;
  padding-right: 30px;
}
.ed-field select:hover { border-color: var(--gs-accent); }
#rp-map { height: min(500px, 60vh); border-radius: 4px; max-width: 100%; }
.rp-export-row { display: flex; gap: 8px; margin-top: 10px; }
.rp-modal-slot .ed-modal-wide { width: min(820px, 96vw); }
.rp-modal-slot .ed-body { max-height: 75vh; }

/* â”€â”€ Bottom nav hidden on desktop; shown via mobile media query below â”€â”€â”€â”€ */
#gs-bottom-nav { display: none; }

/* â”€â”€ Mobile sidebar toggle button (hidden on desktop) â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ */
#gs-sidebar-toggle {
  display: none; /* shown only in mobile media query below */
  position: absolute;
  bottom: 72px;
  right: 12px;
  z-index: 450;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--gs-accent);
  color: var(--gs-on-accent);
  font-size: 21px;
  border: none;
  cursor: pointer;
  align-items: center;
  justify-content: center;
  box-shadow: 0 3px 10px rgba(0,0,0,0.35);
  line-height: 1;
}
#gs-sidebar-toggle:active { background: #0090c0; }
#gs-sidebar-overlay {
  display: none;
  position: absolute;
  inset: 0;
  background: rgba(0,0,0,0.45);
  z-index: 399;
}
#gs-sidebar-overlay.gs-visible { display: block; }

/* â”€â”€ Log manager — dropdown + modals â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ */
.lm-dropdown {
  position: fixed;
  z-index: 2000;
  background: var(--gs-surface);
  border: 1px solid var(--gs-border-2);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.16);
  min-width: 160px;
  padding: 4px 0;
  overflow: hidden;
}
.lm-dd-item {
  padding: 9px 16px;
  cursor: pointer;
  font-size: 12px;
  white-space: nowrap;
  transition: background 0.1s;
}
.lm-dd-item:hover { background: var(--gs-accent-bg); }
.lm-modal { width: min(720px, 94vw); }
.lm-body { display: flex; flex-direction: column; gap: 0; min-height: 120px; }
.lm-loading, .lm-empty, .lm-err { padding: 24px; text-align: center; color: var(--gs-text-3); font-size: 12px; }
.lm-err { color: #c0392b; }
.lm-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--gs-border);
  flex-shrink: 0;
}
.lm-month-label { font-size: 12px; font-weight: 600; color: var(--gs-text); flex: 1; }
.lm-btn {
  display: inline-block;
  padding: 4px 10px;
  border: 1px solid var(--gs-border-4);
  border-radius: 5px;
  background: var(--gs-surface-2);
  color: var(--gs-text);
  font-size: 11px;
  cursor: pointer;
  text-decoration: none;
  line-height: 1.5;
  transition: background 0.1s, border-color 0.1s;
}
.lm-btn:hover { background: var(--gs-accent-bg); border-color: var(--gs-accent); color: #006fa6; }
.lm-btn:disabled { opacity: 0.45; cursor: default; pointer-events: none; }
.lm-btn-dl { border-color: var(--gs-accent); color: var(--gs-accent); background: var(--gs-accent-bg); }
.lm-btn-dl:hover { background: var(--gs-accent-bg); }
.lm-scroll-area { overflow-y: auto; overflow-x: auto; max-height: 55vh; flex: 1; }
.lm-file-table, .lm-entry-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 11.5px;
}
.lm-file-table th, .lm-file-table td,
.lm-entry-table th, .lm-entry-table td {
  padding: 7px 10px;
  border-bottom: 1px solid var(--gs-border);
  text-align: left;
  vertical-align: middle;
}
.lm-file-table th, .lm-entry-table th { background: var(--gs-surface-3); font-weight: 600; font-size: 10.5px; color: var(--gs-text-3); }
.lm-file-table tr:hover td, .lm-entry-table tr:hover td { background: var(--gs-surface-2); }
/* Same fit-the-panel rule as .cp-table above — the log viewer is the other place with wide rows
   (long file paths and raw log lines, neither of which contains a space to wrap at), so it needs
   overflow-wrap for the same reason. .lm-scroll-area keeps overflow-x as a last-resort fallback. */
.lm-file-table th, .lm-file-table td,
.lm-entry-table th, .lm-entry-table td { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 0; }
@media (max-width: 1200px) {
  .lm-file-table, .lm-entry-table { font-size: 10.5px; }
  .lm-file-table th, .lm-file-table td,
  .lm-entry-table th, .lm-entry-table td { padding: 6px 7px; }
}
@media (max-width: 900px) {
  .lm-file-table, .lm-entry-table { font-size: 10px; }
  .lm-file-table th, .lm-file-table td,
  .lm-entry-table th, .lm-entry-table td { padding: 5px 5px; }
}
/* The generic cell rule above forces max-width:0 + overflow:hidden (ellipsis for long log paths); on
   the Actions cell that collapsed its View/Download buttons to a sliver. This more-specific selector
   (0,2,1 beats the 0,1,1 generic td rule) exempts the actions cell so its buttons keep their width. */
.lm-file-table td.lm-actions { display: flex; gap: 6px; align-items: center; white-space: nowrap; max-width: none; overflow: visible; }
.lm-mono { font-family: 'Courier New', monospace; font-size: 11px; }
.lm-ev-badge {
  display: inline-block;
  padding: 2px 7px;
  border-radius: 10px;
  font-size: 10px;
  font-weight: 600;
  white-space: nowrap;
}
.lm-ev-ok   { background: var(--gs-ok-bg); color: var(--gs-ok-fg); }
.lm-ev-out  { background: var(--gs-surface-3); color: var(--gs-text-3); }
.lm-ev-fail { background: var(--gs-danger-bg); color: var(--gs-danger-fg); }
.lm-cur-badge { display: inline-block; padding: 1px 5px; border-radius: 8px; font-size: 10px; font-weight: 600; background: var(--gs-warn-bg); color: var(--gs-warn-fg); margin-left: 4px; vertical-align: middle; }
.lm-sz { color: var(--gs-text-3); }

/* â”€â”€ Settings Objects tab — visibility + edit buttons â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ */
.st-obj-actions { display: flex; gap: 6px; align-items: center; }
.st-vis-btn, .st-edit-obj-btn {
  border: none;
  background: none;
  cursor: pointer;
  font-size: 15px;
  padding: 2px 5px;
  border-radius: 4px;
  line-height: 1;
  transition: background 0.12s, opacity 0.12s;
  opacity: 0.7;
}
.st-vis-btn:hover, .st-edit-obj-btn:hover { background: var(--gs-accent-bg); opacity: 1; }
.st-vis-btn:disabled { opacity: 0.35; cursor: default; }
.st-vis-hidden { opacity: 0.35; }
.st-vis-hidden:hover { opacity: 0.7; }

/* Settings → Objects: search + multi-select toolbar and sortable headers */
.st-obj-toolbar { display: flex; align-items: center; gap: 10px; padding: 8px 0 10px; flex-wrap: wrap; }
.st-obj-toolbar .st-obj-search { flex: 0 0 240px; padding: 5px 8px; border: 1px solid var(--gs-border-4); border-radius: 4px; font-size: 12px; }
.st-obj-toolbar .st-obj-spacer { flex: 1 1 auto; }
.st-obj-selcount { font-size: 11px; color: var(--gs-text-2); }
.st-obj-sort { cursor: pointer; user-select: none; white-space: nowrap; }
.st-obj-sort:hover { color: var(--gs-accent); }
.st-obj-check, #st-obj-selall { cursor: pointer; }

.st-group-toolbar { display: flex; align-items: center; gap: 8px; padding: 10px 0 8px; }
.st-new-group-input { flex: 0 0 220px; padding: 5px 8px; border: 1px solid var(--gs-border-4); border-radius: 4px; font-size: 12px; }
.st-group-save-btn, .st-group-cancel-btn { font-size: 13px; }
.st-empty-note { color: var(--gs-text-3); font-size: 12px; padding: 16px 0; }
.st-group-sel { border: 1px solid var(--gs-border-3); border-radius: 4px; padding: 3px 6px; font-size: 11px; color: var(--gs-text-2); background: var(--gs-surface-2); cursor: pointer; }
.st-group-sel:disabled { opacity: 0.4; }
.st-new-group-form { background: var(--gs-surface-3); border: 1.5px solid var(--gs-border-3); border-radius: 8px; padding: 14px 16px; margin-bottom: 12px; display: flex; flex-direction: column; gap: 10px; }
.st-new-group-row { display: flex; gap: 8px; align-items: center; }
.st-device-picker { display: flex; flex-direction: column; }
.st-device-picker-toggle { display: flex; align-items: center; gap: 7px; background: none; border: 1.5px solid var(--gs-border-3); border-radius: 6px; padding: 7px 12px; font-size: 12px; color: var(--gs-text); cursor: pointer; text-align: left; width: 100%; }
.st-device-picker-toggle:hover { border-color: var(--gs-accent); background: var(--gs-accent-bg); }
.st-device-picker-arrow { font-size: 10px; color: var(--gs-text-3); }
.st-device-picker-count { margin-left: auto; background: var(--gs-accent); color: var(--gs-on-accent); font-size: 10px; font-weight: 600; padding: 1px 8px; border-radius: 10px; }
.st-device-picker-hint { margin-left: auto; font-size: 10px; color: var(--gs-text-4); font-style: italic; }
.st-device-picker-list { border: 1.5px solid var(--gs-border-3); border-top: none; border-radius: 0 0 6px 6px; background: var(--gs-surface); max-height: 220px; overflow-y: auto; }
.st-device-check-row { display: flex; align-items: center; gap: 10px; padding: 7px 12px; cursor: pointer; font-size: 12px; color: var(--gs-text); border-bottom: 1px solid var(--gs-border-2); }
.st-device-check-row:last-child { border-bottom: none; }
.st-device-check-row:hover { background: var(--gs-surface-3); }
.st-device-check-row input[type="checkbox"] { width: 15px; height: 15px; accent-color: var(--gs-accent); flex-shrink: 0; cursor: pointer; }
.st-new-group-actions { display: flex; gap: 8px; }
.st-color-field { display: flex; align-items: center; gap: 8px; }
.st-color-field input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--gs-accent); cursor: pointer; flex-shrink: 0; }
.st-color-field input[type="color"] { width: 44px; height: 28px; padding: 2px; border: 1px solid var(--gs-border-3); border-radius: 4px; cursor: pointer; background: none; }
.st-group-expand-btn { cursor: pointer; user-select: none; font-size: 10px; color: var(--gs-text-3); display: inline-block; padding: 2px 4px; }
.st-group-expand-btn:hover { color: var(--gs-text); }
.st-group-members-row > td { padding: 0 4px 8px !important; }
.st-group-members { background: var(--gs-surface-3); border-radius: 4px; padding: 8px 10px; }
.st-group-device-item { display: flex; align-items: center; justify-content: space-between; padding: 3px 0; border-bottom: 1px solid var(--gs-border-2); font-size: 12px; color: var(--gs-text); }
.st-group-device-item:last-of-type { border-bottom: none; }
.st-group-remove-device { background: none; border: none; cursor: pointer; color: var(--gs-text-4); font-size: 14px; padding: 0 4px; line-height: 1; }
.st-group-remove-device:hover { color: #c0392b; }
.st-group-add-row { display: flex; align-items: center; gap: 6px; margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--gs-border-2); }
.st-group-add-sel { flex: 1; border: 1px solid var(--gs-border-4); border-radius: 4px; padding: 4px 6px; font-size: 11px; color: var(--gs-text); }

@media (max-width: 640px) {
  /* â”€â”€ Reports/dialogs â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ */
  .rp-grid { grid-template-columns: 1fr; }
  .rp-objects { grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); }
  .ed-modal, .ed-modal-wide, .rp-modal-slot .ed-modal-wide { width: 96vw; max-height: 90vh; }
  /* Disable JS resize handle on mobile — touch resize is not useful */
  .gs-dlg-resize-handle { display: none; }

  /* â”€â”€ Map toolbar — hide labels/measure/fullscreen on mobile â”€â”€ */
  #mt-labels, #mt-measure, #mt-fullscreen { display: none; }

  /* â”€â”€ Toolbar — compact icon-only mode on mobile â”€â”€ */
  /* The toolbar must FIT the screen, not scroll off it. A phone is 360-430px wide and the bar can
     carry a dozen icons, so fixed-width items always overflowed — the right-hand ones (theme, logout)
     simply sat off-screen with nothing to indicate it. Now the two sides share the width and the items
     inside each divide what they get, so every icon is reachable at any phone width.
     overflow-x stays as a last-resort safety net for an implausible number of items. */
  #toolbar { height: 52px; padding: 0 2px; overflow-x: auto; -webkit-overflow-scrolling: touch; gap: 0; }
  #toolbar .tb-left { flex: 1 1 auto; min-width: 0; }
  #toolbar .tb-right { flex: 0 0 auto; }
  .tb-side { min-width: 0; }
  /* flex-shrink is re-enabled (the desktop rule pins it to 0) and min-width drops to a thumb-sized
     floor, so N icons divide the available width evenly instead of pushing each other off the edge. */
  .tb-item { width: auto; min-width: 0; flex: 1 1 0; height: 48px; padding: 0 2px; }
  .tb-label { display: none; }
  .tb-icon { font-size: 19px; }
  .tb-badge { top: 1px; right: 2px; }
  .tb-desktop-only { display: none !important; }
  /* Branding is not a control. On a phone it was eating ~90px in the middle of the one row the
     operator actually needs, so it steps aside there — the page title and the login screen still
     carry it. */
  .tb-brand { display: none; }
  .tb-sep-v { margin: 0 1px; }
  /* Theme toggle sized against Logout so the last two controls read as a matched pair, with room
     between them — Logout is the one irreversible thing on the bar and should not be a thumb-width
     from a toggle you press casually. */
  #tb-theme { margin-right: 12px; }
  #tb-theme .tb-icon { width: 22px; height: 22px; }
  #tb-theme .tb-icon svg { width: 22px; height: 22px; }
  .tb-icon-exit { font-size: 11px !important; }

  /* Map tools 25% down from 32px/14px: the toolbar floats over the map and on a phone it was taking
     more of it than a set of secondary controls should. */
  #map-toolbar { gap: 3px; }
  .mt-btn { width: 24px; height: 24px; border-radius: 3px; box-shadow: 0 1px 3px rgba(0,0,0,0.3); font-size: 11px; }
  /* Data points: on a phone the History panel's labelled checkbox is off-screen while you are looking
     at the map, so the same switch is given a button here. */
  #mt-points { display: flex; }
  /* Theme and Logout sat shoulder to shoulder at the right edge, and Logout is the one control here
     you never want hit by accident. Push the theme toggle left so there is a deliberate gap between
     "change how it looks" and "end the session". */
  #tb-theme { margin-right: 10px; }

  /* â”€â”€ Sidebar — fixed slide-over from the left â”€â”€â”€ */
  #sidebar {
    position: fixed;
    top: 0;
    left: 0;
    width: 82vw;
    max-width: 300px;
    height: 100%;
    z-index: 1000;
    transform: translateX(-105%);
    transition: transform 0.22s ease;
    overflow-y: auto;
    box-shadow: none;
    border-right: 1px solid var(--gs-border-2);
  }
  #sidebar.gs-mobile-open {
    transform: translateX(0);
    box-shadow: 4px 0 16px rgba(0, 0, 0, 0.25);
  }
  #sidebar-resize-handle { display: none; }
  #gs-sidebar-overlay {
    position: fixed;
    inset: 0;
    z-index: 999;
    background: rgba(0,0,0,0.35);
  }
  #gs-sidebar-toggle { display: none !important; }

  /* â”€â”€ Main body: leave room for bottom nav â”€â”€â”€â”€â”€â”€â”€ */
  #main-body { padding-bottom: 0; }

  /* â”€â”€ Bottom navigation bar â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ */
  /* Retired 2026-08-21: this bar carried Home / Objects / Reports, which are the same three buttons
     already sitting in the toolbar above it — the icons appeared twice on every phone screen. Hidden
     here rather than deleted in app.js because wireMobileBottomNav() also owns the sidebar open/close
     API that the rest of app.js calls (lines 298/300/375); the element is still built and still tracks
     its active tab, it is simply never drawn. Restore by putting `display: flex` back. */
  #gs-bottom-nav {
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    height: 56px;
    background: var(--gs-surface);
    border-top: 1px solid var(--gs-border-2);
    z-index: 300;
    box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.08);
  }
  .gsbn-item {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .gsbn-item:active { background: var(--gs-surface-4); }
  .gsbn-icon { font-size: 24px; color: var(--gs-text-3); line-height: 1; }
  .gsbn-label { display: none; }   /* icons-only bottom nav on phone — the labels are redundant */
  .gsbn-item.active .gsbn-icon { color: var(--gs-accent); }

  /* â”€â”€ Popovers / Places panel — clamp to viewport â”€ */
  .gs-popover {
    left: 6px !important;
    right: 6px !important;
    width: auto !important;
    min-width: unset !important;
    max-width: calc(100vw - 12px) !important;
    max-height: 75vh;
    overflow-y: auto;
  }
  .pl-panel {
    left: 6px !important;
    right: 6px !important;
    width: auto !important;
    max-height: 70vh;
  }

  /* â”€â”€ Login page â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ */
  .login-card { width: 100%; padding: 24px 20px; }

  /* â”€â”€ Places draw toolbar â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ */
  .pl-draw-toolbar { font-size: 10px; padding: 6px 10px; gap: 6px; }
  .pl-draw-hint { display: none; } /* hint text too long for mobile */

  /* â”€â”€ Device panel — Graph and Messages tabs desktop-only â”€â”€ */
  .dp-tab-desktop { display: none; }
}

.gs-popover {
  position: fixed;
  z-index: 2000;
  background: var(--gs-surface);
  border-radius: 4px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.25);
  padding: 12px;
  min-width: 220px;
}
.gs-popover-title { font-size: 11px; font-weight: bold; color: var(--gs-accent); margin-bottom: 8px; }
.gs-popover-field { margin-bottom: 8px; }
.gs-popover-field label { display: block; font-size: 10px; color: var(--gs-text-3); margin-bottom: 2px; }
.gs-popover-field input {
  width: 100%;
  padding: 6px 8px;
  border: 1px solid var(--gs-border-4);
  border-radius: 4px;
  font-size: 12px;
}
.gs-popover-status { font-size: 10px; color: var(--gs-text-3); margin-top: 6px; }
.gs-popover-note { font-size: 10px; color: #b45309; margin-bottom: 8px; }

/* â”€â”€ Places panel â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ */
.pl-panel {
  position: fixed;
  z-index: 2000;
  background: var(--gs-surface);
  border-radius: 4px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.28);
  width: 260px;
  max-height: 480px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.pl-tabs {
  display: flex;
  border-bottom: 1px solid var(--gs-border-2);
}
.pl-tab {
  flex: 1;
  padding: 9px 6px;
  font-size: 11px;
  font-weight: 600;
  text-align: center;
  cursor: pointer;
  color: var(--gs-text-4);
  border-bottom: 2px solid transparent;
  transition: color .15s, border-color .15s;
}
.pl-tab:hover { color: var(--gs-accent); }
.pl-tab.pl-active { color: var(--gs-accent); border-bottom-color: var(--gs-accent); }
.pl-list {
  flex: 1;
  overflow-y: auto;
  padding: 4px 0;
}
.pl-empty { padding: 14px 12px; font-size: 11px; color: var(--gs-text-4); text-align: center; }
.pl-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 12px;
  font-size: 12px;
  border-bottom: 1px solid var(--gs-border);
}
.pl-item:last-child { border-bottom: none; }
.pl-dot {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  flex-shrink: 0;
}
.pl-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pl-row-actions { display: flex; gap: 4px; flex-shrink: 0; }
.pl-icon-btn {
  background: none;
  border: none;
  cursor: pointer;
  padding: 2px 4px;
  font-size: 13px;
  color: var(--gs-text-3);
  border-radius: 3px;
  line-height: 1;
}
.pl-icon-btn:hover { background: var(--gs-surface-3); color: var(--gs-accent); }
.pl-icon-btn.pl-danger:hover { background: var(--gs-danger-bg); color: var(--gs-danger-fg); }
.pl-footer {
  padding: 8px 12px;
  border-top: 1px solid var(--gs-border-2);
}
.pl-add-btn { width: 100%; }

/* form */
.pl-form-header {
  background: var(--gs-accent);
  color: var(--gs-on-accent);
  padding: 10px 14px;
  font-size: 12px;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 6px;
}
.pl-form-body { padding: 12px 14px; display: flex; flex-direction: column; gap: 10px; }
.pl-field { display: flex; flex-direction: column; gap: 4px; }
.pl-field label { font-size: 10px; color: var(--gs-text-4); font-weight: 600; }
.pl-field input[type="text"] {
  padding: 6px 8px;
  border: 1px solid var(--gs-border-4);
  border-radius: 4px;
  font-size: 12px;
  width: 100%;
  box-sizing: border-box;
}
.pl-field-check { flex-direction: row; align-items: center; justify-content: space-between; }
.pl-field-check input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; }
.pl-color-row { display: flex; align-items: center; gap: 8px; }
.pl-color-row input[type="color"] { width: 48px; height: 28px; border: 1px solid var(--gs-border-4); border-radius: 4px; cursor: pointer; padding: 1px; }
.pl-color-hex { font-size: 11px; font-family: monospace; color: var(--gs-text-3); }
.pl-form-err { min-height: 18px; font-size: 10px; color: #e53935; padding: 0 14px; }
.pl-form-footer { padding: 8px 14px 12px; display: flex; gap: 8px; border-top: 1px solid var(--gs-border-2); }

/* draw toolbar — floats above the map */
.pl-draw-toolbar {
  position: absolute;
  top: 10px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1100;
  background: rgba(30,30,30,0.88);
  color: var(--gs-on-accent);
  border-radius: 6px;
  padding: 7px 14px;
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
  pointer-events: all;
  white-space: nowrap;
  box-shadow: 0 3px 12px rgba(0,0,0,0.4);
}
.pl-draw-type {
  background: rgba(255,255,255,0.12);
  border: 1px solid rgba(255,255,255,0.25);
  color: var(--gs-on-accent);
  border-radius: 4px;
  padding: 4px 10px;
  cursor: pointer;
  font-size: 11px;
  transition: background .15s;
}
.pl-draw-type:hover { background: rgba(255,255,255,0.22); }
.pl-draw-type.pl-dt-active { background: var(--gs-accent); border-color: var(--gs-accent); }
.pl-draw-sep { width: 1px; height: 20px; background: rgba(255,255,255,0.2); }
.pl-draw-hint { color: rgba(255,255,255,0.82); font-size: 11px; }
.pl-draw-cancel {
  background: none;
  border: 1px solid rgba(255,255,255,0.3);
  color: var(--gs-on-accent);
  border-radius: 4px;
  padding: 4px 10px;
  cursor: pointer;
  font-size: 11px;
}
.pl-draw-cancel:hover { background: rgba(255,0,0,0.25); }


#gs-toast-host {
  position: fixed;
  bottom: 16px;
  right: 16px;
  z-index: 3000;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 320px;
}
.gs-toast {
  background: var(--gs-surface);
  border-left: 3px solid var(--gs-accent);
  border-radius: 4px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.2);
  padding: 10px 14px;
  font-size: 11px;
  color: var(--gs-text-2);
  opacity: 1;
  transition: opacity 0.4s;
}
.gs-toast-out { opacity: 0; }

.gs-marker { background: transparent; border: none; }
.gs-marker-dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid #FFFFFF;
  box-shadow: 0 0 2px rgba(0,0,0,0.5);
}
.gs-marker-arrow {
  width: 0;
  height: 0;
  border-left: 8px solid transparent;
  border-right: 8px solid transparent;
  border-bottom: 16px solid #FF0000;
  filter: drop-shadow(0 0 1px rgba(0,0,0,0.6));
}
.gs-marker-emoji { font-size: 21px; line-height: 1; text-align: center; }

/* Object backup/import dialog */
.bk-exp-options { display: flex; flex-direction: column; gap: 8px; padding: 4px 0 8px; width: 100%; }
.bk-exp-option {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 14px; cursor: pointer;
  font-size: 12px; font-weight: 500; color: var(--gs-text);
  background: var(--gs-surface-3); border: 1.5px solid var(--gs-border-3);
  border-radius: 7px; width: 100%; box-sizing: border-box;
  transition: border-color 0.14s, background 0.14s;
}
.bk-exp-option:hover:not(.bk-exp-disabled) { border-color: var(--gs-accent); background: var(--gs-accent-bg); }
.bk-exp-option input[type="radio"] {
  width: 17px; height: 17px; flex-shrink: 0;
  accent-color: var(--gs-accent); cursor: pointer;
}
.bk-exp-option-text { display: flex; flex-direction: column; gap: 2px; }
.bk-exp-hint { font-size: 10px; color: var(--gs-text-3); font-style: normal; font-weight: 400; }
.bk-exp-disabled { opacity: 0.48; cursor: default; pointer-events: none; }
.bk-summary { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.bk-sum-item { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 500; padding: 4px 10px; border-radius: 20px; }
.bk-new   { background: var(--gs-ok-bg); color: var(--gs-ok-fg); }
.bk-exists{ background: var(--gs-warn-bg); color: var(--gs-warn-fg); }
.bk-skip  { background: var(--gs-danger-bg); color: var(--gs-danger-fg); }
.bk-badge { display: inline-block; font-size: 10px; font-weight: 600; padding: 2px 7px; border-radius: 10px; letter-spacing: 0.02em; text-transform: uppercase; }
.bk-badge-new    { background: var(--gs-ok-bg); color: var(--gs-ok-fg); }
.bk-badge-exists { background: var(--gs-warn-bg); color: var(--gs-warn-fg); }
.bk-badge-skip   { background: var(--gs-danger-bg); color: var(--gs-danger-fg); }

/* Light punch on the already-saturated categorized traffic tiles so green/red read clearly */
.gs-traffic-tiles { filter: saturate(1.4) contrast(1.15); }

/* Tachograph detail: activity data-source badge (live tacho vs GPS estimate) */
.tc-src { display:inline-block; padding:2px 9px; border-radius:11px; font-size:11px; font-weight:600; }
.tc-src-tacho { background: var(--gs-ok-bg); color: var(--gs-ok-fg); }
.tc-src-gps   { background: var(--gs-warn-bg); color: var(--gs-warn-fg); }

/* Generic table tools (GsTableTools): click-to-sort headers + per-table search box on every .cp-table */
.cp-table th.gs-sortable { cursor: pointer; user-select: none; }
.cp-table th.gs-sortable:hover { filter: brightness(1.18); }
.gs-sort-arrow { color: #00A9DF; font-size: 10px; }
.gs-tbl-tools { margin: 0 0 8px; }
.gs-tbl-search { width: 260px; max-width: 100%; padding: 6px 11px; border-radius: 6px; border: 1px solid var(--gs-border-4); background: var(--gs-surface); color: var(--gs-text); font-size: 12px; }
.gs-tbl-search:focus { outline: none; border-color: #00A9DF; box-shadow: 0 0 0 2px #00A9DF33; }

/* ---- Tacho-download decoded-view modal (tachodl.js) ---- */
/* The new-card naming control (input + "Name it" button, 2026-08-28) must never be clipped by the
   generic single-line-cell rules above (.cp-table td / .cp-table td > *, line ~1111/1125) -- those
   assume one plain value per cell and ellipsis anything too long, which silently hid this button
   past the column's right edge instead of showing it. This is the one cell that holds an editable
   input and an action button together; wrapping the button to its own line if the column is ever
   too narrow is far better than an unreachable control. Both selectors carry 3 classes' worth of
   specificity, so they win over the 2-class base rules regardless of where they sit in this file. */
.cp-table td.td-card-claim-cell { white-space: normal; overflow: visible; text-overflow: clip; }
.cp-table td .td-card-claim {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  overflow: visible; white-space: normal; max-width: none;
}
.td-view { margin-right: 6px; }
.td-modal-overlay {
  position: fixed; inset: 0; z-index: 4000;
  background: rgba(0,0,0,0.45);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 40px 16px; overflow-y: auto;
}
.td-modal-box {
  background: var(--gs-surface); color: var(--gs-text);
  border: 1px solid var(--gs-border-2); border-radius: 10px;
  box-shadow: 0 12px 40px rgba(0,0,0,0.35);
  display: flex; flex-direction: column;
  width: 780px; max-width: 95vw; height: 78vh; max-height: 90vh;
  /* user-resizable: drag the bottom-right corner */
  resize: both; overflow: hidden; min-width: 360px; min-height: 260px;
}
.td-modal-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px; border-bottom: 1px solid var(--gs-border-2); flex: 0 0 auto;
}
.td-modal-title { font-weight: 600; font-size: 13px; word-break: break-all; }
.td-modal-close {
  background: none; border: none; font-size: 23px; line-height: 1;
  color: var(--gs-text-3); cursor: pointer; padding: 0 4px;
}
.td-modal-close:hover { color: var(--gs-text); }
.td-modal-body { padding: 16px; overflow: auto; flex: 1 1 auto; }

/* Driver + Totals info cards, shown side by side (stack on narrow) */
.td-info-row { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 14px; }
.td-info-card {
  flex: 1 1 260px; min-width: 240px;
  background: var(--gs-surface-2); border: 1px solid var(--gs-border-2); border-radius: 8px;
  padding: 10px 14px;
}
.td-info-cap {
  font-size: 10px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--gs-text-4); font-weight: 600; margin-bottom: 6px;
}
.td-info { width: 100%; border-collapse: collapse; font-size: 12px; }
.td-info th {
  text-align: left; font-weight: 500; color: var(--gs-text-3);
  padding: 3px 14px 3px 0; white-space: nowrap; vertical-align: top; width: 1%;
}
.td-info td { padding: 3px 0; color: var(--gs-text); }
.td-info-totals td { text-align: right; font-variant-numeric: tabular-nums; }
.td-info code, .cp-table code { background: var(--gs-surface-4); padding: 1px 5px; border-radius: 4px; font-size: 11px; }

.td-daytable td, .td-daytable th { font-variant-numeric: tabular-nums; }
.td-dec { color: var(--gs-text-4); font-size: 10px; }
/* Working-time columns (Drive+Work+Avail total and its decimal) — subtly highlighted */
.td-wtcol { background: var(--gs-accent-bg); }
.cp-table td.td-wtcol { color: var(--gs-text); font-weight: 500; }
.td-total-row td {
  font-weight: 600; color: var(--gs-text);
  border-top: 2px solid var(--gs-border-3); background: var(--gs-surface-2);
}
.td-total-row td:first-child { text-transform: uppercase; letter-spacing: .03em; font-size: 11px; }
.td-blocks { display: flex; flex-wrap: wrap; gap: 6px; }
.td-block { background: var(--gs-surface-4); border: 1px solid var(--gs-border-2); border-radius: 12px; padding: 2px 10px; font-size: 11px; color: var(--gs-text-2); }

/* Schedule card */
.td-sched { background: var(--gs-surface-2); border: 1px solid var(--gs-border-2); border-radius: 8px; padding: 12px 14px; margin-bottom: 4px; }
.td-sched-row { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 8px; }
.td-sched-field { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--gs-text-2); white-space: nowrap; }
.td-sched-field input { width: 62px; padding: 5px 8px; border: 1px solid var(--gs-border-3); border-radius: 6px; background: var(--gs-surface); color: var(--gs-text); font-size: 12px; text-align: right; }
.td-sched-hint { font-size: 10px; color: var(--gs-text-4); margin-top: 8px; }
.td-sched-msg { font-size: 11px; color: var(--gs-accent); }
.td-sched-msg.td-err { color: var(--gs-danger-fg, #c0392b); }
.td-sched-divider { border-top: 1px solid var(--gs-border-2); margin: 14px 0 12px; }
.td-agent-row { align-items: center; }
.td-agent-hint { margin-top: 0; flex: 1 1 320px; }

/* Download-status table — columns fully visible (no ellipsis); long vehicle names wrap */
.td-status td, .td-status th { white-space: nowrap; overflow: visible; text-overflow: clip; vertical-align: middle; }
.td-status .td-vehcell { white-space: normal; min-width: 150px; font-weight: 500; color: var(--gs-text); }
.td-status .td-actioncell { white-space: nowrap; }
.td-status .td-force { padding: 3px 10px; font-size: 11px; margin-right: 4px; }
.td-queued { display: inline-block; font-size: 10px; color: var(--gs-accent); background: var(--gs-accent-bg); border: 1px solid var(--gs-accent); border-radius: 10px; padding: 2px 9px; margin-right: 4px; }
.td-muted { color: var(--gs-text-4); }
.td-due { color: #c0392b; font-weight: 600; }

/* Status header with the Select-vehicles button */
/* Keep the "Select vehicles" button NEXT TO its title. It used to be justify-content:space-between,
   which on a full-width page threw the button ~1400px to the right edge — far away from the very
   text that tells you to click it, so it read as missing. */
.td-status-head { display: flex; align-items: center; justify-content: flex-start; gap: 12px; margin-top: 16px; margin-bottom: 6px; flex-wrap: wrap; }
.td-manage { padding: 5px 12px; font-size: 11px; white-space: nowrap; }

/* "Downloaded files" explorer: a wide, flat, Explorer-style window for moving files to a PC. */
.tx-box { width: min(1100px, 96vw); max-width: none; }
.tx-tools { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 10px 14px; border-bottom: 1px solid var(--gs-border-2); }
.tx-all { font-size: 12px; color: var(--gs-text-2); display: flex; align-items: center; gap: 5px; }
.tx-count { font-size: 12px; color: var(--gs-text-3); }
.tx-msg:empty { display: none; }
.tx-msg { padding: 7px 14px; font-size: 12px; color: var(--gs-text-2); background: var(--gs-surface-3); border-bottom: 1px solid var(--gs-border-2); }
.tx-body { max-height: 62vh; overflow: auto; }
.tx-table td, .tx-table th { font-size: 12.5px; white-space: nowrap; }
.tx-table td:nth-child(2) { font-family: ui-monospace, Consolas, monospace; font-size: 12px; }

/* Bulk-select bar: checkbox column + queue-many-at-once actions */
.td-selcell { width: 30px; text-align: center; }
.td-bulk-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.td-bulk-count { font-size: 11px; color: var(--gs-text-3); min-width: 90px; }
.td-bulk-btn { padding: 4px 12px; font-size: 11px; }
.td-bulk-btn:disabled { opacity: 0.5; }

/* Object picker (single-column row list + search + select-all) */
.td-pick-box { max-width: 520px; height: 72vh; }
.td-pick-tools { display: flex; align-items: center; gap: 12px; padding: 10px 16px; border-bottom: 1px solid var(--gs-border-2); flex: 0 0 auto; }
.td-pick-search { flex: 1; min-width: 0; padding: 7px 10px; border: 1px solid var(--gs-border-3); border-radius: 6px; background: var(--gs-surface); color: var(--gs-text); font-size: 12px; }
.td-pick-all { font-size: 12px; color: var(--gs-text-2); white-space: nowrap; display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.td-pick-list { padding: 0 !important; }
.td-pick-row { display: flex; align-items: center; gap: 10px; padding: 9px 16px; border-bottom: 1px solid var(--gs-border); cursor: pointer; font-size: 12px; color: var(--gs-text); }
.td-pick-row:hover { background: var(--gs-accent-bg); }
.td-pick-row input { flex: 0 0 auto; }
.td-pick-foot { display: flex; align-items: center; gap: 8px; padding: 10px 16px; border-top: 1px solid var(--gs-border-2); flex: 0 0 auto; }
.td-pick-count { font-size: 11px; color: var(--gs-text-3); }

/* Received-files list grouped per driver/vehicle (thousands of files collapse to one row per
   person/truck) + a search box to filter by name/plate/date. */
.td-group-search { width: 100%; max-width: 420px; padding: 7px 10px; border: 1px solid var(--gs-border-3); border-radius: 6px; background: var(--gs-surface); color: var(--gs-text); font-size: 12px; }
.td-group-list { border: 1px solid var(--gs-border-2); border-radius: 6px; overflow: hidden; }
.td-group + .td-group { border-top: 1px solid var(--gs-border); }
.td-group-head { display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 12px; background: none; border: none; text-align: left; cursor: pointer; font: inherit; color: var(--gs-text); }
.td-group-head:hover { background: var(--gs-accent-bg); }
.td-group-caret { color: var(--gs-text-4); font-size: 10px; transition: transform 0.15s; flex: 0 0 auto; }
.td-group-open .td-group-caret { transform: rotate(90deg); }
.td-group-label { font-weight: 500; }
.td-group-meta { color: var(--gs-text-3); font-size: 11px; margin-left: auto; white-space: nowrap; }
.td-group-body { border-top: 1px solid var(--gs-border-2); }
.td-group-body .cp-table-wrap { border: none; border-radius: 0; }
.td-group-status { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.td-status-active { background: #4caf50; }
.td-status-dormant { background: var(--gs-border-3); }
.td-group-sort { padding: 7px 10px; border: 1px solid var(--gs-border-3); border-radius: 6px; background: var(--gs-surface); color: var(--gs-text); font-size: 12px; }

/* Per-year sub-groups inside a large driver/vehicle's file list */
.td-year-group + .td-year-group { border-top: 1px solid var(--gs-border); }
.td-year-head { display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 12px; background: var(--gs-surface-4); border: none; text-align: left; cursor: pointer; font: inherit; font-weight: 500; color: var(--gs-text-2); font-size: 11px; }
.td-year-head:hover { background: var(--gs-accent-bg); }
.td-year-open > .td-year-head .td-group-caret { transform: rotate(90deg); }
.td-year-count { color: var(--gs-text-4); font-weight: 400; }
.td-year-body .cp-table-wrap { border: none; border-radius: 0; }

/* ---- Local downloads (operator console) ---------------------------------- */
/* Deliberately tile-first and search-first: the operator asked for "not endless scrolling tables",
   so the only lists that ever appear are (a) 15 card tiles and (b) one card's objects, grouped by
   account and collapsed until asked for. */
.ld-modal { width: min(1080px, 96vw); height: min(86vh, 780px); }
.ld-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 10px; }
.ld-tile { display: flex; flex-direction: column; gap: 5px; text-align: left; cursor: pointer;
  padding: 11px 13px; border-radius: 8px; border: 1px solid var(--ed-border, #2b3b52);
  background: var(--ed-field, #16202e); color: inherit; font: inherit; }
.ld-tile:hover { border-color: var(--accent, #3d9be9); }
.ld-tile-top { display: flex; align-items: center; gap: 7px; }
.ld-tile-name { font-weight: 600; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ld-tile-sub, .ld-tile-nums { font-size: 12px; opacity: .75; }
.ld-tile-nums b { opacity: 1; font-size: 13px; }
.ld-tile-accs { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px; }
.ld-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.ld-dot.ld-on { background: #35c46a; } .ld-dot.ld-off { background: #8a94a2; }
/* Card health dot (last successful CARD_OPEN), separate from the old ld-on/ld-off pair above --
   that pair is untouched and still used by the unplaced/staging tile, which has no health data yet. */
.ld-dot.ld-health-on { background: #35c46a; }
.ld-dot.ld-health-warn { background: #e0a030; }
.ld-dot.ld-health-off { background: #e0453f; }

.ld-cardhead { display: flex; align-items: center; gap: 9px; margin-bottom: 12px; flex-wrap: wrap; }
.ld-name { max-width: 240px; }
.ld-idhint { font-size: 12px; opacity: .6; }

.ld-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
@media (max-width: 780px) { .ld-cols { grid-template-columns: 1fr; } }
.ld-col { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.ld-colhead { display: flex; align-items: center; justify-content: space-between; }
/* Bounded height is the point: a card with 63 objects must not push the page into a long scroll. */
.ld-scroll { max-height: 340px; overflow-y: auto; border: 1px solid var(--ed-border, #2b3b52);
  border-radius: 6px; padding: 4px; }
.ld-row { display: flex; align-items: center; gap: 8px; padding: 5px 7px; border-radius: 5px;
  cursor: pointer; width: 100%; background: none; border: 0; color: inherit; font: inherit; text-align: left; }
.ld-row:hover { background: rgba(255,255,255,.05); }
.ld-row-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
.ld-row-sub { font-size: 11px; opacity: .6; white-space: nowrap; }
.ld-group-head { display: flex; align-items: center; gap: 7px; width: 100%; padding: 6px 7px;
  background: none; border: 0; color: inherit; font: inherit; cursor: pointer; border-radius: 5px; }
.ld-group-head:hover { background: rgba(255,255,255,.05); }
.ld-group-name { flex: 1; text-align: left; font-weight: 600; font-size: 13px; }
.ld-caret { width: 12px; opacity: .7; }
.ld-group-body { padding-left: 14px; }
.ld-sec { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; opacity: .6;
  padding: 7px 7px 3px; display: flex; align-items: center; justify-content: space-between; }
.ld-mini { background: none; border: 0; color: var(--accent, #3d9be9); cursor: pointer; font-size: 11px; }
.ld-hint { padding: 12px 8px; font-size: 12px; opacity: .6; }
.ld-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ld-selcount { font-size: 12px; opacity: .75; margin-right: auto; }
.ld-move { max-width: 170px; }
.ld-tray { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding-top: 4px; }
.ld-tray-cap { font-size: 12px; opacity: .75; }
.ld-tray-note { font-size: 12px; opacity: .8; margin-left: 2px; }
.ld-dest-partial { font-size: 12px; color: #f0a848; margin-left: 6px; white-space: nowrap; }
.ld-dest-fix { margin-left: 6px; font-size: 12px; padding: 3px 9px; }
.ld-tray-note b { font-weight: 600; }
.ld-chip { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; padding: 2px 6px;
  border-radius: 4px; background: rgba(61,155,233,.16); }
.ld-chip-x { background: none; border: 0; color: inherit; cursor: pointer; opacity: .7; font-size: 14px; line-height: 1; }
.ld-note { padding: 8px 11px; border-radius: 6px; margin-bottom: 10px; font-size: 13px;
  background: rgba(53,196,106,.15); border: 1px solid rgba(53,196,106,.4); }
.ld-note-bad { background: rgba(233,76,61,.15); border-color: rgba(233,76,61,.45); }

/* Tacho "Received files": two folders instead of an endless list of expandable file groups.
   The browsing itself happens in the Downloaded files window, which has search, multi-select and
   copy-to-folder -- so the page only needs to name the folders and their counts. */
.td-filemaps { display: flex; gap: 10px; flex-wrap: wrap; margin: 6px 0 4px; }
.td-fmap { display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  padding: 9px 14px; border-radius: 8px; border: 1px solid var(--ed-border, #2b3b52);
  background: var(--ed-field, #16202e); color: inherit; font: inherit; }
.td-fmap:hover { border-color: var(--accent, #3d9be9); }
.td-fmap-on { border-color: var(--accent, #3d9be9); background: rgba(61,155,233,.14); }
.td-fmap-ico { font-size: 16px; line-height: 1; }
.td-fmap-name { font-weight: 600; font-size: 13px; }
.tx-maps { margin: 0 0 8px; padding: 0 2px; }

/* Download status: a folding section with its own bounded scroll, so the page stays one screen
   however many vehicles an account runs. The heading itself is the fold control. */
.td-fold { display: inline-flex; align-items: center; gap: 6px; background: none; border: 0;
  color: inherit; font: inherit; cursor: pointer; padding: 0; }
.td-fold:hover .ed-section-title { color: var(--accent, #3d9be9); }
.td-status-search { max-width: 300px; margin: 8px 0 6px; display: block; }
.td-status-wrap { max-height: 420px; overflow-y: auto; }
/* The header has to stay put while the body scrolls under it, or a long list loses its column names. */
.td-status-wrap thead th { position: sticky; top: 0; z-index: 2; background: var(--panel, #16202e); }

/* "Add a card" tile + its explainer. A card is a physical chip that registers itself when read, so
   this teaches the operator how one appears rather than pretending the UI can create one. */
.ld-tile-add { align-items: center; justify-content: center; border-style: dashed; text-align: center; }
.ld-tile-plus { font-size: 24px; line-height: 1; opacity: .7; }
.ld-help { position: absolute; inset: 0; z-index: 5; display: flex; align-items: center;
  justify-content: center; background: rgba(0,0,0,.55); border-radius: 10px; }
.ld-help-in { max-width: 460px; padding: 20px 22px; border-radius: 10px;
  background: var(--panel, #16202e); border: 1px solid var(--ed-border, #2b3b52); }
.ld-help-title { font-weight: 600; font-size: 15px; margin-bottom: 10px; }
.ld-help-steps { margin: 0 0 12px; padding-left: 20px; font-size: 13px; line-height: 1.65; }
.ld-help-note { font-size: 12px; opacity: .7; margin-bottom: 14px; }
.ld-modal .ed-body { position: relative; }

/* Cards ALMAG holds vs cards a customer holds. The page is about the operator's OWN cards; the
   customer-held ones sit behind one line so a mis-marked card is still correctable. */
.ld-foot { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--ed-border, #2b3b52); }
.ld-linkbtn { background: none; border: 0; color: var(--accent, #3d9be9); cursor: pointer;
  font: inherit; font-size: 12px; padding: 0; }
.ld-linkbtn:hover { text-decoration: underline; }
.ld-where { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; opacity: .85; cursor: pointer; }

/* Reader claiming inside the Add-a-card panel: the step that used to require Fleet Control. */
.ld-rd { max-height: 220px; overflow-y: auto; border: 1px solid var(--ed-border, #2b3b52);
  border-radius: 6px; padding: 4px; margin: 4px 0 12px; }
.ld-rd-row { gap: 6px; }
.ld-rd-acct { max-width: 150px; font-size: 12px; padding: 3px 6px; }
.ld-help-in { max-width: 560px; max-height: 82vh; overflow-y: auto; }

/* Freshly-read cards that have not been placed with a customer yet. Given prominence because an
   unplaced card is the one thing on this screen that needs the operator to act. */
.ld-fresh { margin-bottom: 16px; padding: 12px; border-radius: 8px;
  border: 1px solid rgba(61,155,233,.45); background: rgba(61,155,233,.08); }
.ld-fresh-cap { font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
  margin-bottom: 8px; display: flex; align-items: center; gap: 8px; }
.ld-tile-new { border-color: var(--accent, #3d9be9); }
.ld-tile-new .ld-tile-sub { font-family: ui-monospace, Consolas, monospace; font-size: 11px; }

/* Which account receives the downloads — shown only when the staged objects are co-owned. */
.ld-acct-pick { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; opacity: .9; }
.ld-acct-pick select { max-width: 150px; font-size: 12px; padding: 3px 6px; }

/* Disabled actions must LOOK disabled — the whole point is that they read as inert until a row is
   ticked, so the row cannot be mistaken for an unsaved-changes bar. */
.ld-actions .ed-btn[disabled], .ld-actions select[disabled] { opacity: .45; cursor: not-allowed; }

/* Handing a card back to its customer: an ordinary operation, so it gets a plain explanation rather
   than a warning. Nothing has to be undone — the chip carries the identity. */
.ld-handback { margin: -4px 0 12px; padding: 9px 12px; border-radius: 6px; font-size: 12px;
  line-height: 1.55; background: rgba(255,255,255,.05); border: 1px solid var(--ed-border, #2b3b52); }

/* Mirror grants: which accounts may READ a card's downloads, independent of who owns the vehicles. */
.ld-mirror { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: -4px 0 14px;
  padding: 9px 12px; border-radius: 6px; background: rgba(255,255,255,.04);
  border: 1px solid var(--ed-border, #2b3b52); }
.ld-mirror-cap { font-size: 12px; opacity: .8; }
.ld-mirror-list { display: inline-flex; gap: 5px; flex-wrap: wrap; }
.ld-mirror-in { max-width: 150px; font-size: 12px; padding: 3px 6px; }
.ld-chip-on { background: rgba(53,196,106,.18); }

/* "Selected for download" activity row -- same three tones as the card health dot, so a color once
   learned there reads the same way here. Each chip is now the 90-day vehicle-unit deadline (green/
   amber/red), plus a fourth neutral tone for "never downloaded" -- kept distinct from red on purpose,
   since 91 days overdue and no download ever recorded are not the same fault. Chips are buttons: click
   one to expand the vehicles behind that count, same fold pattern as "Devices on this card" above it. */
.ld-activity-window { opacity: .65; font-weight: normal; }
.ld-activity-summary { font-size: 12px; }
.ld-muted { font-size: 12px; opacity: .6; font-style: italic; }
.ld-activity-chips { display: flex; gap: 6px; flex-wrap: wrap; margin: 6px 0 2px; flex-basis: 100%; }
.ld-activity-body { flex-basis: 100%; }
.ld-chip-ok, .ld-chip-warn, .ld-chip-bad, .ld-chip-never { display: inline-block; padding: 2px 7px; border-radius: 10px;
  font-size: 11px; margin-right: 4px; }
.ld-chip-ok { background: rgba(53,196,106,.18); color: #4fd17a; }
.ld-chip-warn { background: rgba(224,160,48,.18); color: #e0a030; }
.ld-chip-bad { background: rgba(224,69,63,.18); color: #ff6b6b; }
.ld-chip-never { background: rgba(255,255,255,.08); color: rgba(255,255,255,.55); }
.ld-chip-toggle { display: inline-flex; align-items: center; gap: 4px; border: 0; cursor: pointer;
  font: inherit; margin-right: 0; }
.ld-chip-toggle .ld-caret { width: auto; opacity: .8; }
.ld-chip-toggle[disabled] { opacity: .5; cursor: not-allowed; }
.ld-row-activity { cursor: default; flex-wrap: wrap; }
.ld-row-activity:hover { background: none; }

/* Second filter row in the Downloaded files window: which company card produced each file. */
.tx-cards { margin: 0 0 8px; padding: 0 2px; align-items: center; }
.tx-cards-cap { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; opacity: .6; margin-right: 2px; }

/* The vehicle window now carries the full status table, so it needs the room a picker did not. */
.td-pick-box { width: min(1180px, 96vw); }
.td-pick-list { max-height: 60vh; overflow: auto; }
.td-pick-table th { position: sticky; top: 0; z-index: 2; background: var(--panel, #16202e); }
.td-pick-table td { white-space: nowrap; }
.td-pick-table .td-vehcell { white-space: normal; min-width: 180px; }

/* An owner is not a mirror grant: it reads the files by right and cannot be revoked from this screen,
   so it gets no remove button. Showing one would have promised something the × does not do. */
.ld-chip-own { background: rgba(255,255,255,.07); }
.ld-own-tag { font-size: 10px; text-transform: uppercase; letter-spacing: .04em; opacity: .6; }

/* Five-yearly card renewal. Offered only when an unplaced card is sitting there to move onto. */
.ld-renew { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: -4px 0 12px;
  padding: 9px 12px; border-radius: 6px; background: rgba(240,180,60,.10);
  border: 1px solid rgba(240,180,60,.35); font-size: 12px; }
.ld-renew-cap { font-weight: 600; }
.ld-renew-txt { opacity: .8; }
.ld-renew-sel { max-width: 190px; font-size: 12px; padding: 3px 6px; }

/* Inline "saved" for controls that write on change. These screens have no Save button by design —
   the confirmation has to appear where the operator is already looking. */
.ld-saved { font-size: 11px; color: #35c46a; font-weight: 600; }

/* Row toggles in the Control panel write on click — there is no Save button — so the row itself has to
   confirm. Green fade on success, red on failure. Without this a silent success and a change that never
   happened looked exactly the same. */
@keyframes cpRowSaved  { from { background: rgba(53,196,106,.35); } to { background: transparent; } }
@keyframes cpRowFailed { from { background: rgba(233,76,61,.35); }  to { background: transparent; } }
.cp-row-saved  > td { animation: cpRowSaved 1.6s ease-out; }
.cp-row-failed > td { animation: cpRowFailed 1.6s ease-out; }
@media (prefers-reduced-motion: reduce) {
  .cp-row-saved > td, .cp-row-failed > td { animation: none; }
  .cp-row-saved  > td { background: rgba(53,196,106,.18); }
  .cp-row-failed > td { background: rgba(233,76,61,.18); }
}

/* Login log: a deactivated attempt is not a failed one. The password was right and the account was
   turned away, so it gets its own amber badge rather than the red "wrong password" colour. */
.lm-ev-blocked { background: rgba(240,180,60,.20); color: #f0b43c; border: 1px solid rgba(240,180,60,.45); }

/* The kind folders sit UNDER the card row and belong to the selected card, so they are indented and
   quieter — the card is the primary choice, the folder is what you do inside it. */
.tx-maps-sub { margin-left: 14px; padding-left: 10px; border-left: 2px solid var(--ed-border, #2b3b52); }

/* Destination row: where a card's files are written. Sits above the read-access row because it is the
   stronger statement — one decides where files land, the other only who may look at them. */
.ld-dest { background: rgba(61,155,233,.07); border-color: rgba(61,155,233,.3); }

/* Account type-ahead. The operator thinks "mk", the API needs "mklog" — the suggestion bridges the two
   so a reasonable guess never dead-ends in "no account named mk". */
.ld-suggest-wrap { position: relative; display: inline-block; }
.ld-suggest { position: absolute; top: 100%; left: 0; z-index: 40; min-width: 210px; max-height: 220px;
  overflow-y: auto; margin-top: 3px; border-radius: 6px; padding: 3px;
  background: var(--panel, #16202e); border: 1px solid var(--ed-border, #2b3b52);
  box-shadow: 0 8px 22px rgba(0,0,0,.45); }
.ld-suggest-row { display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
  width: 100%; padding: 5px 8px; border: 0; border-radius: 4px; background: none; color: inherit;
  font: inherit; text-align: left; cursor: pointer; }
.ld-suggest-row:hover { background: rgba(255,255,255,.07); }

/* Grouped file explorer: one foldable block per driver card / per vehicle. The header is a table row
   so it lines up with the columns; it is clickable anywhere except its checkbox, which selects the
   whole folder including files folded out of sight. */
.tx-table tr.tx-grp { cursor: pointer; background: rgba(255,255,255,.045); }
.tx-table tr.tx-grp:hover { background: rgba(255,255,255,.075); }
.tx-table tr.tx-grp td { padding-top: 7px; padding-bottom: 7px; }
.tx-grp-arrow { display: inline-block; width: 12px; opacity: .7; }
.tx-table tr.tx-grp b { font-weight: 600; letter-spacing: .2px; }

/* A panel value that is older than the trust window still shows, but carries its age so it is never
   read as current -- a parked vehicle's last stored voltage was taken while the alternator charged. */
.dp-stale { opacity: .65; font-size: 12px; font-weight: 400; }

/* ─────────────────────────────────────────────────────────────────────────────
   PHONE: the device panel.  Added 2026-08-21.

   Scope is deliberately the CLIENT path only — map, device list, device panel.
   The tacho, drivers and reports screens are office tools and are left alone.

   The desktop three-column layout (Data | Daily statistics | Tachograph) is NOT
   restructured: everything here lives inside a max-width query, so it applies to
   phones only and the signed-off desktop arrangement is untouched.

   What was wrong on a phone:
     * 45vh of a short screen left the panel a few lines tall
     * 11px key/value text is below comfortable reading size held at arm's length
     * three 160px columns wrap into a ragged two-then-one on a 375px screen
     * 28px arrows and 34px tabs are under the ~44px a fingertip reliably hits
   ───────────────────────────────────────────────────────────────────────────── */
@media (max-width: 640px) {
  /* Give the data real estate. dvh so the address bar collapsing doesn't clip it. */
  #device-panel { max-height: 62vh; max-height: 62dvh; }

  /* One column reading top-to-bottom, rather than columns fighting for width. */
  .dp-body { flex-direction: column; flex-wrap: nowrap; gap: 10px; padding: 10px 12px; }
  .dp-col, .dp-col-wide { min-width: 0; width: 100%; flex: none; }

  /* Readable at arm's length, and rows tall enough to scan. */
  .dp-kv { font-size: 13px; padding: 7px 0; gap: 12px; }
  .dp-section-title { font-size: 12px; margin: 12px 0 6px; }

  /* Touch targets: 44px is the size a fingertip hits reliably. */
  .dp-header { height: 44px; padding: 0 10px; }
  .dp-tab { padding: 0 14px; font-size: 13px; min-height: 44px; }
  .dp-close { font-size: 22px; padding: 6px; }
  .dp-period-arrow { width: 40px; height: 40px; font-size: 19px; }
  .dp-period-label { font-size: 13px; padding: 8px 12px; }
  .dp-period-dots .dp-dot { width: 9px; height: 9px; }
}

/* ── Elements that were rendering with browser defaults ──────────────────────────────────────
   Found 2026-08-22 by diffing every class used in js/*.js against every class defined here: 19
   visible elements carried only a JS hook class and no styling rule at all, so they fell back to
   the browser's defaults — plain blue underlined links on a dark panel, icon buttons that didn't
   match the identical-looking ones beside them, labels whose checkbox sat detached from its text.
   Each rule below matches an EXISTING sibling rather than introducing a new look. */

/* Control-panel action icons: these sit in the same cell as .cp-edit/.cp-delete and must be
   indistinguishable from them. */
.cp-message, .cp-save-template {
  cursor: pointer; color: var(--gs-text-3);
  width: 28px; height: 24px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 3px;
}
.cp-message:hover, .cp-save-template:hover { background: var(--gs-accent-bg); color: var(--gs-accent); }

/* An owner name inside the Objects table. As a bare <a href="#"> it rendered as the browser's
   default underlined blue, which on this dark theme reads as broken. */
.obj-owner-link { color: var(--gs-accent); text-decoration: none; }
.obj-owner-link:hover { text-decoration: underline; }

/* "Hidden from sub-users" marker — a badge, not body text. */
.obj-hidden-tag {
  font-size: 10px; padding: 1px 5px; border-radius: 3px;
  background: var(--gs-surface-4); color: var(--gs-text-3); white-space: nowrap;
}

/* Section heading inside the Places/zone form, matching .ed-section-title's weight and colour. */
.pl-form-title { display: block; font-size: 12px; font-weight: 600; color: var(--gs-accent); margin-bottom: 6px; }

/* Checkbox rows that were missing the flex layout every other checkbox row in the app has,
   so the box floated away from its label. Same declarations as .ed-field-inline label. */
.hi-checkbox-label, .ed-check {
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; color: var(--gs-text-2); cursor: pointer;
}
.ed-check-row { display: flex; align-items: center; gap: 10px; margin: 8px 0; }

/* Secondary/dimmed text — counts and hints that should recede, not compete. */
.cell-dim, .td-card-msg { font-size: 11px; color: var(--gs-text-4); }

/* Identifier columns: IMEIs and card IDs line up only in a monospaced face. */
.ob-imei, .tx-cardid { font-family: ui-monospace, Consolas, 'Courier New', monospace; font-size: 11px; }

/* Sort direction arrow in sortable table headers. */
.st-sort-arrow { margin-left: 4px; font-size: 9px; color: var(--gs-text-4); }

/* Plain block containers that only ever needed to exist as layout boxes. */
.drv-tabpanel, .rp-inline-results { display: block; }
.ld-group { margin-bottom: 10px; }

/* Small inline button in the Edit-user object table. */
.eu-edit-obj-btn {
  cursor: pointer; background: none; border: 0; padding: 0 4px;
  color: var(--gs-text-3); font-size: 12px;
}
.eu-edit-obj-btn:hover { color: var(--gs-accent); }

/* ---- Visible column (visibility.js cellHtml) + paste-capable date field (gsdate.js) ---- */
.gs-vis { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.gs-vis input[type=checkbox] { margin: 0; cursor: pointer; }
.gs-vis input[type=checkbox]:disabled { opacity: 0.35; cursor: not-allowed; }
.gs-vis-text { font-size: 12px; }
.gs-vis-ok { color: var(--gs-text-2); }
.gs-vis-hidden { color: var(--gs-warn-fg); }
.gs-vis-expired { color: var(--gs-danger-fg); font-weight: 600; }
.gs-vis-date { cursor: pointer; text-decoration: underline dotted; text-underline-offset: 2px; }
.gs-vis-date:hover { opacity: 0.75; }
.gs-datefield { display: inline-flex; align-items: center; gap: 6px; width: 100%; position: relative; }
.gs-datefield-text { flex: 1; min-width: 120px; }
.gs-datefield-pick { flex: 0 0 auto; padding: 6px 8px; cursor: pointer; background: var(--gs-surface); border: 1px solid var(--gs-border-3); border-radius: 6px; color: var(--gs-text); font: inherit; line-height: 1; }
.gs-datefield-pick:hover { border-color: var(--gs-accent); }
.gs-datefield-native { position: absolute; left: 0; bottom: 0; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.gs-datefield-bad { border-color: var(--gs-danger-fg) !important; }

/* ---- searchable account picker (accountpicker.js) + column sort targets ---- */
.gs-acct { position: relative; }
.gs-acct-input { width: 100%; box-sizing: border-box; }
.gs-acct-suggest { position: absolute; left: 0; right: 0; top: 100%; z-index: 10060; max-height: 240px; overflow-y: auto; }
.gs-acct-item { display: flex; flex-wrap: wrap; gap: 2px 8px; align-items: baseline; }
.gs-acct-main { color: var(--gs-text); }
.gs-acct-sub { color: var(--gs-text-3); font-size: 11px; }
.gs-acct-none { padding: 6px 10px; font-size: 11px; color: var(--gs-text-3); font-style: italic; }
.cp-vis-sort, .eu-obj-sort, .st-obj-sort { cursor: pointer; user-select: none; }
.cp-vis-bydate, .eu-vis-bydate, .st-vis-bydate { margin-left: 8px; font-size: 11px; font-weight: normal; color: var(--gs-text-3); }
.cp-vis-bydate:hover, .eu-vis-bydate:hover, .st-vis-bydate:hover { color: var(--gs-accent); }
.eu-sort-arrow { font-size: 10px; }

/* ---- "Copied N IMEIs" confirmation toast (imeicopy.js) ---- */
.gs-copy-toast {
  position: fixed; left: 50%; bottom: 32px; transform: translateX(-50%);
  z-index: 100000; max-width: 90vw;
  background: var(--gs-text); color: var(--gs-surface);
  padding: 10px 16px; border-radius: 8px; font-size: 13px; font-weight: 600;
  box-shadow: 0 4px 18px rgba(0,0,0,0.28);
  opacity: 1; transition: opacity 0.4s ease;
}
.gs-copy-toast-out { opacity: 0; }
