/* Beekon console — shell + component vocabulary.
 *
 * Rules that carry the brand and must not drift:
 *   - Elevation is surface tone + 1px hairlines. There is no drop shadow in the
 *     brand except one amber-tinted glow; overlays are the only exception here.
 *   - Tables/panels use the 1px-gap grid motif: gap:1px over a --line
 *     background with --bg-elev cells, so borders never double up.
 *   - Mono is a semantic layer (ids, keys, coordinates, micro-labels), not
 *     decoration.
 *   - Empty and error states are VISUALLY DISTINCT. Conflating them was the
 *     worst information-design fault of the console this replaces.
 */

/* ---------- base ---------- */

* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; height: 100%; }

body {
  font-family: var(--f-body);
  font-size: var(--t-body);
  line-height: 1.5;
  letter-spacing: var(--tr-body);
  background: var(--bg);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { margin: 0; font-family: var(--f-display); font-weight: 500; }
p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }

a { color: var(--accent-text); text-decoration: none; }
a:hover { text-decoration: underline; }
/* A link inside a sentence is underlined: its colour alone is 1.4:1 against the
   text around it (M4-0407, audit G5-12; WCAG 1.4.1). Buttons and tags that
   happen to be links keep their own look. Usage's stat-card line (M4-0477)
   is a sentence too: "None failed in this window. Webhooks". */
:is(p, .page-sub, .footnote, .field-hint, .empty-body, .usage-stat-sub) a:not(.btn):not(.config-tag) {
  text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px;
}

:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 1px;
  border-radius: var(--r-sm);
}

/* The router moves focus to each view's heading so keyboard and screen-reader
   users land in the new page. That is a programmatic focus on a non-interactive
   element — it must not paint a focus ring, which would read as a selected
   control. Interactive elements keep theirs. */
[tabindex="-1"]:focus,
[tabindex="-1"]:focus-visible { outline: none; }

/* Screen-reader-only, but focusable (skip link). */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip:focus-visible {
  position: fixed; top: var(--s-5); left: var(--s-5); z-index: 100;
  width: auto; height: auto; margin: 0; clip: auto;
  padding: var(--s-3) var(--s-6); background: var(--bg-elev);
  border: 1px solid var(--line-strong); border-radius: var(--r-md);
}

/* ---------- typography helpers ---------- */

.eyebrow {
  font-family: var(--f-mono);
  font-size: var(--t-eyebrow);
  font-weight: 500;
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--accent-text);
}
.eyebrow-muted { color: var(--ink-3); }

.page-title {
  font-size: var(--t-display);
  letter-spacing: var(--tr-display);
  line-height: 1.15;
}
.h2 { font-size: var(--t-h2); letter-spacing: var(--tr-h2); }
.h3 { font-size: var(--t-h3); letter-spacing: var(--tr-h3); }

.muted { color: var(--ink-3); }
.faint { color: var(--ink-4); }
.small { font-size: var(--t-sm); }
.micro { font-size: var(--t-micro); }
.mono { font-family: var(--f-mono); }
.num { font-variant-numeric: tabular-nums; }

.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* An id/ULID rendered inline: mono, dimmed, never wrapping mid-token. */
.id {
  font-family: var(--f-mono);
  font-size: var(--t-micro);
  color: var(--ink-3);
  letter-spacing: 0;
}

/* ---------- app shell: Console v4 (D173, M4-0472) ----------
   Main.dc.html's frame: the page is a rounded shell (--bg, a hairline, a soft
   shadow) on a cream ground (--ground), with Rail.dc.html's icon rail down its
   left side and the top bar over the page. Below 768 px the frame drops away
   and the rail becomes one top row of icons (the phone block below). */

#beekon-console-root:has(> .shell) { padding: var(--shell-pad); }
body:has(> #beekon-console-root > .shell) { background: var(--bg); }

.shell {
  display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start;
  min-height: calc(100vh - 2 * var(--shell-pad)); min-height: calc(100dvh - 2 * var(--shell-pad));
  background: var(--bg);
  /* Frameless (D175, 2026-10-26): no gutter, hairline, radius or shadow round
     the console; it fills the window edge to edge. */
  border: 0; border-radius: 0; box-shadow: none;
  /* clip, not hidden: the corners clip a full-bleed page (the Map) without
     making the shell a scroll container, so the rail and the top bar still
     stick to the window. */
  overflow: clip;
}
.shell-main { min-width: 0; }

/* The rail (Rail.dc.html): 56 px of 44 px tiles, 212 px with its labels. */
.rail {
  --tile: 44px;
  --rail-gap: 4px;
  position: sticky; top: 0; z-index: 25;
  width: var(--rail-w);
  height: calc(100vh - var(--frame-y)); height: calc(100dvh - var(--frame-y));
  display: flex; flex-direction: column; align-items: stretch; gap: var(--rail-gap);
  padding: 14px 6px 14px;
  overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: transparent transparent;
  transition: width var(--dur) var(--ease);
}
.rail:hover, .rail:focus-within { scrollbar-color: var(--line-strong) transparent; }
.rail[data-open="true"] { width: var(--rail-w-open); }

.rail-brand, .rail-toggle, .rail .nav-item, .rail-docs {
  display: flex; align-items: center; gap: 12px; flex: none;
  height: var(--tile); min-height: var(--tile); width: 100%;
  padding: 0 calc((var(--tile) - 20px) / 2); box-sizing: border-box;
  border: 1px solid transparent; border-radius: var(--r-lg);
  background: transparent; color: var(--ink-3);
  font: inherit; font-size: var(--t-body); white-space: nowrap; text-decoration: none;
  cursor: pointer; overflow: hidden;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.rail svg { width: 20px; height: 20px; flex: none; }
.rail :is(.rail-toggle, .nav-item, .rail-docs):hover {
  background: color-mix(in oklch, var(--ink) 5%, transparent); color: var(--ink); text-decoration: none;
}
.rail :is(.rail-brand, .rail-toggle, .nav-item, .rail-docs):focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* The brand: the console's mark on a raised tile, the name beside it open. */
.rail-brand {
  margin-bottom: 12px; padding: 0 8px; color: var(--ink);
  background: var(--bg-elev); border-color: var(--line);
  width: var(--tile);
}
.rail-brand svg { width: 26px; height: 26px; }
.rail-brand-name { font-family: var(--f-display); font-size: 17px; font-weight: 600; letter-spacing: -0.022em; }
.rail[data-open="true"] .rail-brand { width: 100%; background: transparent; border-color: transparent; padding-left: 8px; }
.rail[data-open="true"] .rail-brand svg { margin: 0; }

/* The current tile is filled (a raised tile, the ink at full strength). */
.rail .nav-item[aria-current="page"] {
  background: var(--bg-elev); border-color: var(--line); color: var(--ink);
  box-shadow: 0 1px 2px color-mix(in oklch, var(--ink) 6%, transparent);
  font-weight: 500;
}
.rail .nav-item[aria-current="page"]:focus-visible { box-shadow: var(--focus-ring); }

/* Labels show only with the rail open; the tiles are named by aria-label and
   title either way. */
.rail :is(.nav-label, .rail-toggle-label, .rail-docs-label, .rail-brand-name) { display: none; }
.rail[data-open="true"] :is(.nav-label, .rail-toggle-label, .rail-docs-label, .rail-brand-name) { display: inline; }
.rail-toggle-label, .rail-docs-label { color: var(--ink-2); }

/* A group: a divider closed, its name open (Rail.dc.html). */
.rail .nav-group { display: flex; flex-direction: column; gap: var(--rail-gap); }
.rail-divider { width: 22px; height: 1px; background: var(--line); margin: 6px auto; flex: none; }
.rail-group-label {
  display: none; padding: 10px 12px 2px;
  font-size: var(--t-micro); font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-3); white-space: nowrap;
}
.rail[data-open="true"] .rail-divider { display: none; }
.rail[data-open="true"] .rail-group-label { display: block; }
.rail-spacer { flex: 1 1 auto; min-height: 8px; }

/* A laptop window (M4-0445): the rail fits the window without a scrollbar of
   its own: 44 px tiles need 775 px of rail, so 38 px tiles below 800 px tall
   (631 px), 34 px below 680. */
@media (min-width: 768px) and (max-height: 800px) {
  .rail { --tile: 38px; --rail-gap: 2px; padding-block: 10px; }
  .rail-brand { margin-bottom: 8px; }
  .rail-divider { margin-block: 4px; }
}
@media (min-width: 768px) and (max-height: 680px) {
  .rail { --tile: 34px; --rail-gap: 1px; padding-block: 8px; }
}

/* The top bar: the project chip, the breadcrumb, the org switcher and the
   account menu, on the shell's own surface (Main.dc.html). */
.topbar {
  height: var(--topbar-h);
  background: var(--bg);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-5);
  padding: 0 var(--s-8) 0 var(--s-5);
  position: sticky; top: 0; z-index: 20;
}
.topbar-right { display: flex; align-items: center; gap: var(--s-5); }

/* The top bar's breadcrumb (Topbar.dc.html, M4-0377): scope / page. It
   shrinks before anything else in the bar. */
.crumbs { display: flex; align-items: center; gap: var(--s-3); min-width: 0; flex: 1 1 200px; font-size: var(--t-sm); }
.crumb-scope { color: var(--ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; flex: 0 1 auto; }
.crumb-sep { color: var(--ink-4, var(--ink-3)); }
.crumb-page { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.crumbs [hidden] { display: none; }

/* The account menu (Topbar.dc.html): initials, then a menu under them. */
.account { position: relative; }
.account-btn {
  display: inline-flex; align-items: center; gap: var(--s-3);
  height: 44px; padding: 0 12px 0 4px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--bg-elev); color: var(--ink);
  font: inherit; cursor: pointer;
}
.account-btn:hover { border-color: var(--line-strong); }
.account-avatar {
  width: 34px; height: 34px; border-radius: var(--r-pill);
  background: color-mix(in oklch, var(--honey) 22%, transparent); color: var(--accent-text);
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 600; font-size: 12.5px;
}
.account-menu {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 30;
  width: 280px; max-width: calc(100vw - 32px); box-sizing: border-box; padding: var(--s-3);
  border-radius: var(--r-lg, 14px); border: 1px solid var(--line); background: var(--bg-elev);
  box-shadow: 0 12px 32px color-mix(in oklch, oklch(20% 0.03 60) 16%, transparent);
  display: flex; flex-direction: column; gap: 2px;
}
.account-menu[hidden] { display: none; }
.account-menu-head { padding: var(--s-3) var(--s-4) var(--s-4); border-bottom: 1px solid var(--line); margin-bottom: var(--s-2); }
.account-menu-email { font-weight: 600; }
.account-menu-item, .account-menu-foot .btn {
  display: flex; align-items: center; width: 100%; min-height: 38px; box-sizing: border-box;
  padding: 0 var(--s-4); border-radius: var(--r-md); border: 0; background: transparent;
  color: var(--ink); font: inherit; font-size: var(--t-sm); text-align: left; justify-content: flex-start; cursor: pointer;
}
.account-menu-item:hover, .account-menu-item:focus-visible,
.account-menu-foot .btn:hover, .account-menu-foot .btn:focus-visible {
  background: color-mix(in oklch, var(--ink) 5%, transparent); text-decoration: none;
}
.account-menu-foot { border-top: 1px solid var(--line); margin-top: var(--s-2); padding-top: var(--s-2); }

/* The project switcher (M4-0180), the top bar's project chip since Console v4
   (D173, Main.dc.html): a pill naming the project and its region; since
   M4-0410 (G1-02, G1-03) it opens a popover listbox. */
.switcher { position: relative; display: flex; flex-direction: column; gap: var(--s-2); min-width: 0; flex: 0 1 auto; }
.switcher-btn {
  display: flex; align-items: center; gap: var(--s-3); max-width: 320px; min-width: 0; height: 44px;
  padding: 0 14px 0 16px; box-sizing: border-box;
  border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--bg-elev); color: var(--ink);
  font: inherit; font-size: var(--t-h3); font-weight: 500; text-align: left; cursor: pointer;
}
.switcher-btn:hover { border-color: var(--line-strong); }
.switcher-btn > .switcher-name { flex: 1 1 auto; min-width: 0; }
.switcher-btn > svg { flex: none; color: var(--ink-3); }
.switcher-btn[aria-expanded="true"] > svg { transform: rotate(180deg); }
.region-chip { flex: none; }
.switcher-btn > .region-chip { border: 0; border-radius: var(--r-pill); background: var(--bg-sunk); color: var(--ink-2); }
.switcher-btn > .region-chip[hidden] { display: none; }
.switcher-pop {
  position: absolute; left: 0; top: calc(100% + 6px); z-index: 30;
  width: max(100%, 300px); max-width: calc(100vw - 32px); box-sizing: border-box;
  display: flex; flex-direction: column; gap: var(--s-2); padding: var(--s-2);
  border: 1px solid var(--line); border-radius: var(--r-lg, 14px); background: var(--bg-elev);
  box-shadow: 0 12px 32px color-mix(in oklch, oklch(20% 0.03 60) 16%, transparent);
}
.switcher-pop[hidden], .switcher-filter[hidden], .switcher-new[hidden] { display: none; }
.switcher-filter { width: 100%; min-width: 0; box-sizing: border-box; }
.switcher-list { list-style: none; margin: 0; padding: 0; max-height: min(320px, 50vh); overflow-y: auto; outline: none; }
.switcher-option {
  display: flex; align-items: center; gap: var(--s-3); min-height: 36px; padding: 0 var(--s-3);
  border-radius: var(--r-md); font-size: var(--t-sm); color: var(--ink); cursor: pointer;
}
.switcher-option[hidden] { display: none; }
.switcher-option > .switcher-option-name { flex: 1 1 auto; min-width: 0; }
.switcher-option:hover { background: color-mix(in oklch, var(--ink) 5%, transparent); }
.switcher-option.is-active { background: color-mix(in oklch, var(--honey) 16%, transparent); outline: 2px solid var(--focus, var(--honey)); outline-offset: -2px; }
.switcher-option[aria-selected="true"] { font-weight: 600; }
.switcher-new { justify-content: flex-start; gap: var(--s-2); border-top: 1px solid var(--line); border-radius: 0 0 var(--r-md) var(--r-md); }

/* The top bar's org switcher (M4-0336), in more than one org: a pill select. */
.topbar .org-switcher { height: 44px; max-width: 240px; border-radius: var(--r-pill); padding-inline: 14px 32px; }

/* The account menu's Theme group (M4-0410, G1-37): three menuitemradio, the
   current one checked. */
.account-menu-group { display: flex; flex-direction: column; gap: 2px; border-top: 1px solid var(--line); margin-top: var(--s-2); padding-top: var(--s-2); }
.account-menu-label { padding: var(--s-2) var(--s-4) 0; }
.account-menu-radio { justify-content: space-between; }
.account-menu-radio[aria-checked="true"] { font-weight: 600; }
.account-menu-radio[aria-checked="true"]::after { content: "✓"; color: var(--accent-text); }

/* The frame boot paints while /me is in flight (M4-0410, G5-16): the rail's
   brand and the top bar as empty chrome, and a skeleton where the page goes. */
.shell-boot .boot-rail { display: flex; }
.shell-boot .boot-top { height: var(--topbar-h); }

/* ---------- v4 primitives (D173, Main.dc.html) ----------
   For the screen rows (0473–0482) to build pages from: a pill button or chip,
   the tinted card (Needs attention) and the big stat number. The card itself
   is .card (22 px, below). */
.pill-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-3);
  height: 44px; padding: 0 16px; box-sizing: border-box;
  border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--bg-elev); color: var(--ink);
  font: inherit; font-size: var(--t-h3); font-weight: 500; white-space: nowrap; cursor: pointer; text-decoration: none;
}
.pill-btn:hover { border-color: var(--line-strong); text-decoration: none; }
.pill-btn-sm { height: 36px; padding: 0 12px; font-size: var(--t-sm); }
.pill-chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px;
  border-radius: var(--r-pill); background: var(--bg-sunk); color: var(--ink-2);
  font-size: var(--t-xs); white-space: nowrap;
}
.card-tint { background: var(--honey-soft); border-color: transparent; }
.stat-num { font-size: 42px; font-weight: 400; letter-spacing: -0.03em; line-height: 1; font-variant-numeric: tabular-nums; }
.stat-num-sub { font-size: 18px; color: var(--ink-2); letter-spacing: -0.01em; }

/* Lists of projects as links: the project not-found page and Choose a project
   (M4-0410, G1-28, G1-31). */
.link-list { list-style: none; margin: var(--s-3) 0 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.link-list a { display: flex; align-items: center; gap: var(--s-3); min-height: 40px; padding: 0 var(--s-4); border-radius: var(--r-md); color: var(--ink); }
.link-list a:hover { background: color-mix(in oklch, var(--ink) 5%, transparent); text-decoration: none; }
.link-list-name { font-weight: 500; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.not-found-list { margin-top: var(--s-7); }
.choose-list { margin-top: var(--s-6); }

/* The Organisation page's tabs (Organisation.dc.html, M4-0377): links. */
.org-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--line); margin-bottom: var(--s-7); overflow-x: auto; }
.org-tab {
  display: inline-flex; align-items: center; min-height: 40px;
  padding: 10px 14px; color: var(--ink-3); font-weight: 500; white-space: nowrap;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.org-tab:hover { color: var(--ink); text-decoration: none; }
.org-tab[aria-current="page"] { color: var(--ink); border-bottom-color: var(--honey); }

/* Every page fills the main column; long prose keeps its own ch-based measure. */
.content { padding: var(--s-9) var(--s-8) var(--s-10); }
.content-wide { padding-left: var(--s-7); padding-right: var(--s-7); }
.page-head { margin-bottom: var(--s-8); }
.page-head .page-title { margin-top: var(--s-2); }
.page-head-row { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s-6); flex-wrap: wrap; }
/* The view's one-sentence subtitle (Design v2's .v2-sub). */
.page-sub { color: var(--ink-3); font-size: var(--t-sm); margin-top: var(--s-2); max-width: 80ch; }
/* The project and its region, under the subtitle (M4-0281). */
.page-meta { display: flex; align-items: center; gap: var(--s-4); flex-wrap: wrap; margin-top: var(--s-3); font-size: var(--t-xs); color: var(--ink-3); }
/* A note under a table (Design v2's Devices footnote). */
.footnote { margin-top: var(--s-5); max-width: 80ch; }

section + section { margin-top: var(--s-9); }
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-5); margin-bottom: var(--s-5); }

/* v4 kit ---------------------------------------------------------------------
   Console v4's component kit (D173, M4-0484): shadcn/ui's look and behaviour
   in the console's own CSS, one block, with the class names of the canvas's
   Components.dc.html (between its kit:start and kit:end) so a screen copies
   markup from the canvas as it stands. lib/ui.js builds each one accessibly.
   Colours are tokens only, so every component follows the dark theme; the
   canvas's hex values approximate them. Two deliberate differences from the
   canvas: the focus ring is --focus-ring (a surface gap and a 2 px accent ring,
   7.45:1 / 8.12:1 — the canvas's 35% honey wash is the 1.2:1 ring the
   2026-10-15 audit removed, G5-03), and the phone sizes start below 768 px,
   the console's phone breakpoint, rather than the canvas's 600.
   It sits where the older button rules sat, ahead of every page's rules, so a
   page's own rule (a phone-only button hidden on the desktop) still wins; the
   older generic rules it replaces are gone. */

.sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* [hidden] wins over every kit display. */
:is(.btn, .chip, .seg, .field, .input, .ig, .search, .select, .sw, .sw-row, .check, .choice-group, .badge, .tbl-wrap,
    .alert, .tip, .menu, .dialog, .toast, .empty, .pager)[hidden] { display: none; }

/* Button: 40 by default, 36 for a dense table only; fully round. Primary once
   per view; secondary for the rest, action links in cards included; outline for
   a chosen state; ghost for "See all" in a card head; tinted for a header link
   into a filtered view; destructive outlined, filled red (danger) only inside a
   confirmation dialog. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 40px; padding: 0 16px; box-sizing: border-box;
  border: 1px solid transparent; border-radius: var(--r-pill);
  background: transparent; color: var(--ink);
  font-family: var(--f-body); font-size: var(--t-body); font-weight: 500; line-height: 1.2; letter-spacing: var(--tr-body);
  white-space: nowrap; text-decoration: none; cursor: pointer; flex: 0 0 auto;
  transition: background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.btn:hover { text-decoration: none; }
.btn-primary { background: var(--accent-solid); color: var(--accent-solid-ink); }
.btn-primary:not(:disabled):hover { background: color-mix(in oklch, var(--accent-solid) 86%, var(--bg)); color: var(--accent-solid-ink); }
.btn-secondary { background: var(--bg-elev); border-color: var(--line-strong); color: var(--ink); }
.btn-secondary:not(:disabled):hover { background: var(--bg-sunk); color: var(--ink); }
.btn-outline { background: transparent; border-color: var(--ink); color: var(--ink); }
.btn-outline:not(:disabled):hover { background: var(--bg-sunk); color: var(--ink); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--ink); }
.btn-ghost:not(:disabled):hover { background: var(--bg-sunk); color: var(--ink); }
.btn-tinted { background: var(--honey-soft); color: var(--accent-text); }
.btn-tinted:not(:disabled):hover { background: color-mix(in oklch, var(--honey-soft) 80%, var(--honey)); color: var(--accent-text); }
.btn-destructive { background: var(--bg-elev); border-color: color-mix(in oklch, var(--red) 35%, var(--line)); color: var(--red); }
.btn-destructive:not(:disabled):hover { background: var(--red-soft); color: var(--red); }
.btn-danger { background: var(--red); border-color: var(--red); color: var(--bg-elev); }
.btn-danger:not(:disabled):hover { background: color-mix(in oklch, var(--red) 86%, var(--ink)); color: var(--bg-elev); }
.btn-sm { min-height: 36px; padding: 0 12px; font-size: var(--t-sm); }
.btn-icon { min-width: 40px; padding: 0; }
.btn-icon.btn-sm { min-width: 36px; }
.btn-icon svg { width: 16px; height: 16px; display: block; flex: none; }
.btn-block { display: flex; width: 100%; }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .5; cursor: not-allowed; }
/* A disabled primary keeps its own colours rather than the enabled one faded:
   faded, the dark theme's honey still read as a live Save (M4-0407, G4-10). */
.btn-primary:disabled { opacity: 1; background: var(--bg-sunk); color: var(--ink-3); border-color: var(--line-strong); }
.btn:focus-visible, :is(button, a).chip:focus-visible, .seg > *:focus-visible, .sw:focus-visible,
.btn.is-focus { outline: none; box-shadow: var(--focus-ring); }

/* Chip: a filter toggle (aria-pressed) or an anchor-bar link (aria-current).
   A span.chip stays the small tag it was. */
:is(button, a).chip {
  display: inline-flex; align-items: center; gap: 6px; min-height: 40px; padding: 0 14px; box-sizing: border-box;
  border: 1px solid var(--line-strong); border-radius: var(--r-pill); background: var(--bg-elev); color: var(--ink);
  font-family: var(--f-body); font-size: 13.5px; text-decoration: none; white-space: nowrap; cursor: pointer;
}
:is(button, a).chip:hover { background: var(--bg-sunk); color: var(--ink); text-decoration: none; }
:is(button, a).chip:is([aria-pressed="true"], [aria-current="true"], [aria-current="location"], [aria-current="page"]) { border-color: var(--ink); font-weight: 500; }

/* Segmented control and tabs: a radiogroup (aria-checked), a tablist
   (aria-selected), links (aria-current="page") or a pressed group. */
.seg {
  display: inline-flex; flex-wrap: wrap; gap: 2px; padding: 4px; box-sizing: border-box; max-width: 100%;
  border: 0; border-radius: 24px; background: var(--bg-sunk);
}
.seg > * {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 40px; padding: 0 16px; box-sizing: border-box;
  border: 0; border-radius: var(--r-pill); background: transparent; color: var(--ink-2);
  font-family: var(--f-body); font-size: var(--t-body); text-decoration: none; white-space: nowrap; cursor: pointer;
}
.seg > *:hover { color: var(--ink); text-decoration: none; }
.seg > :is([aria-checked="true"], [aria-selected="true"], [aria-current="page"], [aria-pressed="true"]) {
  background: var(--bg-elev); color: var(--ink); font-weight: 500;
  box-shadow: 0 1px 2px color-mix(in oklch, var(--ink) 10%, transparent);
}
.seg-sm > * { padding: 0 12px; font-size: 13.5px; }

/* Field: the label above the control, the description below. */
.field { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.field > :is(.field-label, .field-hint, .field-error) { margin: 0; }
.field > :is(.seg, .select, .ig) { align-self: flex-start; }
/* field(label, opts) and the pages' own .field keep their old spacing. */
.field-block { margin-bottom: var(--s-5); }
.label { display: block; font-size: var(--t-body); font-weight: 500; line-height: 1.35; color: var(--ink); }
.desc { margin: 0; font-size: var(--t-sm); line-height: 1.5; color: var(--ink-3); }
/* .field-error is under fields, below. */

/* Input: 40 px, a 10 px radius; focus is an ink border and the ring. */
.input {
  display: block; width: 100%; height: 40px; padding: 0 12px; box-sizing: border-box;
  border: 1px solid var(--line-strong); border-radius: var(--r-md); background: var(--bg-elev); color: var(--ink);
  font-family: var(--f-body); font-size: var(--t-h3); outline: none;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
textarea.input { height: auto; min-height: 80px; padding: 8px 12px; line-height: 1.5; }
.input::placeholder, .ig input::placeholder { color: var(--ink-4); }
.input:focus, .input:focus-visible, .input.is-focus { outline: none; border-color: var(--ink); box-shadow: var(--focus-ring); }
.input[aria-invalid="true"], .ig[data-invalid="true"] { border-color: var(--red); }
.input:disabled { opacity: .5; cursor: not-allowed; background: var(--bg-sunk); }

/* Input group: the control with a unit on a sunk segment. */
.ig {
  display: flex; align-items: stretch; width: 100%; max-width: 280px; min-height: 40px; box-sizing: border-box;
  border: 1px solid var(--line-strong); border-radius: var(--r-md); background: var(--bg-elev); overflow: hidden;
}
.ig input {
  flex: 1 1 auto; min-width: 0; margin: 0; padding: 0 12px; border: 0; background: transparent;
  font-family: var(--f-body); font-size: var(--t-h3); color: var(--ink); text-align: left; outline: none;
}
.ig input:focus, .ig input:focus-visible { outline: none; }
.ig-unit {
  display: flex; align-items: center; padding: 0 12px; white-space: nowrap;
  background: var(--bg-sunk); border-left: 1px solid var(--line-strong); font-size: 13.5px; color: var(--ink-3);
}
.ig:focus-within, .ig.is-focus { border-color: var(--ink); box-shadow: var(--focus-ring); }
.ig:has(input:disabled) { opacity: .5; }

/* Search: the magnifier inside, the label visually hidden. */
.search { position: relative; display: flex; align-items: center; flex: 1 1 200px; max-width: 300px; min-width: 0; }
.search > svg { position: absolute; left: 12px; width: 16px; height: 16px; color: var(--ink-3); pointer-events: none; }
.search .input { padding-left: 36px; }

/* Select (more than four options): the native control, the chevron drawn over
   it. A select.select on its own (the older markup) draws its chevron itself. */
:not(select).select { position: relative; display: inline-flex; align-items: center; max-width: 100%; }
:not(select).select > svg { position: absolute; right: 12px; width: 14px; height: 14px; color: var(--ink-2); pointer-events: none; }
select.select, .select > select {
  appearance: none; -webkit-appearance: none;
  height: 40px; max-width: 100%; padding: 0 36px 0 12px; box-sizing: border-box;
  border: 1px solid var(--line-strong); border-radius: var(--r-md); background-color: var(--bg-elev); color: var(--ink);
  font-family: var(--f-body); font-size: var(--t-h3); cursor: pointer; outline: none;
}
.select > select { width: 100%; }
select.select {
  width: 100%;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 18px) 52%, calc(100% - 13px) 52%;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat;
}
:is(select.select, .select > select):focus-visible { outline: none; border-color: var(--ink); box-shadow: var(--focus-ring); }
:is(select.select, .select > select):disabled { opacity: .5; cursor: not-allowed; background-color: var(--bg-sunk); }

/* Switch: a real button, role="switch", a 40 × 24 track in a 44 × 40 target. */
.sw {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 44px; min-width: 44px; height: 40px; padding: 0; border: 0; border-radius: var(--r-pill);
  background: transparent; cursor: pointer;
}
.sw-t { position: relative; display: block; width: 40px; height: 24px; border-radius: var(--r-pill); background: var(--line-strong); transition: background-color var(--dur-fast) var(--ease); }
.sw-k {
  position: absolute; top: 2px; left: 2px; width: 20px; height: 20px; border-radius: 50%;
  background: var(--bg-elev); box-shadow: 0 1px 2px color-mix(in oklch, var(--ink) 25%, transparent);
  transition: transform var(--dur-fast) var(--ease);
}
.sw[aria-checked="true"] .sw-t { background: var(--accent-solid); }
.sw[aria-checked="true"] .sw-k { transform: translateX(16px); }
.sw:disabled { opacity: .5; cursor: not-allowed; }
.sw-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.sw-row > .field { gap: 4px; }
.sw-row > .sw { margin-top: -8px; }

/* Checkbox and radio: an 18 px box in a 40 px label row. */
.choice-group { border: 0; margin: 0; padding: 0; min-width: 0; }
.choice-group > legend { padding: 0; margin-bottom: 4px; }
.check { display: flex; align-items: center; gap: 10px; min-height: 40px; font-size: var(--t-body); color: var(--ink); cursor: pointer; }
.check input { flex: 0 0 auto; width: 18px; height: 18px; margin: 0; accent-color: var(--accent-solid); cursor: pointer; }
.check-note { font-size: var(--t-xs); color: var(--ink-3); }

/* Badge: a dot and a word, never colour alone. */
.badge { display: inline-flex; align-items: center; gap: 6px; min-width: 0; font-size: 13.5px; color: var(--ink); }
.badge::before { content: ""; flex: 0 0 auto; width: 8px; height: 8px; border-radius: 50%; background: var(--ink-4); }
.badge-healthy::before, .badge-kept::before { background: var(--green); }
.badge-attention { color: var(--red); }
.badge-attention::before, .badge-filtered::before { background: var(--red); }
.badge-quiet::before { background: var(--ink-4); }
.badge-none::before { background: var(--line-strong); }
.badge-sub { display: block; font-size: var(--t-xs); color: var(--ink-3); }

/* Card: 22 px, a hairline, 20 px in. Tinted holds what needs attention. */
.card-tinted { background: var(--honey-soft); border-radius: var(--r-xl); padding: var(--s-7); box-sizing: border-box; }
.card-title { margin: 0; font-size: var(--t-h2); font-weight: 500; letter-spacing: var(--tr-h3); }
.card-sub { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--ink-2); }

/* Table: a muted header row, 48 px rows, a sunk hover, a tinted current row. */
.tbl-wrap { overflow-x: auto; }
.tbl { width: 100%; border-collapse: collapse; font-size: var(--t-body); }
.tbl th { padding: 10px 8px; text-align: left; font-size: var(--t-xs); font-weight: 500; color: var(--ink-3); white-space: nowrap; }
.tbl td { height: 48px; padding: 12px 8px; box-sizing: border-box; border-top: 1px solid var(--line); vertical-align: top; }
.tbl :is(th, td):first-child { padding-left: 12px; }
.tbl :is(th, td):last-child { padding-right: 12px; }
.tbl tbody tr:hover td { background: var(--bg-sunk); }
.tbl tbody tr:is([aria-current="true"], [aria-selected="true"]) td { background: var(--honey-soft); }
.tbl tbody tr td:first-child { border-radius: 12px 0 0 12px; }
.tbl tbody tr td:last-child { border-radius: 0 12px 12px 0; }
.tbl .num { text-align: right; font-variant-numeric: tabular-nums; }
.tbl a.cell-link { display: inline-block; padding: 11px 0; margin: -11px 0; color: inherit; text-decoration: none; }
.tbl a.cell-link:hover { text-decoration: underline; }

/* Alert: inline, inside the card it concerns. */
.alert { display: flex; gap: 12px; align-items: flex-start; padding: 14px 16px; border-radius: 16px; font-size: 13.5px; line-height: 1.5; color: var(--ink); }
.alert > svg { flex: 0 0 auto; width: 18px; height: 18px; margin-top: 1px; }
.alert-title { display: block; font-size: var(--t-body); font-weight: 500; }
.alert-info { background: var(--blue-soft); }
.alert-info > svg { color: var(--blue); }
.alert-warning { background: var(--honey-soft); }
.alert-warning > svg { color: var(--accent-text); }
.alert-error { background: var(--red-soft); }
.alert-error > svg { color: var(--red); }

/* Tooltip: repeats a value on hover or focus; never the only copy of a fact. */
.tip {
  display: inline-block; padding: 8px 12px; border-radius: 12px;
  background: var(--ink); color: var(--bg); font-size: 13.5px; font-weight: 500; line-height: 1.35;
  box-shadow: 0 8px 24px color-mix(in oklch, var(--ink) 20%, transparent);
}
.tip-wrap { position: relative; display: inline-flex; }
.tip-wrap > .tip { position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%); z-index: 70; white-space: nowrap; pointer-events: none; }

/* Dropdown menu: opened by a button with aria-haspopup="menu"; 40 px items, a
   destructive item last after a separator. */
.menu {
  display: flex; flex-direction: column; min-width: 200px; padding: 6px; box-sizing: border-box;
  background: var(--bg-elev); border: 1px solid var(--line); border-radius: 16px;
  box-shadow: 0 8px 24px color-mix(in oklch, var(--ink) 10%, transparent);
}
.menu > [role="menuitem"] {
  display: flex; align-items: center; min-height: 40px; padding: 0 12px; border: 0; border-radius: var(--r-md);
  background: transparent; color: var(--ink); font-family: var(--f-body); font-size: var(--t-body); text-align: left; cursor: pointer;
}
.menu > [role="menuitem"]:hover, .menu > [role="menuitem"]:focus-visible { background: var(--bg-sunk); outline: none; box-shadow: none; }
.menu > [role="menuitem"]:focus-visible { box-shadow: inset 0 0 0 2px var(--accent-text); }
.menu > .menu-danger { color: var(--red); }
.menu > [role="separator"] { height: 1px; margin: 6px 0; background: var(--line); }
.menu-wrap { position: relative; display: inline-flex; }
.menu-wrap > .menu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 70; }

/* Dialog: confirms what cannot be undone. modal() draws every dialog as one. */
.dialog {
  display: flex; flex-direction: column; gap: 16px; max-width: 440px; padding: 24px; box-sizing: border-box;
  background: var(--bg-elev); border: 1px solid var(--line); border-radius: var(--r-xl);
  box-shadow: 0 24px 60px color-mix(in oklch, var(--ink) 20%, transparent);
}
.dialog.modal-wide { max-width: 620px; }
.dialog-title { margin: 0; font-size: 19px; font-weight: 500; letter-spacing: var(--tr-h3); }
.dialog > .desc { font-size: var(--t-body); color: var(--ink-2); }
.dialog-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }

/* Toast: says something worked; ink, fully round, bottom of the viewport. */
.toast {
  display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 2px 4px 2px 18px; box-sizing: border-box;
  border: 0; border-radius: 22px; background: var(--ink); color: var(--bg); font-size: var(--t-body);
  box-shadow: 0 8px 24px color-mix(in oklch, var(--ink) 20%, transparent);
}
.toast-msg { flex: 1; min-width: 0; padding: 8px 0; }
.toast .toast-close { flex: none; margin: 0; color: inherit; background: transparent; }
.toast .toast-close:hover { background: color-mix(in oklch, var(--bg) 16%, transparent); color: inherit; }
.toast-bad { background: var(--red); color: var(--bg-elev); }

/* Skeleton: the shimmer bars above (.skel, M4-0443), which stop for reduced
   motion; a loading table keeps its rows' shape. */

/* Empty state: a centred muted sentence and the one action that fixes it. */
.empty {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  padding: 24px 16px; border: 0; border-radius: 16px; background: var(--bg-sunk); color: var(--ink-3); text-align: center;
}
.empty p { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--ink-3); max-width: 44ch; }
.empty > :is(.empty-body, .empty-action) { margin: 0; }

/* Pagination footer: "Showing N of M" left, Show more right; at the end the
   button goes and the count says so. */
.pager {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px;
  padding-top: 12px; border-top: 1px solid var(--line); font-size: 13.5px; color: var(--ink-3);
}

/* A text link's hit area (an inline link keeps its look). */
.hit { padding: 12px 0; }

/* Phones: every control at least 44 × 44. */
@media (max-width: 767.98px) {
  .btn, :is(button, a).chip, .seg > *, .input, .ig, select.select, .select > select, .check, .menu > [role="menuitem"] { min-height: 44px; }
  .btn-icon, .btn-icon.btn-sm, .sw { min-width: 44px; min-height: 44px; }
  .tbl a.cell-link { padding: 13px 0; margin: -13px 0; }
  .hit { padding: 14px 0; }
}
/* end v4 kit */

.btn-row { display: flex; gap: var(--s-3); flex-wrap: wrap; }

/* ---------- fields ---------- */

.field-label {
  display: block; font-size: var(--t-body); font-weight: 500; line-height: 1.35;
  color: var(--ink); margin-bottom: var(--s-2);
}
.field-req { color: var(--ink-4); font-weight: 400; }

/* Inputs, selects and the field layout are the v4 kit's (above). */
.input.mono { font-family: var(--f-mono); font-size: var(--t-sm); }

.field-hint { font-size: var(--t-sm); line-height: 1.5; color: var(--ink-3); margin-top: var(--s-2); }
.field-error { font-size: var(--t-sm); line-height: 1.5; color: var(--red); margin-top: var(--s-2); }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-4); }
/* M4-0418 (G1-08): a password field's Show password toggle, an eye button
   inside the input's right edge (field({reveal: true})). */
.field-reveal-row { position: relative; }
.field-reveal-row > .input { padding-right: 42px; }
.field-reveal {
  position: absolute; top: 50%; right: 4px; transform: translateY(-50%);
  display: inline-grid; place-items: center; width: 32px; height: 32px; padding: 0;
  border: 0; border-radius: var(--r-sm); background: transparent; color: var(--ink-3); cursor: pointer;
}
.field-reveal svg { width: 16px; height: 16px; display: block; }
.field-reveal:hover, .field-reveal[aria-pressed="true"] { color: var(--ink); }
.field-reveal:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.field-reveal:disabled { cursor: default; color: var(--ink-4); }

/* ---------- region picker (M4-0173) ----------
   A permanent, unrepairable choice, so it reads as a decision rather than a
   form control: full-width rows, the human name leading, the cell id and its
   region as secondary metadata in mono because the id is what the operator
   will recognise inside their SDK key. */

/* The region picker is a fieldset (M4-0272): no browser frame around it. */
fieldset.region-field { border: 0; padding: 0; margin-inline: 0; min-width: 0; }
fieldset.region-field > legend { padding: 0; }
.region-options, .role-options { display: grid; gap: var(--s-2); margin-top: var(--s-3); }
.region-option, .role-option {
  display: flex; align-items: flex-start; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: var(--bg-elev);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.region-option:hover, .role-option:hover { border-color: var(--accent-text); }
.region-option:has(input:checked), .role-option:has(input:checked) { border-color: var(--accent-text); background: var(--bg-sunk); }
.region-option:has(input:focus-visible), .role-option:has(input:focus-visible) { box-shadow: var(--focus-ring); }
.region-option input, .role-option input { margin-top: 3px; accent-color: var(--accent-text); flex: none; }
.region-text, .role-text { display: grid; gap: 2px; }
.region-name, .role-name { font-size: var(--t-body); color: var(--ink); }
.region-meta { font-size: var(--t-micro); color: var(--ink-3); }
/* The Change role dialog's roles (M4-0406) borrow the region picker's cards. */
fieldset.role-field { border: 0; padding: 0; margin: 0; min-width: 0; }
.role-options { margin-top: 0; }
.role-meta { font-size: var(--t-sm); color: var(--ink-2); }
.role-option:has(input:disabled) { cursor: default; opacity: .7; }
.role-warn { margin: 0; color: var(--ink); }

/* ---------- cards + the 1px-gap grid motif ---------- */

.card {
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: var(--r-xl); /* 22 px since Console v4 (D173, Main.dc.html) */
  padding: var(--s-7);
}
.card-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-5); margin-bottom: var(--s-5); }
/* M4-0420 (G1-36): a card's one line under its title (Account.dc.html). */
.card-head:has(> .card-sub) { flex-direction: column; align-items: flex-start; gap: 4px; }
.card-sub { margin: 0; }
.card-foot {
  margin-top: var(--s-6); padding-top: var(--s-5);
  border-top: 1px dashed var(--line);
  display: flex; justify-content: space-between; gap: var(--s-5);
  font-family: var(--f-mono); font-size: var(--t-micro); color: var(--ink-3);
}

/* Seamless bordered grid: one hairline between cells, none doubled. */
.grid-panel {
  display: grid; gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.grid-panel > * { background: var(--bg-elev); padding: var(--s-6); }
.cols-2 { grid-template-columns: 1fr 1fr; }
.cols-3 { grid-template-columns: repeat(3, 1fr); }
.cols-4 { grid-template-columns: repeat(4, 1fr); }

/* ---------- table ---------- */

.table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
:is(.table, .skel-table) th {
  text-align: left; font-weight: 500;
  font-family: var(--f-mono); font-size: var(--t-eyebrow);
  letter-spacing: var(--tr-eyebrow); text-transform: uppercase;
  color: var(--ink-3);
  padding: var(--s-4) var(--s-6);
  border-bottom: 1px solid var(--line);
  background: var(--bg-sunk);
  white-space: nowrap;
}
:is(.table, .skel-table) td { padding: var(--s-5) var(--s-6); border-bottom: 1px solid var(--line); vertical-align: middle; }
:is(.table, .skel-table) tr:last-child td { border-bottom: none; }
.table tbody tr { transition: background var(--dur-fast) var(--ease); }
.table tbody tr:hover { background: color-mix(in oklch, var(--ink) 3%, transparent); }
.table .cell-actions { text-align: right; white-space: nowrap; }
.table .cell-num { text-align: right; font-variant-numeric: tabular-nums; }

/* A table wider than its wrap scrolls inside it; clipping hid Devices' last
   column at 1024 px (M4-0407, G5-10). Ids never break mid-token. */
.table-wrap {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow-x: auto;
  background: var(--bg-elev);
}
.table td .id, .table td .mono, .table td .device-id-short { white-space: nowrap; }

/* The Activity table (M4-0181): fourteen columns, so it scrolls inside its own
   wrap rather than clipping (.table-wrap is overflow:hidden) or pushing the page. */
.table-scroll {
  overflow-x: auto;
  max-width: 100%;
  /* Its columns never size the page: on a phone the shell's single column is a
     plain 1fr, which a wide table's min-content would otherwise stretch. */
  contain: inline-size;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg-elev);
}
/* v2's compact table (Activity.dc.html, M4-0275): 9px cells and xs text, so
   all fourteen columns fit a 1440 px screen without the wrap scrolling. v2 drew
   13 columns at 9px 14px; the Flags column (0216) leaves 8px a side. */
.table-scroll :is(.table, .skel-table) td, .table-scroll :is(.table, .skel-table) th { white-space: nowrap; padding: 9px 8px; }
.table-scroll .table td { font-size: var(--t-xs); line-height: 1.4; }
.table-scroll :is(.table, .skel-table) th { letter-spacing: 0.02em; }
.table-scroll .table .mono, .table-scroll .table .num, .table-scroll .table .id { font-size: var(--t-micro); }
.table-scroll .table .pos { font-size: 11px; }
/* Pills in the compact table read as words, not eyebrows: the same tones,
   without the uppercase and the tracking that cost a column its width. */
.table-scroll .table .pill { font-size: 12px; padding: 1px 5px; }
.table-scroll .table .wrap-gap { gap: 4px; }
.table-foot { display: flex; justify-content: space-between; gap: var(--s-6); padding: var(--s-5) var(--s-2) 0; font-size: var(--t-xs); color: var(--ink-3); }
.table tr.gated td { background: color-mix(in oklch, var(--red) 4%, transparent); }
/* A session boundary (M4-0338): one cell across the table, not a point. */
.table tr.session-break td {
  padding: 5px 8px; white-space: normal;
  font-size: var(--t-xs); color: var(--ink-2);
  background: var(--bg-sunk);
  border-top: 1px dashed var(--line-strong); border-bottom: 1px dashed var(--line-strong);
}
.table tbody tr.session-break:hover { background: none; }
.table tr.session-break .mono { font-size: var(--t-micro); }

/* A segmented choice: the v4 kit's .seg (above). */

/* A merged rail entry's view switch under the page title (D150, M4-0437):
   Locations = Table · Map, Users & devices = Users · Devices. */
.view-switch-row { margin-top: var(--s-4); }
.view-switch .seg-btn { min-width: 72px; }

.activity-controls {
  display: flex; flex-wrap: wrap; align-items: flex-end;
  gap: var(--s-4) var(--s-5);
  margin-bottom: var(--s-6);
}
.activity-ctl { display: flex; flex-direction: column; gap: var(--s-2); min-width: 0; max-width: 100%; }
.activity-ctl .field-label { margin-bottom: 0; }
.activity-custom { display: flex; flex-wrap: wrap; gap: var(--s-4); min-width: 0; max-width: 100%; }
.activity-custom[hidden] { display: none; }
/* M4-0418: the zone beside the inputs, and the pair's one refusal under them. */
.activity-zone { align-self: flex-end; padding-bottom: 10px; }
.activity-custom > .field-error { flex-basis: 100%; margin-top: 0; }
.activity-apply { flex-direction: row; gap: var(--s-3); }
.activity-controls[hidden], .page-head-row > .row[hidden], .page-sub[hidden] { display: none; }
/* The filter toolbar (v2's toolbar card) and its note. */
.activity-controls {
  padding: var(--s-5) var(--s-6);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--bg-elev);
}
.activity-hint { flex-basis: 100%; margin: 0; font-size: var(--t-micro); color: var(--ink-3); }
.page-sub { color: var(--ink-3); font-size: var(--t-sm); margin: var(--s-2) 0 0; }
.activity-footnote { text-align: center; margin-top: var(--s-5); }
.activity-cap { margin-bottom: var(--s-5); }
.activity-cap[hidden] { display: none; }

/* ---------- status ---------- */

/* A pill reads as a word: 12 px sentence case in the body face, not a mono
   eyebrow (M4-0407, G1-44). Each tone is its text colour on its -soft tint. */
.pill {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-family: var(--f-body); font-size: 12px; font-weight: 500;
  letter-spacing: 0; text-transform: none;
  padding: 2px 8px; border-radius: 4px;
  background: color-mix(in oklch, var(--ink) 8%, transparent);
  color: var(--ink-2);
}
.pill-ok { background: var(--green-soft); color: var(--green); }
.pill-warn { background: color-mix(in oklch, var(--amber) 18%, transparent); color: var(--honey-ink); }
.pill-bad { background: var(--red-soft); color: var(--red); }
:root[data-theme="dark"] .pill-warn { color: var(--honey); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .pill-warn { color: var(--honey); } }

.dot { width: 6px; height: 6px; border-radius: 50%; background: var(--green); flex: none; }
.dot-live { animation: pulse 1.8s infinite; }
@keyframes pulse {
  0% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--green) 55%, transparent); }
  70% { box-shadow: 0 0 0 6px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
@media (prefers-reduced-motion: reduce) { .dot-live { animation: none; } }

/* A status: a coloured dot and its words, never colour alone (Design v2's
   v2-status; M4-0277 ports the same piece for the Map's "Data as of"). */
.status { display: inline-flex; align-items: center; gap: 7px; font-size: var(--t-xs); color: var(--ink-2); white-space: nowrap; }
.status::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--ink-4); flex: none; }
.status.ok::before { background: var(--green); }
.status.warn::before { background: var(--amber); }
.status.bad::before { background: var(--red); }
.status.quiet::before { background: transparent; border: 1.5px solid var(--ink-4); width: 6px; height: 6px; }

/* Health chips (Design v2's v2-chip): one fact each. */
.chips { display: flex; gap: 6px; flex-wrap: wrap; }
.chip { font-family: var(--f-mono); font-size: 11px; padding: 2px 7px; border-radius: var(--r-sm); background: var(--bg-sunk); border: 1px solid var(--line); color: var(--ink-2); white-space: nowrap; }
.chip.bad { color: var(--red); border-color: color-mix(in oklch, var(--red) 35%, var(--line)); }

/* ---------- stat tile ---------- */

/* 12 px sentence case, like the Activity table's labels (M4-0407, G1-44). */
.stat-label {
  font-family: var(--f-body); font-size: 12px; font-weight: 500;
  letter-spacing: 0; text-transform: none; color: var(--ink-3);
}
.stat-value {
  font-family: var(--f-display); font-size: 28px; font-weight: 500;
  letter-spacing: -0.025em; line-height: 1.1; margin-top: var(--s-3);
  font-variant-numeric: tabular-nums;
}
.stat-note { font-size: var(--t-micro); color: var(--ink-3); margin-top: var(--s-2); }
/* M4-0420 (G1-24): a day-counting tile says whose day beside its label. */
.stat-head { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.stat-zone { font-size: 11px; color: var(--ink-3); }

/* ---------- empty vs error: deliberately different ---------- */

.empty-title { font-family: var(--f-display); font-size: var(--t-h2); letter-spacing: var(--tr-h2); color: var(--ink); }
.empty-body { margin-top: var(--s-3); font-size: var(--t-sm); max-width: 46ch; margin-left: auto; margin-right: auto; }
.empty-action { margin-top: var(--s-6); }
/* The project-state rule's actions (M4-0415, lib/project-state.js), centred
   under the sentence and wrapping on a narrow box. */
.empty-actions { justify-content: center; flex-wrap: wrap; }

.errorbox {
  display: flex; gap: var(--s-4); align-items: flex-start;
  padding: var(--s-5) var(--s-6);
  border: 1px solid color-mix(in oklch, var(--red) 40%, var(--line));
  border-left: 3px solid var(--red);
  border-radius: var(--r-md);
  background: var(--red-soft);
  color: var(--ink);
  font-size: var(--t-sm);
}
.errorbox-title { font-weight: 500; color: var(--red); }
.errorbox-body { color: var(--ink-2); margin-top: 2px; }
.errorbox-actions { margin-left: auto; display: flex; gap: var(--s-3); flex-wrap: wrap; }

/* An inline refusal (M4-0408, G4-09, States §6): said next to what failed, in
   the dialog, form or panel, never in a toast. Copy error details carries the
   raw text. */
.inline-error {
  display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-4); align-items: center;
  margin-top: var(--s-5); padding: var(--s-4) var(--s-5);
  border: 1px solid color-mix(in oklch, var(--red) 40%, var(--line));
  border-left: 3px solid var(--red); border-radius: var(--r-md);
  background: var(--red-soft); color: var(--ink); font-size: var(--t-sm);
}
.inline-error[hidden] { display: none; }
.inline-error-msg { flex: 1 1 220px; min-width: 0; }
.inline-error-actions { display: flex; gap: var(--s-3); }
.copy-failed { display: inline-block; margin: 0 0 0 var(--s-3); }

/* The read-only banner (D129 States §5): one note at the top of a page a member
   may view but not change. A member's edit controls are hidden, so this is the
   single acknowledgement, not a row of disabled buttons. */
.readonly-banner { margin-bottom: var(--s-5); }

/* A shortened id with a Copy button (D129 States §8). */
.id-chip { display: inline-flex; align-items: center; gap: var(--s-3); min-width: 0; }
.id-chip .id { color: var(--ink-3); }

/* An "unavailable / not measured" state — neither empty nor error. This is the
   ingest-health honesty case: we could not ask, which is not the same as zero. */
.notice {
  padding: var(--s-5) var(--s-6);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-sunk);
  color: var(--ink-3);
  font-size: var(--t-sm);
}

/* ---------- skeleton ---------- */

.skel {
  background: linear-gradient(90deg, var(--skel-a) 25%, var(--skel-b) 37%, var(--skel-a) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.4s infinite linear;
  border-radius: var(--r-sm);
  height: 1em;
}
@keyframes shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }
@media (prefers-reduced-motion: reduce) { .skel { animation: none; } }
.skel-row { height: 44px; border-radius: var(--r-md); margin-bottom: var(--s-3); }

/* Page-shaped skeletons (M4-0443, lib/ui.js): each mirrors the loaded layout it
   stands in for, inside the same card, so nothing moves when the data lands. */
.skel-cell, .skel-line, .skel-dot, .skel-li-end, .skel-stat, .skel-tab, .skel-heading { display: inline-block; vertical-align: middle; }
.skel-cell { height: 0.8em; max-width: 100%; }
.skel-table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.skel-table td { height: 1.45em; }
.skel-list { display: flex; flex-direction: column; }
.skel-li {
  display: grid; grid-template-columns: 10px minmax(0, 1fr) auto; gap: 10px; align-items: center;
  padding: 11px 16px; border-bottom: 1px solid var(--line);
}
.skel-li-plain { grid-template-columns: minmax(0, 1fr) auto; padding-inline: 0; border-bottom: 0; }
.skel-list-boxed { gap: var(--s-3); }
.skel-list-boxed > .skel-li { grid-template-columns: minmax(0, 1fr) auto; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--bg-elev); }
.skel-dot { width: 10px; height: 10px; border-radius: 50%; }
.skel-li-text { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.skel-line { display: block; height: 0.75em; max-width: 100%; }
.skel-line-sub { height: 0.6em; }
.skel-li-end { width: 44px; height: 0.7em; }
.skel-stat { width: 3.2ch; height: 0.8em; min-width: 18px; border-radius: var(--r-sm); }
.skel-stat-lg { display: block; height: 26px; width: 40%; max-width: 140px; margin-top: var(--s-3); }
.skel-chart { width: 100%; height: auto; border-radius: var(--r-md); }
.skel-tiles > .skel-tile { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.skel-fields { display: flex; flex-direction: column; gap: var(--s-4); }
.skel-heading { display: block; height: 1.1em; }
.skel-field-grid { row-gap: var(--s-6); margin-top: var(--s-4); }
.skel-field { display: flex; flex-direction: column; gap: var(--s-3); }
.skel-input { display: block; height: 36px; border-radius: var(--r-md); }
.skel-form { display: flex; align-items: flex-start; gap: 24px; }
.skel-form-index { flex: 0 0 200px; display: flex; flex-direction: column; gap: 2px; }
.skel-form-link { min-height: 36px; padding: 0 10px; display: flex; align-items: center; }
.skel-form-cards { flex: 1 1 auto; min-width: 0; }
.skel-tabs { display: flex; gap: var(--s-7); padding: 12px 4px; border-bottom: 1px solid var(--line); }
.skel-tab { width: 72px; height: 0.8em; }
@media (max-width: 760px) {
  .skel-form { flex-direction: column; align-items: stretch; }
  .skel-form-index { flex: none; flex-direction: row; flex-wrap: wrap; }
}

/* ---------- modal ---------- */

.overlay {
  position: fixed; inset: 0; z-index: 60;
  background: color-mix(in oklch, oklch(15% 0.02 60) 55%, transparent);
  display: flex; align-items: center; justify-content: center;
  padding: var(--s-7);
  backdrop-filter: blur(3px);
}
/* The dialog box itself is the v4 kit's .dialog (above); .modal
   keeps what only a scrolling overlay box needs. */
.modal { width: 100%; max-height: calc(100vh - 2 * var(--s-7)); overflow-y: auto; }
.modal-body { color: var(--ink-2); font-size: var(--t-sm); }

/* ---------- side panels (M4-0413; audit §4.3, §5.4) ----------
   lib/ui.js sidePanel(): Activity's point, Trips' trip, Webhooks' endpoint and
   the Map's device card. Beside the table at 768 px and wider, where each view
   keeps its own layout; below that Activity's, Trips' and Webhooks' are a modal
   bottom sheet in the viewport (G2-03, G3-20, G4-06): the list stays visible
   above it, and the tap that opened it is never answered off-screen. */
.side-panel :is(h1, h2, h3)[tabindex="-1"]:focus { outline: none; }
.side-panel :is(h1, h2, h3)[tabindex="-1"]:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; border-radius: var(--r-sm); }
.overlay.overlay-sheet { align-items: flex-end; justify-content: stretch; padding: var(--s-10) 0 0; }
.overlay-sheet > .side-panel {
  width: 100%; max-width: none; margin: 0;
  max-height: 85vh; max-height: 85dvh; overflow-y: auto; overscroll-behavior: contain;
  border-width: 1px 0 0; border-radius: 18px 18px 0 0;
  padding-bottom: calc(var(--s-6) + env(safe-area-inset-bottom, 0px));
  box-shadow: 0 -6px 24px color-mix(in oklch, var(--ink) 12%, transparent);
}
.overlay-sheet > .side-panel::before { content: ""; display: block; flex: none; width: 36px; height: 4px; border-radius: 2px; background: var(--line-strong); margin: -8px auto 0; }

/* ---------- secret reveal ---------- */

.secret {
  display: flex; align-items: center; gap: var(--s-4);
  font-family: var(--f-mono); font-size: var(--t-sm);
  background: var(--bg-sunk);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  padding: var(--s-4) var(--s-5);
  word-break: break-all;
}
.secret-value { flex: 1; min-width: 0; }
.secret-masked { letter-spacing: 0.08em; color: var(--ink-3); }
.warn-band {
  display: flex; gap: var(--s-4); align-items: flex-start;
  padding: var(--s-5) var(--s-6);
  border: 1px solid color-mix(in oklch, var(--amber) 45%, var(--line));
  border-left: 3px solid var(--amber);
  border-radius: var(--r-md);
  background: color-mix(in oklch, var(--amber) 10%, var(--bg-elev));
  font-size: var(--t-sm);
}

/* ---------- first-data onboarding + SDK setup code (M4-0183) ---------- */

.onboard {
  border: 1px solid var(--line-strong);
  border-radius: var(--r-lg);
  background: var(--bg-elev);
  padding: var(--s-6) var(--s-7);
  margin-bottom: var(--s-7);
}
.onboard > * + * { margin-top: var(--s-4); }
.onboard-head { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s-4); }
.onboard-ok { border-color: color-mix(in oklch, var(--green) 40%, var(--line)); }
.onboard-link { margin-left: auto; }
/* Design v2's onboarding row (Keys.dc.html): a mark, the words, the actions. */
.onboard-row { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: var(--s-6); align-items: center; }
.onboard-title { font-weight: 600; margin-top: 2px; }
.onboard-text { margin-top: 4px; max-width: 70ch; }
.onboard-pulse { width: 38px; height: 38px; border-radius: 50%; border: 2px solid var(--honey); display: grid; place-items: center; }
.onboard-pulse::after { content: ""; width: 12px; height: 12px; border-radius: 50%; background: var(--honey); }
.onboard-pulse-off { border-color: var(--ink-4); }
.onboard-pulse-off::after { background: var(--ink-4); }
.onboard-check { width: 38px; height: 38px; border-radius: 50%; border: 2px solid var(--green); position: relative; }
.onboard-check::after { content: ""; position: absolute; left: 13px; top: 8px; width: 8px; height: 14px; border: solid var(--green); border-width: 0 2px 2px 0; transform: rotate(45deg); }
.onboard-setup[hidden] { display: none; }
@media (max-width: 640px) { .onboard-row { grid-template-columns: minmax(0, 1fr); } .onboard-pulse, .onboard-check { display: none; } }

/* Keys v4 (M4-0481, D173; Keys.dc.html): the key cards beside the tinted
   "Which key do I need?" card, which wraps under them on a narrow screen.
   Every control on the page is the kit's (end of this file); these rules lay
   the page out only. */
.keys-layout { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--s-6); }
.keys-main { flex: 999 1 560px; min-width: 0; display: flex; flex-direction: column; gap: var(--s-6); }
.keys-aside { flex: 1 1 300px; max-width: 360px; min-width: 0; }
.keys-which { display: flex; flex-direction: column; gap: 10px; }
.keys-which p { font-size: 13.5px; line-height: 1.55; color: var(--ink-2); }
.keys-which b { font-weight: 500; color: var(--ink); }
.keys-which .mono { font-size: 12.5px; }
.keys-which-links { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-2); }
.keys-card { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.keys-card-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; }
.keys-card-text { flex: 1 1 260px; min-width: 0; }
.keys-card-text .card-sub { margin-top: 4px; }
/* One suspended notice above both key cards (M4-0415, G4-15). */
.keys-suspended { margin-bottom: var(--s-6); }
.keys-card .footnote { margin: 0; max-width: none; font-size: var(--t-xs); line-height: 1.5; color: var(--ink-3); }
.key-prefix { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); min-width: 0; }
.key-prefix .mono { overflow-wrap: anywhere; }
.keys-card .tbl td { vertical-align: middle; }
.table tbody.keys-revoked[hidden] { display: none; }
.keys-revoked-foot { padding-top: var(--s-2); }
.keys-revoked-chev { width: 0; height: 0; border-left: 5px solid currentColor; border-top: 4px solid transparent; border-bottom: 4px solid transparent; transition: transform var(--dur-fast) var(--ease); }
.keys-revoked-toggle[aria-expanded="true"] .keys-revoked-chev { transform: rotate(90deg); }
.keys-revoked tr td { color: var(--ink-3); }
/* v3 Keys (D133): a revoked key's mark in its Key cell, and the row ⋯ menu,
   the kit's .menu drawn fixed beside its button so no scroll box clips it. */
.key-cell { display: inline-flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
.key-revoked-tag { color: var(--red); white-space: nowrap; }
.row-menu { display: inline-flex; justify-content: flex-end; }
.row-menu-list { position: fixed; z-index: 40; }
.keys-issue { display: flex; flex-direction: column; align-items: flex-end; }
.keys-scopes { font-size: 13.5px; color: var(--ink-2); }
.keys-scopes > summary { cursor: pointer; font-weight: 500; color: var(--ink); padding: 13px 0; }
.keys-scopes > ul { margin: 8px 0 0; padding-left: 18px; list-style: disc; line-height: 1.6; columns: 2 220px; column-gap: 24px; overflow-wrap: anywhere; }
/* M4-0420 (G4-20): Select all read / Clear above the scopes. */
.scope-presets { margin-bottom: var(--s-3); }
.key-name { overflow-wrap: anywhere; }
/* M4-0365: server keys with scopes; the picker is the region picker's pattern (M4-0272). */
fieldset.scope-field { border: 0; padding: 0; margin-inline: 0; min-width: 0; }
fieldset.scope-field > legend { padding: 0; margin-bottom: 8px; }
.scope-desc { margin-bottom: var(--s-3); }
.scope-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-2); }
.scope-option {
  display: flex; align-items: flex-start; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: var(--bg-elev);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.scope-option:hover { border-color: var(--accent-text); }
.scope-option:has(input:checked) { border-color: var(--accent-text); background: var(--bg-sunk); }
.scope-option:has(input:focus-visible) { box-shadow: var(--focus-ring); }
.scope-option input { margin-top: 3px; accent-color: var(--accent-text); flex: none; }
.scope-option > span { display: grid; gap: 2px; min-width: 0; }
.scope-name { font-size: var(--t-sm); color: var(--ink); }
@media (max-width: 640px) { .scope-grid { grid-template-columns: minmax(0, 1fr); } }
/* M4-0257: platform, model and SDK under a device's install id. */
.device-meta { margin-top: 2px; overflow-wrap: anywhere; }

.code-switch { min-width: 0; }
.code-switch > * + * { margin-top: var(--s-3); }
.code-switch-head { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--s-4); }
/* M4-0420 (G4-18): each language's versioned install line. M4-0448: the label,
   the line and its Copy on one row, Copy at the row's end (no wrap: the line
   itself wraps, overflow-wrap: anywhere). */
.code-install { display: flex; align-items: center; gap: var(--s-3); flex-wrap: nowrap; min-width: 0; }
.code-install > .btn { flex: none; margin-left: auto; }
.code-install-line { font-size: 12px; padding: 4px 8px; border-radius: var(--r-sm); background: var(--bg-sunk); border: 1px solid var(--line); overflow-wrap: anywhere; min-width: 0; flex: 1 1 auto; }
.code-install-label { flex: none; }
.lang-tabs {
  display: inline-flex; flex-wrap: wrap; gap: var(--s-2);
  padding: var(--s-2); border-radius: var(--r-md); background: var(--bg-sunk);
}
.lang-tab {
  font: inherit; font-size: var(--t-sm); color: var(--ink-3);
  background: transparent; border: 0; border-radius: var(--r-sm);
  min-height: 32px; padding: var(--s-2) var(--s-4); cursor: pointer;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.lang-tab:hover { color: var(--ink); }
.lang-tab:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.lang-tab-on { background: var(--bg-elev); color: var(--ink); box-shadow: 0 0 0 1px var(--line); }
/* M4-0448: the snippet wraps rather than scrolling sideways. */
.code-block {
  display: block; margin: 0;
  white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.6;
  font-family: var(--f-mono); font-size: var(--t-sm);
  background: var(--bg-sunk);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  padding: var(--s-4) var(--s-5);
}

/* ---------- toast ---------- */

/* M4-0408 (G5-08): bottom-left, as the States board draws it. Two regions,
   made at shell mount: the alert region (errors with no other home) over the
   status region (what worked). */
.toasts {
  position: fixed; bottom: var(--s-7); left: var(--s-7); z-index: 80;
  display: flex; flex-direction: column; gap: var(--s-3);
  max-width: min(380px, calc(100vw - 2 * var(--s-7)));
  pointer-events: none;
}
.toast-region { display: flex; flex-direction: column; gap: var(--s-3); }
.toast-region:empty { display: none; }
/* The toast itself is the v4 kit's (above). */
.toast { pointer-events: auto; }

/* ---------- login ---------- */

.auth-wrap { min-height: 100vh; display: grid; place-items: center; padding: var(--s-7); }
.auth-card { width: 100%; max-width: 380px; }
.auth-brand { display: flex; align-items: center; gap: var(--s-4); margin-bottom: var(--s-8); }
.auth-brand svg { width: 30px; height: 30px; }
/* Why Login is showing, when the console knows (views/login.js). */
.auth-message { margin-top: var(--s-6); }
/* M4-0418 (K11, G1-11): the refused sign-in band above the fields, and the
   forgotten-password line under the form. */
.auth-error { margin: 0 0 var(--s-5); }
.auth-forgot { margin-top: var(--s-6); }
/* Design v2's Login (M4-0272, Login.dc.html): the sign-in pane beside an
   illustrated one, which goes below 900 px. */
.auth-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); place-items: stretch; padding: 0; }
.auth-pane { display: flex; align-items: center; justify-content: center; padding: var(--s-8) var(--s-7); min-width: 0; }
.auth-body { margin-top: var(--s-8); }
.auth-art { position: relative; overflow: hidden; background: var(--bg-sunk); border-left: 1px solid var(--line); min-height: 100vh; }
.auth-art-map { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.auth-art-roads path { stroke: var(--bg-elev); stroke-width: 10; fill: none; }
.auth-art-water { fill: color-mix(in oklch, var(--map-track) 14%, var(--bg-sunk)); }
.auth-art-track { stroke: var(--map-fence); stroke-width: 4; fill: none; stroke-linecap: round; }
.auth-art-halo { fill: var(--map-fence); fill-opacity: 0.16; }
.auth-art-pin { fill: var(--map-fence); stroke: var(--bg-elev); stroke-width: 3; }
.auth-art-fence { fill: var(--map-track); fill-opacity: 0.08; stroke: var(--map-track); stroke-width: 2; stroke-dasharray: 6 5; }
.auth-art-copy { position: absolute; left: 40px; right: 40px; bottom: 40px; max-width: 420px; }
.auth-art-title { font-family: var(--f-display); font-size: 22px; letter-spacing: -0.02em; font-weight: 600; color: var(--ink); }
.auth-art-sub { color: var(--ink-2); margin-top: 6px; }
@media (max-width: 900px) {
  .auth-split { grid-template-columns: minmax(0, 1fr); }
  .auth-art { display: none; }
  .auth-pane { padding: var(--s-7) var(--s-5); }
}
.btn-block { width: 100%; }

/* ---------- map surface ---------- */

.map-wrap {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--bg-sunk);
  min-height: 420px;
}
/* The basemap is still loading (M4-0443): the grid is drawn, under a slow,
   quiet shimmer, rather than an empty flat area or the no-basemap note. */
.map-wrap.is-basemap-loading::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(100deg, transparent 35%, var(--skel-a) 50%, transparent 65%);
  background-size: 300% 100%;
  animation: shimmer 2.4s infinite linear;
}
@media (prefers-reduced-motion: reduce) { .map-wrap.is-basemap-loading::after { animation: none; background: var(--skel-a); opacity: 0.5; } }
/* The Map's page before the project is read (tab-map.js mapSkeleton). */
.map-bar .skel-tab { height: 30px; width: 140px; }
.map-panel .skel-heading { width: 60%; }
.map-panel-head > .skel + .skel { margin-top: 10px; }
.map-bar .skel-tabs { padding: 0; border: 0; }
/* The fence overlay only: an icon placed in the map keeps its own size (0208). */
.map-wrap svg.fence-canvas { display: block; width: 100%; height: 100%; position: absolute; inset: 0; }
.map-note {
  position: absolute; left: var(--s-4); bottom: var(--s-4);
  font-family: var(--f-mono); font-size: var(--t-eyebrow);
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-3);
  background: color-mix(in oklch, var(--bg-elev) 85%, transparent);
  padding: 3px 8px; border-radius: 4px;
}
.map-attr {
  position: absolute; right: var(--s-4); bottom: var(--s-4);
  font-size: var(--t-eyebrow); color: var(--ink-3);
  background: color-mix(in oklch, var(--bg-elev) 85%, transparent);
  padding: 2px 7px; border-radius: 4px;
}
/* ---------- Geofences: Console v4 (M4-0478, D173; Geofences.dc.html) ----------
   Page layout only: the controls are the kit's (.btn, .card, .card-tinted,
   .field, .check, .search, .seg, .empty, .pager, 0484). The map first, the
   fence panel beside it at 300-360 px, under it below 1024 px. */
.fence-editor {
  --fence-map-h: clamp(480px, calc(100dvh - 275px), 1100px);
  display: grid; grid-template-columns: minmax(0, 1fr) clamp(300px, 26vw, 360px);
  gap: var(--s-6); align-items: start;
}
.fence-stage { position: relative; min-width: 0; }
.fence-stage .map-wrap { height: var(--fence-map-h); min-height: 0; border-radius: var(--r-xl); }
/* The panel scrolls inside the map's height, so revealing the card never
   moves the map from under the pointer. */
.fence-side {
  display: flex; flex-direction: column; gap: var(--s-6); min-width: 0;
  max-height: var(--fence-map-h); overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin;
}
.fence-side > * { flex: none; }
/* The fence form's latitude / longitude pair stacks on a panel under 360 px. */
.fence-side { container-type: inline-size; }
.fence-notes[hidden], .fence-card-host[hidden] { display: none; }

/* The list card: "N fences" and the search on one line, the entries, the
   count line with Show more. */
.fence-list-card { display: flex; flex-direction: column; gap: var(--s-3); padding: var(--s-6) var(--s-7); }
.fence-list-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); min-height: 40px; }
.fence-search { display: flex; flex: 0 1 180px; min-width: 0; }
.fence-search[hidden] { display: none; }
.fence-search .search { max-width: none; flex: 1 1 auto; }
.fence-entries { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-2); }
.fence-entry {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  width: 100%; min-height: 44px; padding: var(--s-4) var(--s-5); box-sizing: border-box;
  border: 1px solid transparent; border-radius: var(--r-lg); background: transparent;
  font: inherit; color: var(--ink); text-align: left; cursor: pointer;
}
.fence-entry:hover { background: var(--bg-sunk); }
.fence-entry:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.fence-entry[aria-current="true"] { background: color-mix(in oklch, var(--honey-soft) 60%, var(--bg-elev)); border-color: color-mix(in oklch, var(--honey) 35%, var(--line)); }
/* Card <-> shape: the lit pair (G3-29), driven by hoverId, not :hover. */
.fence-entry-lit { border-color: var(--honey); background: color-mix(in oklch, var(--honey) 10%, var(--bg-elev)); }
.fence-entry-text { display: flex; flex-direction: column; min-width: 0; }
.fence-entry-name { display: block; font-size: var(--t-body); font-weight: 500; }
.fence-entry-sub { display: block; font-size: var(--t-xs); color: var(--ink-3); }
.fence-entry-chips { display: flex; flex-wrap: wrap; gap: 4px; flex: none; }
.fence-pager[hidden] { display: none; }
.fence-pager > .pager-count { display: block; padding-top: var(--s-3); font-size: var(--t-sm); color: var(--ink-3); }

/* The open fence's card. */
.fence-form { display: flex; flex-direction: column; gap: var(--s-6); scroll-margin-top: var(--s-5); scroll-margin-bottom: var(--s-5); }
.fence-form-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s-4); }
.fence-form-titles { display: flex; flex-direction: column; gap: var(--s-1); min-width: 0; }
.fence-eyebrow { font-size: var(--t-xs); color: var(--ink-3); }
.fence-editor-title { margin: 0; min-width: 0; }
.fence-head-id { display: flex; margin-top: var(--s-1); }
/* The centre: latitude and longitude (kit input groups, ° inside) side by
   side, one description line under both (0478 follow-up). */
.fence-centre { display: flex; flex-direction: column; gap: var(--s-3); }
.fence-coords { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s-6) var(--s-4); }
@container (max-width: 359.98px) { .fence-coords { grid-template-columns: minmax(0, 1fr); } }
/* The notify switches, their one line, and the flags-vs-events disclosure. */
.fence-notify { display: flex; flex-direction: column; gap: var(--s-3); min-width: 0; }
/* list-item keeps the native disclosure triangle; padding makes the 40 px row. */
.fence-explain > summary {
  display: list-item; min-height: 40px; padding: var(--s-4) 0; box-sizing: border-box; width: fit-content; max-width: 100%;
  border-radius: var(--r-md); font-size: var(--t-sm); font-weight: 500; color: var(--ink-2); cursor: pointer;
}
.fence-explain > summary:hover { color: var(--ink); }
.fence-explain > summary:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.fence-explain[open] > summary { margin-bottom: var(--s-1); }
.fence-form .desc { margin: 0; }
/* M4-0418 (G3-35): the radius's under-100 m warning line. */
.field-warn { font-size: var(--t-micro); color: var(--ink-2); }
.field-warn[hidden] { display: none; }
/* The actions stay in view while the card is taller than the panel (M4-0383),
   with the line that says what is unsaved over them. */
.fence-form-foot {
  position: sticky; bottom: 0; z-index: 1;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--s-3);
  margin: 0 calc(-1 * var(--s-7)); padding: var(--s-5) var(--s-7);
  border-top: 1px solid var(--line); background: var(--bg-elev);
}
.fence-dirty { flex: 1 1 100%; font-size: var(--t-xs); color: var(--ink-3); }
.fence-delete {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-4);
  border-top: 1px solid var(--line); padding-top: var(--s-6);
}
.fence-delete > .desc { flex: 1 1 180px; }
@media (max-width: 767.98px) { .fence-explain > summary { min-height: 44px; } }

/* Where the events go. */
.fence-about { display: flex; flex-direction: column; gap: var(--s-3); }
.fence-about .card-sub { margin: 0; }
.fence-about-links { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3) var(--s-6); }

/* The map's floating controls: the tools top-left, zoom top-right, the hint
   bottom-left, the scale bottom-right (drawn in the overlay). */
.map-toolbar {
  position: absolute; z-index: 2; left: var(--s-6); top: var(--s-6);
  display: flex; gap: 4px; padding: 4px;
  border: 1px solid var(--line); border-radius: var(--r-pill);
  background: color-mix(in oklch, var(--bg-elev) 95%, transparent);
}
.fence-zoom { position: absolute; z-index: 2; right: var(--s-6); top: var(--s-6); display: flex; flex-direction: column; gap: var(--s-2); }
.fence-zoom .btn { font-size: 20px; font-weight: 400; }
.fence-hint {
  position: absolute; z-index: 2; left: var(--s-6); bottom: var(--s-6); max-width: calc(100% - 220px);
  font-size: var(--t-xs); line-height: 1.45; color: var(--ink-2);
  background: color-mix(in oklch, var(--bg-elev) 95%, transparent);
  border: 1px solid var(--line); border-radius: var(--r-lg); padding: var(--s-4) var(--s-5);
}
.fence-hint[hidden] { display: none; }
/* On a basemap the hint sits above Mapbox's logo, which its terms require to
   stay visible (M4-0443). */
.map-wrap:has(.mapboxgl-ctrl-logo) .fence-hint { bottom: calc(var(--s-6) + 32px); }
/* The no-basemap note sits under the tools, clear of the hint. */
.fence-stage .map-note { top: calc(var(--s-6) + 60px); bottom: auto; left: var(--s-6); max-width: calc(100% - 120px); }
.fence-measure {
  position: absolute; z-index: 2; pointer-events: none;
  font-size: var(--t-xs); color: var(--accent-text);
  background: var(--bg-elev); border-radius: var(--r-pill);
  padding: 3px 10px; white-space: nowrap;
}
.fence-measure[hidden] { display: none; }
.map-wrap svg.fence-canvas[data-tool="circle"], .map-wrap svg.fence-canvas[data-tool="polygon"] { cursor: crosshair; }
.map-wrap svg.fence-canvas[data-tool="select"] { cursor: grab; }
.map-wrap svg.fence-canvas.panning { cursor: grabbing; }
/* On a basemap with Select armed, the empty map is Mapbox's; shapes stay live. */
.map-wrap svg.fence-canvas.passthrough { pointer-events: none; }
.map-wrap svg.fence-canvas.passthrough [data-fence], .map-wrap svg.fence-canvas.passthrough [data-handle] { pointer-events: auto; }

/* A fence's name drawn on the map (G3-29). --ink or better for contrast (axe
   stays green, 0407's AxeContrast); a halo keeps it legible over the honey
   fills and the grid. The overlay is aria-hidden, so this is decoration. */
.fence-name {
  font-family: var(--f-body); font-size: var(--t-sm); font-weight: 500;
  fill: var(--ink); paint-order: stroke;
  stroke: var(--bg); stroke-width: 3px; stroke-linejoin: round;
}
.fence-name-lit { fill: var(--honey-ink, var(--ink)); font-weight: 600; }
/* The minimum-size mark for a fence too small to see at the current zoom. */
.fence-mark { filter: drop-shadow(0 1px 2px color-mix(in oklch, oklch(20% 0 0) 40%, transparent)); }

/* G3-38: when the grid is centred on the project's region the note is a
   sentence, not the uppercase label the no-basemap note uses — it reads as
   guidance ("check coordinates before saving"), and its case must survive to
   the accessible text. A touch wider than the plain note to fit the sentence. */
.map-note.map-note-region {
  text-transform: none; letter-spacing: normal;
  font-family: var(--f-body); font-size: var(--t-xs);
  max-width: calc(100% - 2 * var(--s-4));
}

/* ---------- charts ---------- */

/* Axis labels are read: 11 px at least, in --ink-3 (M4-0407, G5-28, G3-03). */
.chart-label { font-family: var(--f-mono); font-size: var(--t-eyebrow); fill: var(--ink-3); letter-spacing: 0.04em; }
.spark { width: 72px; height: 20px; display: block; }
.spark path { fill: none; stroke: var(--honey); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }

/* The Usage dashboard (M4-0294). Its series slots, --series-1/--series-2, are
   tokens.css's (M4-0407). Failures use the reserved --red, always with a word
   beside it. */

/* M4-0477 (D173, Usage.dc.html): two columns — the daily figures in the main
   column, the tracked-users card and its definition in an aside — under one
   column on a narrow window. */
.usage { display: grid; grid-template-columns: minmax(0, 1fr) minmax(280px, 340px); gap: var(--s-6); align-items: start; }
.usage-main, .usage-aside { display: grid; gap: var(--s-6); min-width: 0; align-content: start; }
/* The page spaces its cards by grid gap, not the global section + section margin. */
.usage section + section { margin-top: 0; }
@media (max-width: 1100px) { .usage { grid-template-columns: minmax(0, 1fr); } }
.usage-filters { display: flex; flex-wrap: wrap; gap: var(--s-4); align-items: center; }
.usage-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-5); }
.usage-asof { margin: 0; font-size: var(--t-sm); color: var(--ink-3); }
.usage-asof[hidden] { display: none; }
.usage-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--s-4); }
.usage-stat { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.usage-stat-label { font-size: var(--t-body); color: var(--ink-2); }
.usage-stat-sub { font-size: var(--t-sm); line-height: 1.5; color: var(--ink-3); }
.usage-stat-value.usage-stat-none { font-size: 28px; color: var(--ink-2); }
.usage-card { display: flex; flex-direction: column; gap: var(--s-2); }
.usage-cap { display: flex; flex-direction: column; gap: var(--s-4); }
.usage-cap .card-title, .usage-counts .card-title { margin: 0; }
.usage-cap-value.usage-cap-none { font-size: 24px; color: var(--ink-2); }
.usage-cap-of { color: var(--ink-2); }
.usage-cap-form { display: flex; flex-direction: column; gap: var(--s-4); padding-top: 14px; border-top: 1px solid color-mix(in oklch, var(--honey) 30%, transparent); }
.usage-cap-line { margin: 0; font-size: var(--t-sm); line-height: 1.5; color: var(--ink-2); }
.usage-cap-rules { margin: 0; font-size: var(--t-xs); line-height: 1.5; color: var(--ink-2); }
.usage-cap-rules code, .usage-counts code { font-family: var(--f-mono); font-size: var(--t-xs); }
.usage-counts { display: flex; flex-direction: column; gap: var(--s-3); }
.usage-counts .card-sub { line-height: 1.55; }
.usage-grid { display: grid; gap: var(--s-6); }
.usage-cap-skel { display: flex; flex-direction: column; gap: var(--s-4); margin-top: var(--s-4); }
.usage-cap-skel > .skel-stat-lg { margin-top: 0; height: 32px; }
/* M4-0417: Table view swaps the chart grid for the one table; [hidden] must win over display. */
.usage-grid[hidden], .usage-table-card[hidden] { display: none; }
/* Wholly empty (M4-0415): the one empty state stands in for the chart cards. */
.usage-whole:empty { display: none; }
.usage-view { display: grid; gap: var(--s-6); }
.usage-card-head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--s-5); margin-bottom: var(--s-4); }
.usage-card-summary { color: var(--ink-2); margin-bottom: var(--s-3); }
.usage-card-summary:empty { display: none; }
.usage-cap-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-3); margin: 0 0 var(--s-3); }
.usage-cap-note { margin-top: var(--s-3); }
.usage-cap-drops { margin: var(--s-2) 0 0; font-size: var(--t-sm); color: var(--ink-2); }
.usage-foot { margin: 0; font-size: var(--t-xs); color: var(--ink-3); }
.usage-table { margin-top: var(--s-2); }
/* M4-0417 (G3-05): one day × metric table; its header stays while the page scrolls. */
.usage-table thead th { position: sticky; top: 0; z-index: 1; }
.usage-table tbody th { font-weight: 400; text-align: left; white-space: nowrap; }
.usage-table-lead th { font-style: italic; }
.usage-cap-month { margin: 0; font-size: var(--t-sm); color: var(--ink-2); }
.usage-cap-asof { margin: 0 0 var(--s-3); font-size: var(--t-sm); color: var(--ink-2); }
.usage-cap-provisional { font-weight: 600; color: var(--ink-2); }
.usage-cap-prev { margin: 0; font-size: var(--t-sm); color: var(--ink-2); }
.usage-cap-prev:empty { display: none; }
/* M4-0383 (the v3 Usage board): why points were filtered, beside the key table. */
.usage-pair { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); gap: var(--s-6); align-items: start; }
.usage-why { display: flex; flex-direction: column; gap: var(--s-5); }
.why-list { display: flex; flex-direction: column; gap: var(--s-5); }
.why-row { display: flex; flex-direction: column; gap: 6px; }
.why-head { display: flex; justify-content: space-between; gap: var(--s-4); font-size: var(--t-sm); }
.why-value { color: var(--ink-2); }
.why-track { height: 10px; border-radius: 4px; background: var(--bg-sunk); overflow: hidden; }
.why-fill { height: 100%; background: var(--series-2); border-radius: 0 4px 4px 0; }
.usage-why-link { align-self: flex-start; }
/* M4-0477: beside the aside the main column is too narrow for the pair until
   a wide window (M4-0445: never let the key table scroll the page). */
@media (max-width: 1680px) { .usage-pair { grid-template-columns: minmax(0, 1fr); } }

.meter { height: 8px; border-radius: var(--r-pill); background: var(--bg-sunk); overflow: hidden; border: 1px solid var(--line); }
.meter-fill { height: 100%; background: var(--honey); border-radius: var(--r-pill); }
.meter-fill.warn { background: var(--amber); }
.meter-fill.over { background: var(--red); }

/* ---------- misc layout ---------- */

.stack > * + * { margin-top: var(--s-5); }
.stack-lg > * + * { margin-top: var(--s-7); }
.row { display: flex; align-items: center; gap: var(--s-4); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--s-5); }
.spacer { flex: 1; }
.wrap-gap { display: flex; flex-wrap: wrap; gap: var(--s-3); }

/* ---------- responsive ---------- */

@media (max-width: 1200px) {
  .cols-4 { grid-template-columns: 1fr 1fr; }
  .cols-3 { grid-template-columns: 1fr 1fr; }
}

/* Below 1024 px the fence panel goes under the map (the map stays first, as
   the DOM has it), and the panel no longer scrolls on its own. */
@media (max-width: 1023.98px) {
  .fence-editor { grid-template-columns: minmax(0, 1fr); }
  .fence-side { max-height: none; overflow: visible; }
}

/* A phone (G3-39, Q2/D116): the map first, then the list and the card, and the
   map's overlays in clear, non-overlapping bands: the hint and the no-basemap
   note stack bottom-left, clear of the scale bar at the bottom-right. */
@media (max-width: 767.98px) {
  .fence-editor { --fence-map-h: 420px; }
  .fence-stage .map-wrap { border-radius: var(--r-lg); }
  .map-toolbar { left: var(--s-4); top: var(--s-4); }
  .fence-zoom { right: var(--s-4); top: var(--s-4); }
  .fence-hint {
    left: var(--s-4); right: auto; bottom: calc(var(--s-4) + 64px);
    max-width: calc(100% - 160px); padding: var(--s-2) var(--s-4);
  }
  .map-wrap:has(.mapboxgl-ctrl-logo) .fence-hint { bottom: calc(var(--s-4) + 64px); }
  .fence-stage .map-note { top: auto; left: var(--s-4); right: auto; bottom: var(--s-4); max-width: calc(100% - 160px); }
}

@media (max-width: 767.98px) {
  /* minmax(0, 1fr), not a bare 1fr: a bare 1fr track is sized by its content's
     min-content, so a wide page body (the device page's Points table in its
     .table-scroll) would stretch the one column past the viewport and push the
     topbar sideways. minmax(0, …) lets the column shrink to the viewport and
     the body's own wrap/scroll take the overflow (M4-0381, Contract-change:
     D131 — the v3 device page is the first phone body wide enough to show it). */
  .shell { grid-template-columns: minmax(0, 1fr); }

  /* Console v4 on a phone (D173): no frame, and the rail is one top row of
     icons, scrolling sideways inside itself if it must — the only navigation,
     with no drawer and no second copy of it. The labels, the group names and
     the toggle stay hidden whatever the rail's stored state. */
  :root { --shell-pad: 0px; --frame-y: 0px; }
  #beekon-console-root:has(> .shell) { padding: 0; }
  .shell { border: 0; border-radius: 0; box-shadow: none; overflow: visible; min-height: 100vh; min-height: 100dvh; }
  .rail {
    position: static; width: auto; height: auto;
    flex-direction: row; align-items: center; gap: 4px;
    padding: 4px var(--s-3); border-bottom: 1px solid var(--line);
    overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
    transition: none;
  }
  .rail[data-open="true"] { width: auto; }
  .rail :is(.rail-brand, .nav-item, .rail-docs) { width: var(--tile); justify-content: center; padding: 0; }
  .rail .rail-brand { margin: 0; }
  .rail .rail-toggle, .rail .rail-spacer { display: none; }
  .rail[data-open="true"] :is(.nav-label, .rail-toggle-label, .rail-docs-label, .rail-brand-name) { display: none; }
  .rail[data-open="true"] .rail-brand { width: var(--tile); background: var(--bg-elev); border-color: var(--line); padding: 0; }
  .rail .nav-group { flex-direction: row; align-items: center; gap: 4px; }
  .rail .rail-group-label, .rail[data-open="true"] .rail-group-label { display: none; }
  .rail .rail-divider, .rail[data-open="true"] .rail-divider { display: block; width: 1px; height: 22px; margin: 0 4px; }

  .topbar { padding: 0 var(--s-6); gap: var(--s-4); }
  .topbar > .spacer { display: none; }
  /* G1-34: the project chip names the project on a phone; the breadcrumb's
     page name is the page's own title just below, so the bar drops it. */
  .topbar > .crumbs { display: none; }
  .topbar > .switcher { flex: 1 1 auto; }
  .switcher-btn { max-width: 100%; }
  .topbar-right { margin-left: auto; gap: var(--s-3); }
  .topbar .org-switcher { max-width: 120px; }
  .content { padding: var(--s-7) var(--s-6) var(--s-9); }
  .cols-2, .cols-3, .cols-4 { grid-template-columns: 1fr; }
  .field-row { grid-template-columns: 1fr; }
  .page-head-row { align-items: flex-start; }

  /* Tables reflow to stacked cards — the page body must never scroll sideways. */
  .table, .table tbody, .table tr, .table td { display: block; width: 100%; }
  .table thead { display: none; }
  .table tr { padding: var(--s-5) var(--s-6); border-bottom: 1px solid var(--line); }
  .table tr:last-child { border-bottom: none; }
  .table td { border: none; padding: var(--s-2) 0; text-align: left; }
  .table td::before {
    content: attr(data-label);
    display: block;
    font-family: var(--f-mono); font-size: var(--t-eyebrow);
    letter-spacing: var(--tr-eyebrow); text-transform: uppercase;
    color: var(--ink-3); margin-bottom: 2px;
  }
  .table td:empty { display: none; }
  .table .cell-actions, .table .cell-num { text-align: left; }
  .table .cell-actions { padding-top: var(--s-4); }
  /* The Activity table keeps its columns and scrolls inside its own wrap. */
  .table-scroll .table { display: table; width: 100%; }
  .table-scroll .table thead { display: table-header-group; }
  .table-scroll .table tbody { display: table-row-group; width: auto; }
  .table-scroll .table tr { display: table-row; width: auto; padding: 0; }
  .table-scroll .table td { display: table-cell; width: auto; padding: 9px 8px; border-bottom: 1px solid var(--line); }
  .table-scroll .table td::before { content: none; }
  .table-scroll .table .cell-num { text-align: right; }
  .activity-controls > .spacer { display: none; }

  .toasts { left: var(--s-5); right: var(--s-5); bottom: var(--s-5); max-width: none; }
}

/* ---------- map view (M4-0182) ---------- */

/* The map's paint colours (--map-*) and the chart series slots are tokens.css's
   since M4-0407 (audit G5-33): sRGB there, because mapbox-gl cannot parse oklch(). */

/* Design v2's Map (M4-0277): the map is the page under the topbar, edge to
   edge, and its chrome floats over it (Map.dc.html; the kit's v2-float,
   v2-map-bar, v2-map-panel, v2-card, v2-zoom, v2-fresh, v2-legend). */
.content-bleed { padding: 0; }
/* The window less the top bar and the v4 frame round the shell (--frame-y;
   0 on a phone, where the frame drops away). */
.map-stage { position: relative; height: calc(100vh - var(--topbar-h) - var(--frame-y)); height: calc(100dvh - var(--topbar-h) - var(--frame-y)); min-height: 520px; overflow: hidden; }
.map-stage > .map-wrap { position: absolute; inset: 0; border: 0; border-radius: 0; min-height: 0; }
.map-float {
  position: absolute; z-index: 2;
  background: var(--bg-elev); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: 0 6px 24px color-mix(in oklch, var(--ink) 10%, transparent);
}
.map-bar { top: 16px; left: 16px; right: 16px; display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-5); padding: 8px 10px; }
.map-bar .seg { flex-wrap: wrap; max-width: 100%; }
.map-bar .spacer { flex: 1; }
/* Custom…'s From / To pair, a row of its own under the Time window group (M4-0416, G2-16). */
.map-custom { flex-basis: 100%; display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-5); min-width: 0; }
.map-custom[hidden] { display: none; }
.map-custom-ctl { display: inline-flex; align-items: center; gap: var(--s-2); min-width: 0; max-width: 100%; }
.map-custom-ctl .field-label { margin-bottom: 0; }
/* --map-bar-b is the bar's real bottom edge plus the gap (tab-map.js
   syncBarEdge, M4-0443): the bar wraps to two rows when Custom… shows From /
   To or the window is narrow, and nothing under it may cover it. */
.map-panel { top: var(--map-bar-b, 76px); left: 16px; bottom: 60px; width: 288px; display: flex; flex-direction: column; overflow: hidden; }
.map-panel-head { padding: 14px 16px 10px; border-bottom: 1px solid var(--line); }
.map-panel-head .card-title { margin: 0; }
/* Locations v4 (M4-0474, D173): the panel is the kit's card, "On the map";
   the layers are kit chips in a row; the list ends with its end state. */
.map-panel.card { padding: 0; }
.map-layers { display: flex; flex-wrap: wrap; gap: 6px; }
.map-end { margin: 0 0 10px; }
.map-end:empty { display: none; }
.map-panel-foot > p { margin: 8px 0 0; }
.map-fences { margin-top: 12px; }
.map-fences[hidden] { display: none; }
.map-fences .map-fence-card { display: flex; flex-direction: column; gap: 4px; padding: 12px 14px; color: inherit; text-decoration: none; }
.map-fences .map-fence-card .micro { color: var(--accent-text); }
.map-fence-names { font-weight: 500; overflow-wrap: anywhere; }
.map-count { margin-top: 3px; }
.map-search { margin-top: 10px; padding-top: 6px; padding-bottom: 6px; font-size: var(--t-sm); width: 100%; }
.map-panel-filter { padding: 8px 16px; border-bottom: 1px solid var(--line); }
.map-panel-filter[hidden] { display: none; }
.map-cap { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-3); padding: 10px 16px; border-bottom: 1px solid var(--line); }
.map-cap[hidden] { display: none; }
.map-dev-list { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow-y: auto; }
.map-dev {
  display: grid; grid-template-columns: 10px minmax(0, 1fr) auto; gap: 10px; align-items: center;
  padding: 11px 16px; border: 0; border-bottom: 1px solid var(--line); background: transparent;
  color: var(--ink); font: inherit; text-align: left; cursor: pointer; min-width: 0;
}
.map-dev:hover { background: color-mix(in oklch, var(--ink) 3%, transparent); }
.map-dev[aria-current="true"] { background: color-mix(in oklch, var(--honey) 12%, transparent); }
.map-dev-id { display: flex; flex-direction: column; min-width: 0; overflow-wrap: anywhere; }
.map-swatch { width: 10px; height: 10px; border-radius: 50%; }
.map-none { padding: 11px 16px; margin: 0; overflow-wrap: anywhere; }
.map-panel-foot { padding: 12px 16px; border-top: 1px solid var(--line); }
.map-card { top: var(--map-bar-b, 76px); right: 16px; width: 300px; padding: 16px; max-height: calc(100% - var(--map-bar-b, 76px) - 168px); overflow-y: auto; min-width: 0; }
.map-card.is-empty { visibility: hidden; }
.map-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.map-card-id { font-size: 12.5px; font-weight: 400; margin: 0; padding-top: 6px; min-width: 0; overflow-wrap: anywhere; }
.map-card-head .btn-icon { flex: none; margin: -4px -6px 0 0; }
.map-card-filtered { color: var(--red); margin-top: var(--s-2); }
.kv { display: grid; grid-template-columns: auto 1fr; gap: 6px 14px; font-size: var(--t-xs); margin-top: 12px; }
.kv dt { color: var(--ink-3); }
.kv dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
/* The no-basemap note and the empty state sit between the panel and the card. */
.map-basemap-note { top: var(--map-bar-b, 76px); left: 332px; right: 332px; margin-inline: auto; width: fit-content; max-width: 560px; padding: 12px 16px; display: flex; gap: 12px; align-items: flex-start; }
.map-basemap-note > svg { color: var(--ink-3); flex: none; margin-top: 1px; }
/* Its Dismiss (M4-0416, G2-42): icon-only, so the note's words are unchanged. */
.map-basemap-note .map-note-close { flex: none; margin: -6px -8px -6px 0; min-width: 28px; min-height: 28px; padding: 6px; }
.map-basemap-note .map-note-close svg { width: 14px; height: 14px; }
/* M4-0415 (G2-20): the empty state is centred in the part of the map the chrome
   leaves clear (tab-map.js placeEmpty sets left/top), never under the freshness
   chip; it carries the project-state rule's box, drawn compact over the map. */
.map-empty { left: 50%; top: 50%; transform: translate(-50%, -50%); width: max-content; max-width: min(420px, calc(100% - 32px)); padding: 0; font-size: var(--t-sm); }
.map-empty[hidden] { display: none; }
.map-empty .empty { border: 0; background: transparent; padding: var(--s-5) var(--s-6); }
.map-empty .empty-title { font-size: var(--t-h3); }
.map-empty .empty-action { margin-top: var(--s-4); }
/* The zoom: three kit icon buttons in a column (Map.dc.html, M4-0474), no box of its own. */
.map-zoom { right: 16px; bottom: 84px; display: flex; flex-direction: column; gap: 6px; background: none; border: 0; box-shadow: none; }
.map-zoom > .btn { font-size: 20px; font-weight: 400; }
/* The grid drags to pan (M4-0416, G2-14 / G3-17). */
.map-view .map-wrap svg.fence-canvas, .trip-map svg.fence-canvas { cursor: grab; }
.map-view .map-wrap svg.fence-canvas.panning, .trip-map svg.fence-canvas.panning { cursor: grabbing; }
.map-fresh { left: 16px; bottom: 16px; padding: 7px 12px; font-size: var(--t-micro); color: var(--ink-2); display: flex; gap: 10px; align-items: center; border-radius: var(--r-pill); max-width: calc(100% - 32px - 400px); }
.map-fresh:has(.map-tiles-off) { flex-wrap: wrap; row-gap: 6px; border-radius: var(--r-lg); }
.map-fresh .map-tiles-off { flex-basis: 100%; }
/* The two-line chip is taller: the panel above it gives way. */
.map-stage:has(.map-tiles-off) > .map-panel { bottom: 84px; }
.map-legend { right: 16px; bottom: 16px; margin: 0; padding: 9px 14px; list-style: none; font-size: var(--t-micro); color: var(--ink-2); display: flex; gap: 14px; align-items: center; border-radius: var(--r-pill); }
.map-legend li { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
/* v4's legend words are longer (Map.dc.html, M4-0474): it and the stamp each
   keep to their half of the map's foot, wrapping rather than meeting. */
.map-stage > .map-legend { max-width: calc(50% - 24px); flex-wrap: wrap; row-gap: 6px; border-radius: var(--r-lg); }
.map-stage > .map-fresh { max-width: calc(50% - 24px); }
/* On a basemap Mapbox's own logo and attribution stay visible above the
   floating chips; the view's duplicate attribution line is not needed here. */
.map-stage .map-attr { display: none; }
.map-stage .mapboxgl-ctrl-bottom-left, .map-stage .mapboxgl-ctrl-bottom-right { bottom: 50px; }
.map-stage .mapboxgl-ctrl-bottom-left { left: 312px; }
/* A phone stacks the chrome under the map instead of over it. */
@media (max-width: 900px) {
  .map-stage { height: auto; min-height: 0; overflow: visible; display: flex; flex-direction: column; gap: var(--s-4); padding: var(--s-5); }
  .map-stage > .map-wrap { position: relative; inset: auto; height: 420px; border: 1px solid var(--line); border-radius: var(--r-lg); order: 2; }
  .map-stage > .map-float { position: static; width: auto; max-width: none; max-height: none; box-shadow: none; }
  .map-stage > .map-bar { order: 1; }
  .map-stage > .map-fresh { order: 3; border-radius: var(--r-lg); flex-wrap: wrap; }
  .map-stage > .map-legend { order: 4; flex-wrap: wrap; border-radius: var(--r-lg); }
  .map-stage > .map-card { order: 5; }
  .map-stage > .map-card.is-empty { display: none; }
  .map-stage > .map-panel { order: 6; }
  .map-basemap-note { left: var(--s-4); right: var(--s-4); }
  .map-basemap-note { top: var(--s-4); }
  .map-zoom { bottom: var(--s-4); }
  .map-stage .mapboxgl-ctrl-bottom-left, .map-stage .mapboxgl-ctrl-bottom-right { bottom: 0; }
  .map-stage .mapboxgl-ctrl-bottom-left { left: 0; }
}

/* ---------- one device's page (M4-0293) ---------- */

/* The install id is the title: device-made and up to 128 bytes, so it breaks
   anywhere rather than widening the page. */
.device-page .device-title { min-width: 0; flex: 1 1 320px; }
.device-page .device-id { font-size: clamp(18px, 2.4vw, 26px); overflow-wrap: anywhere; word-break: break-word; }
.device-page .device-links { flex-wrap: wrap; }
/* The head's actions (Open on map · Upload queued points now · Request logs)
   wrap rather than running past a narrow viewport (M4-0381; the three buttons
   are ~450 px in a row, wider than a 390 px phone). */
.device-page .device-actions { flex-wrap: wrap; }
.device-tiles { margin-bottom: var(--s-8); }
.device-tiles .stat-value { overflow-wrap: anywhere; }
.device-card .set-aside { margin-bottom: var(--s-4); }
.device-grid { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: var(--s-7); margin-bottom: var(--s-7); }
.device-grid > section.device-card + section.device-card { margin-top: 0; }
.device-page > div > section.device-card { margin-top: var(--s-7); }
.device-card { min-width: 0; }
.device-card .card-head { flex-wrap: wrap; }

/* Last 24 hours: the track drawn from the points (no basemap). */
.minimap-wrap { background: var(--bg-sunk); border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
.minimap { display: block; width: 100%; height: auto; max-height: 300px; }
.minimap .mm-track { fill: none; stroke: var(--ink-3); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }
.minimap .mm-point { fill: var(--ink-2); }
.minimap .mm-gated { fill: none; stroke: var(--red); stroke-width: 1.5; }
.minimap .mm-halo { fill: color-mix(in oklch, var(--honey) 22%, transparent); stroke: color-mix(in oklch, var(--honey) 60%, transparent); stroke-width: 1; }
.minimap .mm-newest { fill: var(--honey); stroke: var(--bg-elev); stroke-width: 2; }
.minimap-legend { display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-6); margin-top: var(--s-4); }
.minimap-legend > span { display: inline-flex; align-items: center; gap: 6px; }
.mm-key { display: inline-block; width: 10px; height: 10px; border-radius: 50%; }
.mm-key-track { height: 2px; width: 14px; border-radius: 1px; background: var(--ink-3); }
.mm-key-gated { border: 1.5px solid var(--red); }
.mm-key-newest { background: var(--honey); }
.minimap-foot { margin-top: var(--s-3); }
/* Where they've been / Where it has been on the Map's own basemap (M4-0456,
   D158; map/surface.js compactTrackMap). The map is laid out invisibly over
   the fallback drawing while it comes up, then takes its place. */
.mm-host { position: relative; }
.mm-host[hidden], .mm-fallback[hidden], .mm-map[hidden], .mm-host .minimap-legend[hidden] { display: none; }
.mm-map { position: relative; height: 300px; border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; background: var(--bg-sunk); }
.mm-map.mm-map-pending { position: absolute; inset: 0; height: auto; min-height: 200px; visibility: hidden; pointer-events: none; }
.mm-zoom { top: 10px; right: 10px; bottom: auto; }
.mm-zoom button { width: 32px; height: 32px; }
.mm-marks { position: absolute; inset: 0; pointer-events: none; }
.mm-marker {
  position: absolute; z-index: 1; width: 14px; height: 14px; border-radius: 50%;
  transform: translate(-50%, -50%); pointer-events: none;
  background: var(--honey); border: 2px solid var(--bg-elev);
  box-shadow: 0 1px 4px color-mix(in oklch, var(--ink) 25%, transparent);
}
.mm-marker[hidden] { display: none; }
.mm-marker-newest.is-filtered { background: transparent; border-color: var(--red); }
/* The panels' maps (M4-0463, D168): a trip's start and end, as its grid
   drawing colours them; a point (red when held back) and its previous one. */
.mm-marker-start { background: var(--map-track-2); }
.mm-marker-end { background: var(--map-fence); }
.mm-marker-this { background: var(--map-track); }
.mm-marker-this.is-filtered { background: var(--red); }
.mm-marker-prev { width: 10px; height: 10px; background: var(--map-track); border-width: 1.5px; }
.mm-host-panel .mm-map { height: 210px; }
.mm-host-point .mm-map { height: 170px; }
.mm-host-panel .mm-map.mm-map-pending { height: auto; min-height: 0; }
/* On the basemap the track and filtered rings wear the Map's colours. */
.mm-key-track.mm-key-basemap { background: var(--map-track); }
.mm-key-gated.mm-key-basemap { border-color: var(--map-gated); }
@media (max-width: 767.98px) { .mm-map { height: 240px; } .mm-host-panel .mm-map { height: 200px; } .mm-host-point .mm-map { height: 160px; } }

/* Device's own report: one fact a line, the value right. */
.report-rows { display: grid; gap: var(--s-3); }
.report-row { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s-5); font-size: var(--t-sm); border-bottom: 1px dashed var(--line); padding-bottom: var(--s-3); }
.report-row:last-child { border-bottom: none; }
.report-value { text-align: right; min-width: 0; overflow-wrap: anywhere; }
.report-value.bad, .log-summary .bad { color: var(--red); }
.report-error { font-size: var(--t-xs); }
.report-foot { margin-top: var(--s-5); }

/* Commands and their uploaded lines. */
.log-summary { flex-wrap: wrap; gap: var(--s-3); }
.device-logs:not(:empty) { margin-top: var(--s-6); }
.log-lines {
  font-family: var(--f-mono); font-size: var(--t-micro); line-height: 1.6;
  background: var(--bg-sunk); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: var(--s-4) var(--s-5); max-height: 420px; overflow: auto;
}
.log-line { white-space: pre-wrap; overflow-wrap: anywhere; }
.log-line .log-ts, .log-line .log-cat { color: var(--ink-3); }
.log-line .log-level { text-transform: uppercase; }
.log-error .log-level, .log-error .log-msg { color: var(--red); }

@media (max-width: 1000px) {
  .device-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- Devices v3 and the device page (M4-0405; Devices.dc.html, Device.dc.html) ----------
   0381 shipped these classes with no rules (2026-10-15 audit G2-01). Values are
   the boards'; colours are the console's tokens, so dark mode follows them. */
.devices-v3 { display: flex; flex-direction: column; gap: var(--s-6); }
/* A narrow laptop window (M4-0445): beside the sidebar the seven columns need
   ~712 px and got 687 at 1024, so cells wrapped word by word and Points today
   was cut off. Config goes; the "Config behind" chip and the device page keep it.
   The cells' side padding tightens too, for the long ids and models real fleets have. */
@media (min-width: 768px) and (max-width: 1180px) {
  .devices-v3 .tbl :is(th, td):nth-child(6) { display: none; }
  .devices-v3 .tbl :is(th, td) { padding-inline: var(--s-4); }
}
/* Beside the device panel (M4-0475) the list is narrower again: Platform goes
   up to 1400 px (the panel names it). */
@media (min-width: 1200px) and (max-width: 1399.98px) {
  .devices-v3:not(.users-v3) .tbl :is(th, td):nth-child(5) { display: none; }
}

/* The status chips: a 36 px pill each, solid when pressed. */
.device-chips { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.filt {
  display: inline-flex; align-items: center; gap: var(--s-3);
  min-height: 36px; padding: 0 14px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--bg-elev); color: var(--ink-2);
  font-family: var(--f-body); font-size: var(--t-sm); cursor: pointer; white-space: nowrap;
}
.filt:hover { border-color: var(--line-strong); color: var(--ink); }
.filt:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.filt[aria-pressed="true"] { background: var(--accent-solid); color: var(--accent-solid-ink); border-color: var(--accent-solid); }
.filt .chip-count { font-weight: 600; font-variant-numeric: tabular-nums; }

/* A dot is a real 7 px disc in its chip's colour (the base .dot is a green
   6 px block that only sizes inside a flex row). */
.filt .dot, .chip .dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: none; }
.filt .dot.ok { background: var(--green); }
.filt .dot.warn { background: var(--amber); }
.filt .dot.quiet { background: var(--ink-4); }

/* v3 chips: sentence case in the body face, in a tone (ok, warn, bad, info,
   quiet). Text mixes the tone with --ink, which darkens it on light and
   lightens it on dark. Scoped to these two pages; other screens keep .chip. */
.device-page .chip {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--f-body); font-size: 12px; font-weight: 500;
  padding: 2px 9px; border: 0; border-radius: var(--r-pill);
  background: var(--bg-sunk); color: var(--ink-2);
}
.device-page .chip.ok { background: var(--green-soft); color: var(--green); }
.device-page .chip.warn { background: color-mix(in oklch, var(--amber) 18%, transparent); color: var(--honey-ink); }
.device-page .chip.bad { background: var(--red-soft); color: var(--red); }
.device-page .chip.info { background: var(--blue-soft); color: var(--blue); }
:root[data-theme="dark"] .device-page .chip.warn { color: var(--honey); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .device-page .chip.warn { color: var(--honey); } }
.device-page .chip.quiet .dot { background: var(--ink-4); }

/* The list: search bar, the device cell (name over its shortened id), health
   word over its reason, the foot. */
.device-list-bar { display: flex; flex-wrap: wrap; gap: var(--s-4); margin-bottom: var(--s-5); }
.device-list-bar .input { flex: 1 1 260px; }
/* Nothing to filter (M4-0415): Devices hides its chips and search. */
.device-chips[hidden], .device-list-bar[hidden] { display: none; }
.device-list .device-name { color: var(--ink); font-weight: 600; }
.device-list .device-name:hover { color: var(--accent-text); }
.device-id-short { display: block; font-size: var(--t-micro); color: var(--ink-3); }
.health-cell { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; }
.device-list-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-4); margin-top: var(--s-4); }
.device-list-foot:empty { display: none; }
.device-list-note { margin: 0; }
/* The link-only ?show= filter's note (no_user, from the Users tab footer). */
.device-link-note { margin: 0; }
.device-link-note[hidden] { display: none; }

/* The device page head: title row with its chips, the id line, the tabs. */
.device-title-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4); }
.device-title-row .page-title { margin: 0; }
.device-title .health-why { margin: var(--s-2) 0 0; }
.device-title .health-see-why { margin: 2px 0 0; }
.device-ids {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3);
  margin-top: var(--s-3); font-size: var(--t-sm); color: var(--ink-3);
}
.device-ids .device-user, .device-ids .device-install { display: inline-flex; align-items: center; gap: var(--s-2); min-width: 0; }
.device-ids .id-value { color: var(--ink); overflow-wrap: anywhere; }
.device-ids .btn-copy { padding: 4px; min-width: 32px; min-height: 32px; }
.device-ids .btn-copy svg { width: 13px; height: 13px; }
.device-head .page-head-row { align-items: flex-start; }
.device-tabs { margin: var(--s-6) 0 var(--s-6); }
.device-tab { flex: none; }
/* A tab's sections are hidden by attribute; a display rule (the KPI grid) would
   otherwise outrank the browser's [hidden]. */
.device-panels { min-width: 0; }
.device-panels [hidden] { display: none; }
.device-tabs .tab-note { font-weight: 400; }

/* The KPI row: four cards in one row, two below 768 px. */
.device-kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-5); }
.device-kpis .kpi { padding: 14px 16px; min-width: 0; }
.device-kpis .stat-value { font-size: 22px; font-weight: 600; letter-spacing: -0.02em; margin-top: var(--s-2); }

/* Health: a left-aligned key/value list (.kv.kv-left); a fact in trouble is red. */
.device-health .kv dd.bad { color: var(--red); }

@media (max-width: 767.98px) {
  .device-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .filt { min-height: 44px; }
}

/* ---------- Overview: setup checklist and foot (M4-0265, Overview.dc.html) ----------
   Ported from the canvas's v2-onboard / v2-steps, without the prefix (0216 §Styles). */
.setup { margin-bottom: var(--s-8); }
.setup-item {
  border: 1px solid color-mix(in oklch, var(--honey) 45%, var(--line));
  background: color-mix(in oklch, var(--honey) 7%, var(--bg-elev));
  border-radius: var(--r-lg);
  padding: 20px 22px;
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 18px; align-items: center;
}
.setup-item + .setup-item { margin-top: var(--s-4); }
.setup-steps { display: flex; gap: 6px; }
.setup-steps i { width: 26px; height: 4px; border-radius: 2px; background: var(--line-strong); display: block; }
.setup-steps i.done { background: var(--honey); }
.setup-title { font-weight: 600; }
.setup-text { margin-top: 3px; max-width: 70ch; }
.setup-more { margin-top: var(--s-4); }
.overview-dashboard[hidden] { display: none; }
/* M4-0340: when the rollup figures were computed, above the tiles. */
.overview-asof { margin: 0 0 var(--s-4); max-width: 80ch; }
.overview-asof[hidden] { display: none; }

/* ---------- Overview: org tiles and Open problems (M4-0295, design pass §0295) ---------- */
.overview-dashboard { display: grid; gap: var(--s-7); margin-bottom: var(--s-8); }
.overview-tiles .tile { display: flex; flex-direction: column; min-width: 0; }
.tile-partial { color: var(--ink-2); }
.problems .section-head { margin-bottom: var(--s-4); }
.problems-list { list-style: none; margin: 0; padding: 0; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--bg-elev); }
.problem { display: grid; grid-template-columns: 84px minmax(0, 1fr) auto; gap: var(--s-4); align-items: center; padding: 12px 16px; }
.problem + .problem { border-top: 1px solid var(--line); }
.problems-more { margin-top: var(--s-3); }
.problems-more-error { margin-left: var(--s-2); }
@media (max-width: 640px) { .problem { grid-template-columns: minmax(0, 1fr); } }
/* M4-0382 (Main.dc.html): Needs attention as one card, its rows divided by
   lines, and Recent changes beside it; they stack when the row is narrow. */
.overview-split { display: flex; flex-wrap: wrap; gap: var(--s-6); align-items: flex-start; }
.overview-split > .problems { flex: 999 1 520px; min-width: 0; padding: 18px 18px 8px; }
.overview-split > .overview-recent { flex: 1 1 300px; min-width: 0; padding: 18px; }
.overview-split > section + section { margin-top: 0; }
.overview-split .section-head { margin-bottom: var(--s-3); }
.overview-split .problems-list { border: 0; border-radius: 0; background: none; }
.overview-split .problem { grid-template-columns: 110px minmax(0, 1fr) auto; padding: 12px 0; border-top: 1px solid var(--line); }
.overview-split .problems-none { margin: 0 0 var(--s-4); }
.problem-sub { margin-top: 2px; }
/* M4-0382 (D135): the KPI row's "of N", a failure count in red, the cap bar,
   and the region chip and count in the projects card. */
.stat-of { font-size: 16px; font-weight: 500; color: var(--ink-3); }
.stat-bad { color: var(--red); }
.tile-cap { margin: 4px 0 2px; }
.project-cell { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.region-chip { font-size: 11px; }
.overview-projects .section-head { align-items: baseline; }
.recent-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
.recent-item { display: flex; flex-direction: column; gap: 2px; min-width: 0; overflow-wrap: anywhere; }
@media (max-width: 640px) { .overview-split .problem { grid-template-columns: minmax(0, 1fr); } }
.overview-foot {
  display: flex; flex-wrap: wrap; gap: var(--s-4) 32px;
  margin-top: 28px; padding-top: 18px; border-top: 1px solid var(--line);
  font-size: var(--t-xs); color: var(--ink-3);
}
@media (max-width: 640px) { .setup-item { grid-template-columns: minmax(0, 1fr); } }
.strong { font-weight: 500; }

/* Settings (M4-0271, Settings.dc.html): cards with an h2 head, left-aligned
   key/value rows (v2's .v2-kv as Settings sets it), the danger row at a card's
   foot, and Organisation beside Appearance. */
.settings-card { padding: 20px; }
.kv.kv-left { grid-template-columns: 160px 1fr; margin-top: 0; font-size: var(--t-sm); }
.kv.kv-left dd { text-align: left; color: var(--ink); }
.settings-note { margin: 10px 0 0; max-width: 72ch; }
.settings-foot {
  display: flex; justify-content: space-between; align-items: center; gap: 20px;
  margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line);
}
.settings-foot > p { margin: 0; max-width: 60ch; }
.settings-pair { display: grid; grid-template-columns: 1.5fr 1fr; gap: 18px; }
/* M4-0367: the Account link under Settings, the Account form, the cap control. */
.account-form { margin-top: 18px; max-width: 480px; }
.usage-cap-control:empty { display: none; }
@media (max-width: 760px) {
  .settings-pair { grid-template-columns: 1fr; }
  .settings-foot { flex-direction: column; align-items: flex-start; }
  .kv.kv-left { grid-template-columns: 1fr; }
}

/* ---------- phone layouts (M4-0268; Design v2's PhoneNav, PhoneActivity, PhoneMap) ----------
   The shell's phone breakpoint (768 px) and nothing else: below it the rail's
   top row is the navigation (D173); this block makes the v2 phone frame's touch
   targets, Activity's cards and the full-bleed Map. CSS only, no new control:
   every action stays where the desktop has it. */
@media (max-width: 767.98px) {
  /* v2's phone frame: 44 px targets for the rail's tiles (their own 44 px) and
     the buttons; a small button keeps 36 px (v2's "Layers", "Filters"). */
  /* Icon buttons, copy buttons, row menus, segmented chips and tabs are at
     least 40 px on a phone (M4-0407, G5-17, G4-11, G1-33). */
  /* They are all .btn, so already 44 px tall here. */
  :is(.row-menu-btn, .key-copy-btn, .btn-copy) { min-width: 40px; }
  .id-chip .btn, .map-tool, .lang-tab, .row-menu-item,
  .wh-tabs [role="tab"], .device-tabs [role="tab"], .org-tab { min-height: 40px; }
  /* v2's phone body keeps a 16 px gutter; Login's pane (0272) too. */
  .auth-pane { padding-left: var(--s-6); padding-right: var(--s-6); }

  /* Activity, and a device's recent points: one card per point (v2-pcard), its
     cells a two-column grid of captioned values; the time and the row's link
     take the whole width. Nothing scrolls sideways, not even inside the wrap. */
  .table-scroll { border: 0; border-radius: 0; background: transparent; }
  .table-scroll .table, .table-scroll .table tbody { display: block; width: 100%; }
  .table-scroll .table thead { display: none; }
  .table-scroll .table tr {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 14px;
    width: 100%; padding: 12px 14px; margin-bottom: 10px;
    border: 1px solid var(--line); border-radius: var(--r-md); background: var(--bg-elev);
  }
  .table-scroll .table tr:last-child { margin-bottom: 0; border-bottom: 1px solid var(--line); }
  .table-scroll .table tr.gated { border-color: color-mix(in oklch, var(--red) 30%, var(--line)); }
  .table-scroll .table tr.gated td { background: transparent; }
  .table-scroll .table td {
    display: block; width: auto; min-width: 0; padding: 0; border: 0;
    white-space: normal; overflow-wrap: anywhere; text-align: left;
  }
  .table-scroll .table td::before {
    content: attr(data-label); display: block;
    font-family: var(--f-mono); font-size: var(--t-eyebrow);
    letter-spacing: var(--tr-eyebrow); text-transform: uppercase;
    color: var(--ink-3); margin-bottom: 2px;
  }
  .table-scroll .table td.cell-actions::before { content: none; }
  .table-scroll .table td:first-child, .table-scroll .table td.cell-actions { grid-column: 1 / -1; }
  .table-scroll .table td:empty { display: none; }
  .table-scroll .table .cell-num { text-align: left; }
  /* A session divider between the cards (M4-0338): a captionless line, not a card. */
  .table-scroll .table tr.session-break { display: block; padding: 0; border: 0; background: transparent; }
  .table-scroll .table tr.session-break td { display: block; padding: 6px 4px; border-left: 0; border-right: 0; border-radius: 0; overflow-wrap: anywhere; }
  .table-scroll .table tr.session-break td::before { content: none; }

  /* Map (PhoneMap): the map is the screen under the topbar, edge to edge; the
     window and layers bar floats on its top, the chosen device's card is a
     bottom sheet on its bottom edge, and the freshness line, the legend and the
     device list follow below it. One grid cell holds the map and its floats. */
  .map-stage { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0; padding: 0; height: auto; min-height: 0; overflow: visible; }
  .map-stage > .map-wrap {
    grid-area: 1 / 1; position: relative; inset: auto;
    height: calc(100vh - var(--topbar-h)); height: calc(100dvh - var(--topbar-h)); min-height: 420px;
    border: 0; border-radius: 0;
  }
  .map-stage > .map-bar {
    grid-area: 1 / 1; align-self: start; position: relative; inset: auto; z-index: 2; margin: 12px;
    box-shadow: 0 6px 24px color-mix(in oklch, var(--ink) 10%, transparent);
  }
  .map-stage > .map-card {
    grid-area: 1 / 1; align-self: end; position: relative; inset: auto; z-index: 3;
    width: auto; max-height: 45vh; max-height: 45dvh; overflow-y: auto; margin: 0;
    border-width: 1px 0 0; border-radius: 18px 18px 0 0; padding: 10px 18px 22px;
    box-shadow: 0 -6px 24px color-mix(in oklch, var(--ink) 12%, transparent);
  }
  .map-stage > .map-card::before { content: ""; display: block; width: 36px; height: 4px; border-radius: 2px; background: var(--line-strong); margin: 0 auto 12px; }
  .map-stage > .map-fresh, .map-stage > .map-legend, .map-stage > .map-panel { margin: 12px 12px 0; }
  .map-stage > .map-panel { margin-bottom: 12px; }
  /* The zoom and the no-basemap note sit at the map's middle, clear of the bar
     above and the sheet below. */
  .map-stage .map-zoom { top: 50%; bottom: auto; right: 12px; transform: translateY(-50%); }
  .map-stage .map-basemap-note { top: 50%; bottom: auto; left: 12px; right: 60px; margin: 0; width: auto; max-width: none; transform: translateY(-100%); }
}

/* ---------- phone rows (M4-0414; audit 2026-10-15 §5.4, G2-25, G1-35, G3-08, G3-19) ----------
   Below the phone breakpoint, Devices, Activity's points, the Audit log and
   Overview's projects are list rows of two or three lines (Phone.dc.html), not
   label-per-field cards. Same table, same row hooks (data-install, data-pos),
   laid out as a grid of lines: the cells say what they are by where they sit,
   so the reflow's td::before captions are off for these four tables (Q1).
   A spanning item always spans the row's 1fr track, so a long name never
   widens the figures' auto tracks under it. Separators are ::after, with an
   empty alt text so a screen reader does not read them. */
.device-today-phone { display: none; }
.map-note-chip { display: none; }
@media (max-width: 767.98px) {
  :is(.overview-projects, .audit-body) .table tbody tr,
  .devices-v3:not(.users-v3) .tbl tbody tr,
  .table-scroll .table.activity-table tbody tr.ac-row {
    display: grid; grid-template-columns: auto auto minmax(0, 1fr) auto;
    align-items: center; gap: 2px 0; min-height: 44px;
  }
  :is(.overview-projects, .audit-body) .table tbody td,
  .devices-v3:not(.users-v3) .tbl tbody td,
  .table-scroll .table.activity-table tbody tr.ac-row td {
    display: block; width: auto; height: auto; min-width: 0; padding: 0; border: 0; text-align: left;
  }
  :is(.overview-projects, .audit-body) .table tbody td::before,
  .devices-v3:not(.users-v3) .tbl tbody td::before,
  .table-scroll .table.activity-table tbody tr.ac-row td::before { display: none; }
  .devices-v3:not(.users-v3) .tbl tbody td:is(:nth-child(3), :nth-child(4))::after,
  .table-scroll .table.activity-table tbody tr.ac-row td:is(:nth-child(2), :nth-child(3))::after,

  :is(.devices-v3, .overview-projects, .audit-body) .table tbody tr,
  .tbl-wrap .tbl.activity-table tbody tr.ac-row {
    display: grid; grid-template-columns: auto auto minmax(0, 1fr) auto;
    align-items: center; gap: 2px 0; min-height: 44px;
  }
  :is(.devices-v3, .overview-projects, .audit-body) .table tbody td,
  .tbl-wrap .tbl.activity-table tbody tr.ac-row td {
    display: block; width: auto; min-width: 0; padding: 0; border: 0; text-align: left;
  }
  :is(.devices-v3, .overview-projects, .audit-body) .table tbody td::before,
  .tbl-wrap .tbl.activity-table tbody tr.ac-row td::before { display: none; }
  .devices-v3 .table tbody td:is(:nth-child(2), :nth-child(3))::after,
  .tbl-wrap .tbl.activity-table tbody tr.ac-row td:is(:nth-child(2), :nth-child(4))::after,
  .audit-body .table tbody td:nth-child(2)::after {
    content: "·" / ""; margin: 0 6px; color: var(--ink-3);
  }

  /* Devices (Phone.dc.html; v4 columns since M4-0475): name and health
     badge, then "user · last point · N points today", then the health reason
     when there is one. The name's link covers the row, so the whole row opens
     the device page. Platform and Config are on the device page. */
  .devices-v3:not(.users-v3) .tbl tbody tr { position: relative; padding: 8px 14px; border-top: 1px solid var(--line); }
  .devices-v3:not(.users-v3) .tbl tbody td:nth-child(1) { grid-area: 1 / 1 / 2 / 4; }
  .devices-v3:not(.users-v3) .tbl tbody td:nth-child(1) .device-id-short { display: none; }
  .devices-v3:not(.users-v3) .tbl tbody td:nth-child(1) a.device-name { font-weight: 600; padding: 0; margin: 0; } /* the row is its hit area (::after) */
  .devices-v3:not(.users-v3) .tbl tbody td:nth-child(1) a.device-name::after { content: ""; position: absolute; inset: 0; }
  .devices-v3:not(.users-v3) .tbl tbody td:nth-child(2), .devices-v3:not(.users-v3) .tbl .health-cell { display: contents; }
  .devices-v3:not(.users-v3) .tbl tbody .badge { grid-area: 1 / 4; justify-self: end; margin-left: 8px; }
  .devices-v3:not(.users-v3) .tbl tbody .badge-sub {
    grid-area: 3 / 1 / 4 / -1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  }
  .devices-v3:not(.users-v3) .tbl tbody td:nth-child(3) { grid-area: 2 / 1; }
  .devices-v3:not(.users-v3) .tbl tbody td:nth-child(4) { grid-area: 2 / 2; white-space: nowrap; }
  .devices-v3:not(.users-v3) .tbl tbody td:nth-child(7) { grid-area: 2 / 3 / 3 / -1; }
  .devices-v3:not(.users-v3) .tbl tbody td:is(:nth-child(3), :nth-child(4), :nth-child(7)) { font-size: 13px; color: var(--ink-3); }
  .devices-v3:not(.users-v3) .tbl tbody td:is(:nth-child(5), :nth-child(6)) { display: none; }
  .devices-v3 .device-today-total { display: none; }
  .devices-v3 .device-today-phone { display: inline; }
  .devices-v3:not(.users-v3) .tbl thead { display: none; }

  /* Activity (v4 columns, D173): the time and the status badge, then
     device · user · accuracy. Where and Speed are in the point's panel.
     Session dividers stay as they are. */
  .tbl-wrap .tbl.activity-table tbody tr.ac-row { padding: 8px 14px; margin-bottom: 8px; }
  /* The badge spans the user and accuracy tracks (the last one flexible), so
     a long status ("Filtered: not accurate enough") takes the room beside the
     time instead of widening an auto track that line 2 shares and wrapping
     the row to three lines (M4-0483). */
  .tbl-wrap .tbl.activity-table tbody tr.ac-row td:nth-child(1) { grid-area: 1 / 1; font-weight: 500; white-space: nowrap; }
  .tbl-wrap .tbl.activity-table tbody tr.ac-row td:nth-child(3) { grid-area: 1 / 2 / 2 / -1; justify-self: end; margin-left: 8px; min-width: 0; max-width: 100%; }
  /* On the badge's line, the reason beside "Filtered" (a second line on the
     desktop), cut with an ellipsis when long; the point's panel says it whole. */
  .tbl-wrap .tbl.activity-table tbody tr.ac-row .ac-status { flex-direction: row; align-items: baseline; gap: 6px; max-width: 100%; white-space: nowrap; }
  .tbl-wrap .tbl.activity-table tbody tr.ac-row .ac-status .badge-sub { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  .tbl-wrap .tbl.activity-table tbody tr.ac-row td:nth-child(2) { grid-area: 2 / 1; }
  .tbl-wrap .tbl.activity-table tbody tr.ac-row td:nth-child(4) { grid-area: 2 / 2; }
  .tbl-wrap .tbl.activity-table tbody tr.ac-row td:nth-child(6) { grid-area: 2 / 3 / 3 / -1; }
  .tbl-wrap .tbl.activity-table tbody tr.ac-row td:is(:nth-child(2), :nth-child(4), :nth-child(6)) {
    font-size: 13px; color: var(--ink-3); white-space: nowrap;
  }
  .tbl-wrap .tbl.activity-table tbody tr.ac-row td:is(:nth-child(5), :nth-child(7)) { display: none; }
  .tbl-wrap .tbl.activity-table tbody tr.ac-row td { height: auto; }
  .tbl-wrap .tbl.activity-table tbody tr.ac-row td:is(:first-child, :last-child) { border-radius: 0; }

  /* The Audit log: the sentence, then who · when. */
  .audit-body .table tbody tr { padding: 8px 14px; }
  .audit-body .table tbody td:nth-child(3) { grid-area: 1 / 1 / 2 / -1; overflow-wrap: anywhere; }
  .audit-body .table tbody td:nth-child(2) { grid-area: 2 / 1; overflow-wrap: anywhere; }
  .audit-body .table tbody td:nth-child(1) { grid-area: 2 / 2 / 3 / -1; white-space: nowrap; }
  .audit-body .table tbody td:is(:nth-child(1), :nth-child(2)) { font-size: 13px; color: var(--ink-3); }

  /* Overview's projects: the name, its region and status, then devices
     sending · points today · last point; Open (or Settings) at the right of
     both lines. */
  .overview-projects .table tbody tr { grid-template-columns: auto auto auto minmax(0, 1fr) auto auto; padding: 8px 14px; }
  .overview-projects .table tbody td:nth-child(1) { grid-area: 1 / 1 / 2 / 5; }
  .overview-projects .table tbody td:nth-child(2) { grid-area: 1 / 5; justify-self: end; margin-left: 8px; }
  .overview-projects .table tbody td:nth-child(3) { grid-area: 2 / 1; }
  .overview-projects .table tbody td:nth-child(4) { grid-area: 2 / 2; }
  .overview-projects .table tbody td:nth-child(5) { grid-area: 2 / 3 / 3 / 6; overflow: hidden; text-overflow: ellipsis; }
  .overview-projects .table tbody td:nth-child(6) { grid-area: 1 / 6 / 3 / 7; padding: 0 0 0 10px; }
  .overview-projects .table tbody td:is(:nth-child(3), :nth-child(4), :nth-child(5)) { font-size: 13px; color: var(--ink-3); white-space: nowrap; }
  .overview-projects .table tbody td:nth-child(3)::after { content: "\00a0sending\00a0\00a0·\00a0\00a0" / " sending,"; }
  .overview-projects .table tbody td:nth-child(4)::after { content: "\00a0points today\00a0\00a0·\00a0\00a0" / " points today,"; }

  /* The Trip page: the four tiles two by two, and the no-basemap note a
     one-line chip at the map's top-left (.map-wrap.trip-map: above the
     Trips section's own .trip-map rule further down). */
  .trip-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .map-wrap.trip-map .map-basemap-note {
    right: auto; width: max-content; max-width: calc(100% - 72px); padding: 6px 10px; gap: 6px; align-items: center;
    border-radius: var(--r-pill);
  }
  .map-wrap.trip-map .map-basemap-note > svg { margin-top: 0; }
  .trip-map .map-note-chip { display: inline; white-space: nowrap; font-weight: 500; }
  .trip-map .map-note-full {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
}

/* ---------- Project settings (M4-0482; v4's Settings.dc.html, D173) ----------
   A main column (the section index as the kit's anchor-bar chips, then
   General, Data kept, Suspend or erase) and an aside (Organisation, Who can
   do what here). Every control is the kit's; these rules only lay out. */
.settings-layout { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 16px; }
.ps-main { flex: 999 1 560px; min-width: 0; max-width: 820px; display: flex; flex-direction: column; gap: 16px; padding-bottom: 24px; }
.ps-aside { flex: 1 1 300px; min-width: 0; max-width: 360px; display: flex; flex-direction: column; gap: 16px; }
.settings-index { display: flex; flex-wrap: wrap; gap: 8px; }
.ps-main .readonly-banner { margin: 0; }
.ps-card { display: flex; flex-direction: column; gap: 16px; scroll-margin-top: calc(var(--topbar-h) + 16px); }
.ps-aside .ps-card { gap: 10px; }
.ps-name-line { display: flex; flex-wrap: wrap; gap: 8px; }
.ps-name-line .input { flex: 1 1 220px; width: auto; min-width: 0; }
.ps-name-why { flex-basis: 100%; margin: 0; }
.ps-name-value { margin: 0; font-size: var(--t-body); }
.ps-tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.ps-tile { min-width: 0; padding: 12px 14px; border-radius: var(--r-lg); background: var(--bg-sunk); }
.ps-tile-label { font-size: var(--t-xs); color: var(--ink-2); }
.ps-tile-value { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 4px; font-size: 17px; min-width: 0; }
.ps-tile-value .badge { font-size: 17px; }
.ps-tile-note { font-size: 13.5px; color: var(--ink-2); }
.ps-region-id { font-size: 13.5px; padding: 2px 8px; border-radius: var(--r-pill); background: var(--bg-elev); border: 1px solid var(--line); }
.ps-id { font-size: var(--t-xs); overflow-wrap: anywhere; min-width: 0; }
.ps-note { margin: 0; font-size: 13.5px; color: var(--ink-2); }
.settings-retention { margin: 0; } /* Data kept's server read (M4-0320, views/settings-data.js) */
.settings-retention-line { margin: 0; font-weight: 500; }
.ps-data-link { margin: 0; font-size: var(--t-sm); }
/* A link inside a line of text is told apart by more than its colour
   (axe link-in-text-block, M4-0483). */
a.link-in-text { text-decoration: underline; text-underline-offset: 2px; }
.ps-danger { gap: 0; border-color: color-mix(in oklch, var(--red) 25%, var(--line)); }
.ps-danger > .card-title { margin-bottom: 6px; color: var(--red); }
.ps-row {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 0; border-top: 1px solid var(--line);
}
.ps-danger > .card-title + .ps-row { border-top: 0; }
.ps-row-main { display: flex; flex-direction: column; gap: 2px; flex: 1 1 320px; min-width: 0; }
.ps-row-aside { flex: none; }
.ps-label { font-weight: 500; font-size: 14.5px; }
.ps-help { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--ink-2); }
.ps-links { display: flex; flex-wrap: wrap; gap: 8px; }
.dialog-message > p { margin: 0 0 10px; }
.erase-list { margin: 0 0 12px; padding-left: 20px; font-size: var(--t-sm); }
.erase-list li { margin: 4px 0; }
/* M4-0420 (G4-36): what the project holds now. */
.erase-counts { margin: 0 0 12px; }
@media (max-width: 600px) {
  .ps-tiles { grid-template-columns: 1fr; }
}
.settings-hook-url { word-break: break-all; }

/* The Members tab (M4-0267, M4-0323; v3 board M4-0389, D129): the inline
   invite form, a compact role select per row, the member cell's initials and
   chips, the three-role legend, and the invite link shown once in its dialog. */
.select.select-sm { width: auto; min-width: 0; padding-top: 4px; padding-bottom: 4px; font-size: var(--t-sm); }
.members-status { margin-top: 12px; }
.invite-form { padding: var(--s-5); }
.invite-fields { display: flex; flex-wrap: wrap; gap: var(--s-4); align-items: flex-start; }
/* M4-0483: the button lines up with the controls, under the fields' labels. */
.invite-submit { padding-top: 28px; }
@media (max-width: 767.98px) { .invite-submit { padding-top: 0; } }
.invite-fields > .field { margin-bottom: 0; flex: 1 1 260px; }
.invite-fields > .field + .field { flex: 0 1 220px; }
.invite-note { margin: var(--s-3) 0 0; }
.invite-error { margin-top: var(--s-3); }
.member-who { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.member-av {
  width: 30px; height: 30px; border-radius: 999px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: color-mix(in oklch, var(--honey) 22%, transparent); color: var(--accent-text);
  font-weight: 600; font-size: 12px;
}
.member-chip {
  display: inline-flex; align-items: center; padding: 2px 9px; border-radius: 999px;
  font-size: 12px; font-weight: 500; white-space: nowrap;
  background: var(--bg-sunk); color: var(--ink-2);
}
.member-chip.warn { background: color-mix(in oklch, var(--amber) 18%, transparent); color: var(--honey-ink); }
:root[data-theme="dark"] .member-chip.warn { color: var(--honey); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .member-chip.warn { color: var(--honey); } }
.members-legend {
  display: flex; flex-wrap: wrap; gap: 6px 20px;
  margin-top: var(--s-4); padding-top: var(--s-4); border-top: 1px solid var(--line);
}
.members-legend strong { color: var(--ink); font-weight: 600; }
.invite-link {
  display: block; padding: 10px 12px; border: 1px solid var(--line); border-radius: 6px;
  word-break: break-all; user-select: all; font-size: var(--t-sm);
}

/* Phone: Activity's "Filters · N" collapse (M4-0329; Design v2's
   PhoneActivity). Above 768 px the filters' group lays out as if it were not
   there and the button is not shown. */
.activity-filters { display: contents; }
.activity-filters-btn { display: none; }
@media (max-width: 767.98px) {
  .activity-filters-btn { display: inline-flex; align-self: flex-end; }
  .activity-controls:not([data-filters="open"]) .activity-filters { display: none; }
  .activity-controls[data-filters="open"] .activity-filters {
    display: flex; flex-wrap: wrap; align-items: flex-end;
    gap: var(--s-4) var(--s-5); flex-basis: 100%; min-width: 0;
  }
}

/* ---------- The Webhooks page (M4-0364; v4 M4-0480, D173) ----------
   Webhooks.dc.html: the tabs as one segmented control in the head; the
   endpoints table with its side panel BESIDE it (it wraps under the table only
   when the two cannot share the row); with no endpoint, the Add an endpoint
   form beside the page; the Event log as rows of a tile, a sentence and its
   type. Layout only: the controls, cards, table, badges, chips and pager are
   the component kit's (M4-0484). */
.wh-head .page-head-row { align-items: flex-end; flex-wrap: wrap; }
.wh-head-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--s-4); }
.wh-tab-bad { color: var(--red); }
.wh-screen { display: flex; flex-direction: column; gap: var(--s-6); }
.wh-screen .readonly-banner { margin-bottom: 0; }
.wh-screen [role="tabpanel"][hidden] { display: none; }
.wh-cols { display: flex; flex-wrap: wrap; gap: var(--s-6); align-items: flex-start; }
.wh-main { flex: 999 1 560px; min-width: 0; display: flex; flex-direction: column; gap: var(--s-6); }
.wh-aside { flex: 1 1 320px; max-width: 380px; min-width: 0; display: flex; flex-direction: column; gap: var(--s-6); }
.wh-aside[hidden] { display: none; }
.wh-split { display: flex; flex-wrap: wrap; gap: var(--s-6); align-items: flex-start; }
.wh-split > .wh-table-card { flex: 999 1 480px; min-width: 0; }
.wh-panel-host { flex: 1 1 340px; max-width: 420px; min-width: 0; }
.wh-panel-host:empty { display: none; }
.wh-table-card { display: flex; flex-direction: column; gap: var(--s-4); min-width: 0; }
.wh-table-card > .tbl-wrap, .wh-table-card > div > .tbl-wrap { margin-inline: -8px; }
.wh-row { cursor: pointer; }
.wh-host { display: block; font-weight: 500; overflow-wrap: anywhere; }
.wh-path { display: block; font-size: var(--t-xs); color: var(--ink-3); overflow-wrap: anywhere; }

/* No endpoint yet: the card's sentence and its one action on a row. */
.wh-none { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-5); }
.wh-none-text { min-width: 0; flex: 1 1 260px; display: flex; flex-direction: column; gap: 4px; }

/* The Add an endpoint form and what an endpoint offers. */
.wh-add { display: flex; flex-direction: column; gap: 14px; }
.wh-add .card-sub { margin-top: 6px; }
.wh-add-form { display: flex; flex-direction: column; gap: 14px; }
.wh-form { display: flex; flex-direction: column; gap: 14px; }
.wh-add-note { margin: 0; font-size: var(--t-xs); line-height: 1.5; color: var(--ink-2); }
.wh-then { display: flex; flex-direction: column; gap: 8px; }
.wh-then .card-sub { line-height: 1.55; }
.wh-types { border: 0; padding: 0; margin: 0; min-width: 0; display: flex; flex-direction: column; }
.wh-types > legend { padding: 0; margin-bottom: 4px; }
.wh-types > .desc { margin-bottom: 2px; }
/* M4-0483: an event type's label on one line, its wire name under it. */
.wh-type { display: flex; flex-direction: column; gap: 1px; min-width: 0; padding: 4px 0; }
.wh-type-name { line-height: 1.35; white-space: nowrap; }
.wh-type-code { font-family: var(--f-mono); font-size: var(--t-xs); line-height: 1.35; color: var(--ink-3); overflow-wrap: anywhere; }

/* A card's head: its title and its chips on one wrapping row, a note under. */
.wh-card-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; }
.wh-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.wh-card-note { margin: 0; font-size: var(--t-xs); line-height: 1.5; color: var(--ink-3); }
.wh-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.wh-filters:empty, .wh-filters:not(:has(> :not([hidden]))) { display: none; }
.wh-scope { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: var(--t-sm); }

/* The Event log: one row per event. */
.wh-ev-list { list-style: none; margin: 0; padding: 0; }
.wh-ev { border-top: 1px solid var(--line); }
.wh-ev-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; padding: 12px 0; }
.wh-ev-icon {
  width: 38px; height: 38px; flex: none; border-radius: 12px;
  display: inline-flex; align-items: center; justify-content: center;
}
.wh-tone-honey { background: var(--honey-soft); color: var(--accent-text); }
.wh-tone-sunk { background: var(--bg-sunk); color: var(--ink-2); }
.wh-tone-blue { background: var(--blue-soft); color: var(--blue); }
.wh-tone-green { background: var(--green-soft); color: var(--green); }
.wh-tone-red { background: var(--red-soft); color: var(--red); }
.wh-ev-text { flex: 1 1 220px; min-width: 0; }
.wh-ev-what { display: block; font-size: var(--t-h3); overflow-wrap: anywhere; }
.wh-ev-meta { display: block; font-size: var(--t-xs); color: var(--ink-3); overflow-wrap: anywhere; }
.wh-ev-when { font-size: var(--t-sm); color: var(--ink-2); white-space: nowrap; }
.wh-ev-more { padding: 0 0 12px 52px; }
.wh-ev-more[hidden] { display: none; }
.wh-when2 { display: inline-flex; flex-direction: column; gap: 2px; }
.wh-when-logged { font-size: var(--t-xs); color: var(--ink-3); }

/* The endpoint's side panel. */
.wh-panel { display: flex; flex-direction: column; gap: var(--s-6); min-width: 0; }
.wh-panel-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.wh-panel-title { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; min-width: 0; }
.wh-panel-title .card-title { overflow-wrap: anywhere; }
.wh-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.wh-url-row {
  display: flex; align-items: center; gap: 8px; min-width: 0;
  padding: 6px 6px 6px 12px; border-radius: var(--r-md); background: var(--bg-sunk);
}
.wh-url-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--t-xs); }
.wh-kv.kv-left { grid-template-columns: 120px 1fr; }
.wh-kv dd { text-align: left; }
.wh-sub { margin: 0 0 calc(-1 * var(--s-3)); font-size: var(--t-sm); font-weight: 500; color: var(--ink-2); }
.wh-small { margin: 0; font-size: var(--t-xs); color: var(--ink-3); }
.wh-queue { display: flex; flex-direction: column; gap: var(--s-5); align-items: flex-start; }
.wh-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; width: 100%; }
.wh-tile { display: flex; flex-direction: column; gap: 4px; padding: 12px 14px; border-radius: var(--r-lg); background: var(--bg-sunk); }
.wh-tile-label { font-size: var(--t-xs); color: var(--ink-3); }
.wh-tile-num { font-size: 24px; font-weight: 400; letter-spacing: -0.02em; line-height: 1; font-variant-numeric: tabular-nums; }
.wh-tile-bad { background: var(--red-soft); }
.wh-tile-bad .wh-tile-label, .wh-tile-bad .wh-tile-num { color: var(--red); }
.wh-latest { list-style: none; margin: 0; padding: 0; width: 100%; }
.wh-latest li { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; padding: 10px 0; border-top: 1px solid var(--line); font-size: var(--t-sm); }
.wh-latest-what { flex: 1 1 160px; min-width: 0; }
.wh-latest-when { font-size: var(--t-xs); color: var(--ink-3); white-space: nowrap; }
.wh-panel-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap;
  padding-top: var(--s-5); border-top: 1px solid var(--line);
}
@media (max-width: 767px) {
  .wh-head-actions { justify-content: flex-start; }
  .wh-aside { max-width: none; }
  .wh-ev-more { padding-left: 0; }
}

/* The shared tabs() underline (device page, usage, the key dialog). */
.wh-tabs, .device-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--line); overflow-x: auto; }
.wh-tabs [role="tab"], .device-tabs [role="tab"] {
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; cursor: pointer;
  min-height: 40px; padding: 10px 14px; border: 0; border-bottom: 2px solid transparent; background: none;
  font: inherit; font-weight: 500; color: var(--ink-3);
}
.wh-tabs [role="tab"][aria-selected="true"], .device-tabs [role="tab"][aria-selected="true"] { color: var(--ink); border-bottom-color: var(--honey); }
/* Inset: the tab strip scrolls sideways, which would clip an outer ring. */
.wh-tabs [role="tab"]:focus-visible, .device-tabs [role="tab"]:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--accent-text); border-radius: var(--r-sm); }
.table tbody tr.sel td { background: color-mix(in oklch, var(--honey) 14%, transparent); }
/* ---------- Trips (M4-0366) ---------- */
/* A trip's map is the Map's surface in a card: the note at the top and the
   zoom at the right, inside the map, at every width. */
.trip-map { min-height: 0; height: clamp(300px, 52vh, 520px); }
.trip-map .map-basemap-note { top: 12px; left: 12px; right: 60px; margin: 0; width: auto; max-width: 560px; transform: none; }
.trip-map .map-zoom { top: 12px; bottom: auto; right: 12px; transform: none; }
/* A route point's card (M4-0416, G3-17), bottom-left over the trip's map. */
.trip-point-card { left: 12px; bottom: 12px; width: min(260px, calc(100% - 24px)); padding: 10px 14px 12px; }
.trip-point-card[hidden] { display: none; }
.trip-point-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); }
.trip-point-head .btn-icon { margin: -4px -8px -4px 0; }
.trip-point-card .kv { margin-top: 6px; }
.trip-key-start { background: var(--map-track-2); }
.trip-key-end { background: var(--map-fence); }
.trip-key-track { height: 3px; width: 14px; border-radius: 1px; background: var(--map-track); }
/* ---------- The remote config editor (M4-0363; views/config.js) ----------
   Console v4 (M4-0479, D173; Config.dc.html), in the v4 kit: the page's
   layout only — the controls are the kit's. A main column of section cards
   (one column of fields each) beside a side column (Who has version N, Who
   can change this); the chip index sticks at the top, the save bar at the
   bottom. The skeleton's field grid (lib/ui.js skeletonFields) keeps
   .config-grid. */
.config-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 24px; align-items: start; }
.config-page .page-head { max-width: 720px; }
.config-head-text { min-width: 0; }
.config-rev { font-weight: 500; color: var(--ink); font-variant-numeric: tabular-nums; }
.config-rev-note { color: var(--ink-2); }
.cfg-layout { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 16px; }
.cfg { flex: 999 1 560px; min-width: 0; max-width: 720px; display: flex; flex-direction: column; gap: 16px; }
.cfg-aside { flex: 1 1 300px; max-width: 360px; min-width: 0; display: flex; flex-direction: column; gap: 16px; }
.config-index {
  position: sticky; top: 0; z-index: 2; display: flex; flex-wrap: wrap; gap: 8px;
  padding: 8px 0; background: var(--bg);
}
.config-card { display: flex; flex-direction: column; gap: 20px; scroll-margin-top: 72px; }
.config-card > .card-head { margin: 0; }
.dep { display: flex; flex-direction: column; gap: 20px; margin: -4px 0 0 2px; padding-left: 18px; border-left: 2px solid var(--line); }
.config-floor[hidden] { display: none; }
.config-read .config-value { display: block; font-variant-numeric: tabular-nums; color: var(--ink); }
/* The marks: a field's "Changed · was …" and an index chip's "n changes" /
   "n to fix". */
.config-tag {
  display: inline-flex; align-items: center; gap: 4px; padding: 2px 9px; border-radius: 999px;
  font-size: var(--t-xs); font-weight: 500; line-height: 1.5; white-space: nowrap;
  background: var(--bg-sunk); color: var(--ink-2);
}
.config-tag[hidden] { display: none; }
.config-tag-edit { background: color-mix(in oklch, var(--honey) 18%, transparent); color: var(--honey-ink); }
.config-tag-bad { background: var(--red-soft); color: var(--red); }
:root[data-theme="dark"] .config-tag-edit { color: var(--honey); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .config-tag-edit { color: var(--honey); } }
/* The save bar. */
.config-bar {
  position: sticky; bottom: 12px; z-index: 2;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 18px; background: var(--bg-elev); border: 1px solid var(--line); border-radius: 22px;
  box-shadow: 0 8px 24px color-mix(in oklch, var(--ink) 10%, transparent);
}
.config-bar-text { flex: 1 1 260px; min-width: 0; font-size: var(--t-sm); }
.config-bar-line { margin: 0; }
.config-count { font-weight: 500; }
.config-bar-note { color: var(--ink-2); }
.config-blocker { margin: 2px 0 0; font-size: var(--t-xs); color: var(--red); }
.config-blocker[hidden], .config-status[hidden], .config-problem[hidden] { display: none; }
.config-blocker a { color: var(--red); text-decoration: underline; }
.config-status { margin: 2px 0 0; color: var(--ink-2); }
.config-problem { margin-top: 8px; }
.config-problem-body { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
/* Who has version N. */
.cfg-roll, .cfg-who { display: flex; flex-direction: column; gap: 14px; }
.cfg-who { gap: 8px; }
.cfg-roll-body { display: flex; flex-direction: column; gap: 14px; }
.cfg-roll-bar { display: flex; height: 10px; border-radius: 999px; overflow: hidden; gap: 2px; }
.cfg-roll-seg { flex: 1 1 0; }
.cfg-roll-on { background: var(--honey); }
.cfg-roll-old { background: var(--ink-3); }
.cfg-roll-never { background: var(--bg-elev); }
.cfg-roll-rows { display: flex; flex-direction: column; gap: 6px; font-size: 13.5px; color: var(--ink-2); }
.cfg-roll-row { display: flex; justify-content: space-between; gap: 12px; }
.cfg-roll-n { font-variant-numeric: tabular-nums; }
.cfg-roll-link { align-self: flex-start; }
@media (max-width: 760px) {
  .config-grid { grid-template-columns: minmax(0, 1fr); }
  .cfg-aside { max-width: none; }
  .config-index { position: static; }
}
/* ---------- The audit log (M4-0376; views/audit.js) ----------
   Console v3's Organisation › Audit log: one card, the filter row on top, the
   When / Who / What table flush to the card's edges, and a footer with the
   order note and "Load older". */
.audit-card { padding: 0; overflow: hidden; }
.audit-filters { display: flex; flex-wrap: wrap; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--line); }
.audit-filters .select { flex: 0 1 220px; width: auto; min-width: 0; }
.audit-card .table-wrap { border: 0; border-radius: 0; }
.audit-body > .skel-row, .audit-body > div > .skel-row { margin: 12px 14px; }
.audit-body > .empty, .audit-body > .errorbox, .audit-body > .notice { margin: 16px 14px; }
.audit-foot { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 12px 14px; border-top: 1px solid var(--line); }
.audit-foot:empty { display: none; }
@media (max-width: 760px) {
  .audit-filters .select { flex: 1 1 100%; }
}

/* ---------- Activity and Trips side panels (M4-0380; D129, Activity.dc.html,
   Trips.dc.html) ----------
   Selecting a row opens its panel BESIDE the table; the two share the row while
   they fit (the table first, the panel up to 400 px) and the panel wraps under
   the table only when they cannot. The reason chips sit above the table. */
.ac-split { display: flex; flex-wrap: wrap; gap: var(--s-6); align-items: flex-start; }
.ac-main { flex: 999 1 520px; min-width: 0; }
.ac-panel-host { flex: 1 1 320px; min-width: 0; max-width: 400px; }
.ac-panel-host:empty { display: none; }
.ac-row, .tp-row { cursor: pointer; }
.ac-row:focus-visible, .tp-row:focus-visible { outline: 2px solid var(--accent-text); outline-offset: -2px; }
.ac-panel, .tp-panel { display: flex; flex-direction: column; gap: var(--s-5); padding: var(--s-6); min-width: 0; }
.ac-panel-head, .tp-panel-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.tp-panel-title { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; min-width: 0; }
.tp-panel-title .h3, .ac-panel-head .h3 { margin: 0; overflow-wrap: anywhere; }
.ac-held {
  display: flex; flex-direction: column; gap: 4px; padding: 12px; border-radius: var(--r-md);
  background: var(--red-soft); font-size: var(--t-sm);
}
.ac-held strong { color: var(--red); font-weight: 600; }
.ac-mini-wrap { display: flex; flex-direction: column; gap: 6px; }
.ac-mini, .tp-route {
  display: block; width: 100%; height: auto; border-radius: var(--r-md);
  border: 1px solid var(--line); background: var(--bg-sunk);
}
.ac-mini-note { margin: 0; }
.ac-kv.kv-left { grid-template-columns: 96px minmax(0, 1fr); }
.ac-kv .id-chip { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.ac-panel-links, .tp-panel-links { flex-wrap: wrap; gap: 8px; }
.tp-route-wrap { display: flex; flex-direction: column; gap: 6px; }
.tp-route-wrap > p { margin: 0; }
.tp-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.tp-stat { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tp-stat-value { font-family: var(--f-display); font-size: 18px; font-weight: 600; font-variant-numeric: tabular-nums; }

/* v3's line above the Activity table (the zone, the chips) and its foot (how
   far the shown rows reach, Load older), D137. */
.ac-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; margin-bottom: var(--s-5); }
.ac-bar[hidden] { display: none; }
.ac-zone { margin: 0; }
.ac-foot[hidden] { display: none; }
.ac-more { margin-left: auto; }
.ac-more:empty { display: none; }
.tp-who { display: block; font-size: var(--t-xs); }
/* Activity's Device cell (M4-0411, G2-21): the device's name, its short id beneath. */
.ac-dev { display: inline-flex; flex-direction: column; min-width: 0; }
/* The Map card's heading: the device's name (G2-13). */
.map-card-title { margin: 0 0 2px; overflow-wrap: anywhere; }
.tp-chip { display: inline-flex; align-items: center; gap: 5px; padding: 2px 9px; border-radius: var(--r-pill); font-size: var(--t-xs); font-weight: 500; white-space: nowrap; background: var(--bg-sunk); color: var(--ink-2); }
.tp-chip-live { background: var(--green-soft); color: var(--green); }
.tp-chip-live::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.ac-actions { flex-wrap: wrap; justify-content: flex-end; }

/* ---------- Locations v4, the table (M4-0474, D173; Locations.dc.html) ----------
   The filter row: the window's segmented control, then each filter with its
   label beside it; the table in a card; the right column with the window's
   card and "Why points get filtered" (the point panel takes the column while
   it is open). Controls are the kit's; these are layout only. */
.activity .activity-controls { padding: 0; border: 0; background: none; align-items: center; gap: 8px 12px; }
.activity .activity-ctl { flex-direction: row; align-items: center; gap: 8px; }
.activity .activity-ctl > .input { width: 160px; }
.activity .activity-custom { align-items: flex-end; }
.activity .activity-custom .activity-ctl { flex-direction: column; align-items: stretch; }
.ac-split { flex-wrap: wrap; gap: 16px; }
.ac-main { flex: 999 1 560px; }
.ac-side { flex: 1 1 300px; max-width: 360px; min-width: 0; display: flex; flex-direction: column; gap: 16px; }
.ac-side > .ac-panel-host { flex: none; max-width: none; }
.ac-side:has(.ac-panel-host > *) > .ac-sums { display: none; }
.ac-sums { display: flex; flex-direction: column; gap: 16px; }
.ac-sums[hidden] { display: none; }
.ac-card { padding: 8px 20px; }
.ac-card > .ac-bar { margin: 8px 0 0; }
.ac-card > .pager { padding-bottom: 10px; }
.ac-sum { display: flex; flex-direction: column; gap: 10px; }
.ac-keptbar { display: flex; gap: 2px; height: 8px; border-radius: var(--r-pill); overflow: hidden; }
.ac-keptbar[hidden] { display: none; }
.ac-keptbar-kept { background: var(--honey); }
.ac-keptbar-filtered { background: var(--red); }
.ac-keptbar > span[style^="flex:0 "] { display: none; }
.ac-perm { margin: 0; border-top: 1px solid var(--line); padding-top: 10px; }
.ac-why b { font-weight: 500; color: var(--ink); }
.ac-why-counts { display: flex; flex-direction: column; gap: 6px; font-size: 13.5px; }
.ac-why-counts[hidden] { display: none; }
.ac-why-row { display: flex; justify-content: space-between; gap: 12px; }
.ac-why > .btn { align-self: flex-start; margin-top: 4px; }
.ac-status { display: inline-flex; flex-direction: column; }
.ac-where { font-size: var(--t-xs); white-space: nowrap; }
.activity-table td:first-child { white-space: nowrap; }
.tbl.activity-table tr.session-break td { height: auto; padding: 6px 12px; font-size: var(--t-xs); color: var(--ink-3); background: var(--bg-sunk); }
/* A phone reflows the points to cards (PhoneActivity; the grid of lines is
   with the other phone rows above): no header row, nothing wider than the page. */
@media (max-width: 767.98px) {
  .tbl.activity-table, .tbl.activity-table tbody, .tbl.activity-table tr, .tbl.activity-table td { display: block; width: 100%; }
  .tbl.activity-table thead { display: none; }
  .tbl.activity-table tbody tr { border-bottom: 1px solid var(--line); }
  /* Each cell names its column (the reflow's caption, as .table's), shown
     only where a row is not laid out as the grid of lines. */
  .tbl.activity-table td::before { content: attr(data-label); display: block; font-size: var(--t-xs); color: var(--ink-3); }
  .tbl.activity-table tr.session-break td { padding: 6px 4px; border-radius: 0; overflow-wrap: anywhere; }
  .ac-side { max-width: none; }
}

/* ---------- Users tab and the user page (M4-0424; D139, D142) ---------- */

/* The list reuses the devices-v3 chips, search bar and footer; the view renders
   the table or the phone list (never both), so the phone list is a plain block. */
.user-phone-list { list-style: none; margin: 0; padding: 0; }
.users-anon-note { display: block; margin-top: var(--s-3); }

/* The user page. */
.user-head { display: flex; flex-direction: column; gap: var(--s-2); }
.user-head-row { display: flex; align-items: center; gap: var(--s-4); flex-wrap: wrap; }
.user-content { display: flex; flex-direction: column; gap: var(--s-6); }
.user-seen { display: flex; gap: var(--s-3); flex-wrap: wrap; color: var(--ink-3); }
.user-actions { flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-4); }
.user-extras { margin-top: var(--s-3); }
.extra-chips { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-2); }
.extra-chip { display: inline-flex; gap: 6px; align-items: baseline; }
.extra-chip .extra-key { font-family: var(--f-mono); color: var(--ink-3); }
.extra-chip .extra-val { font-weight: 500; overflow-wrap: anywhere; }

/* The four KPI tiles: four in a row, two below 768 px (device-kpis' shape). */
.user-kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-5); }
.user-kpis .kpi { padding: 14px 16px; min-width: 0; border: 1px solid var(--line); border-radius: var(--r-card); background: var(--bg-card); }
.user-kpis .stat-value { font-size: 20px; font-weight: 600; letter-spacing: -0.02em; margin-top: var(--s-2); overflow-wrap: anywhere; }

.user-section { }
.user-subhead { font-size: var(--t-h3); margin: var(--s-5) 0 var(--s-3); }
.user-device-list, .user-also-list { display: flex; flex-direction: column; gap: var(--s-3); }
.user-device-list li, .user-also-list li { display: flex; align-items: baseline; gap: var(--s-3); flex-wrap: wrap; }

/* Where they've been (M4-0436): the window's name over the minimap, the
   window's trips under it. */
.user-map-window { margin: var(--s-4) 0 var(--s-3); font-weight: 500; }
.user-map-trips .link-list a { justify-content: space-between; }
.user-map-note { margin-top: var(--s-4); }

.user-place-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-3); }
.user-place { display: flex; align-items: baseline; gap: var(--s-3); flex-wrap: wrap; }
.user-place .place-kind { font-weight: 600; }
.user-place .place-fence { color: var(--ink-2); overflow-wrap: anywhere; }

.user-month-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-2); }
.user-month-list li { display: flex; justify-content: space-between; gap: var(--s-4); padding: var(--s-2) 0; border-bottom: 1px solid var(--line); }
.user-month-list li:last-child { border-bottom: none; }

@media (max-width: 767.98px) {
  /* The list is phone rows, not a table (audit §5.4; the view renders one). */
  .user-phone-row { border-bottom: 1px solid var(--line); }
  .user-phone-row:last-child { border-bottom: none; }
  .user-phone-link { display: flex; flex-direction: column; gap: 4px; padding: var(--s-4) var(--s-2); min-height: 56px; text-decoration: none; color: inherit; }
  .user-phone-top { display: flex; align-items: center; gap: var(--s-3); justify-content: space-between; }
  .user-phone-id { font-weight: 500; }
  .user-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Device detail loading (M4-0443, views/device.js): the KPI cards' value bar
   at the value's size; the tab bars on the strip's line. */
.device-kpis .skel-tile > .skel + .skel { height: 22px; margin-top: var(--s-2); }
.device-card > .skel:first-child { height: 1.1em; margin-bottom: var(--s-5); }
.device-tabs > .skel-tabs { border-bottom: 0; }

/* ---------- Users & devices v4 (M4-0475, D173; Users.dc.html, Devices.dc.html) ----------
   The list (search, chips, the table card) with the selected row's panel
   beside it from 1200 px; below that the panel is not drawn and a row opens
   its page. The components are the kit's (.chip, .badge, .tbl, .pager,
   .alert, .btn-*); these rules are the page's own layout. */
.fleet-v4 { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-6); align-items: start; }
.fleet-main { display: flex; flex-direction: column; gap: var(--s-6); min-width: 0; }
.fleet-panel { display: none; }
@media (min-width: 1200px) {
  .fleet-v4 { grid-template-columns: minmax(0, 1fr) minmax(300px, 360px); }
  /* Scrolls inside itself when taller than the window, so its last actions
     (Open user page, Erase…) are reachable without scrolling the whole list
     to its end (M4-0483: a 757 px tall window hid them). */
  .fleet-panel { display: flex; flex-direction: column; gap: var(--s-6); min-width: 0; position: sticky; top: calc(var(--topbar-h) + var(--s-5));
    max-height: calc(100vh - var(--topbar-h) - 2 * var(--s-5)); overflow-y: auto; overscroll-behavior: contain; }
  .fleet-panel:empty { display: none; }
}

/* ---------- Trips v4 (M4-0476; D173, Trips.dc.html) ----------
   Layout only: every control is the kit's (end of this file). The filter row
   sits bare on the page; three summary cards; the table in its card with the
   pager footer; on the right the trip's panel over "When a trip ends". */
.trips .trips-controls { padding: 0; border: 0; background: transparent; align-items: center; gap: 8px 12px; margin-bottom: var(--s-6); }
.trips-filter { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
.trips-filter > .input { width: 160px; max-width: 100%; }
.trips .activity-custom > .field { min-width: 0; }
.trips-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 16px; }
.trips-stats[hidden] { display: none; }
.trips-stat { padding: 16px 18px; min-width: 0; }
.trips-stat-label { font-size: 13.5px; color: var(--ink-2); }
.trips-stats .stat-num { font-size: 28px; margin-top: 6px; }
.trips-stats .stat-num-sub { font-size: 17px; }
.trips-list { padding: 6px 8px; min-width: 0; }
.trips-list .tbl-wrap { max-width: 100%; contain: inline-size; }
.trips-table td { white-space: nowrap; }
.trips-who { display: inline-flex; flex-direction: column; min-width: 0; }
.trips-pager { padding: 14px 12px 10px; }
.trips-pager[hidden] { display: none; }
.trips-count { color: var(--ink-2); }
.trips-foot-note { flex-basis: 100%; font-size: var(--t-xs); color: var(--ink-3); }
.trips-side { flex: 1 1 300px; max-width: 360px; min-width: 0; display: flex; flex-direction: column; gap: 16px; }
.trips-side > .ac-panel-host { flex: none; max-width: none; }
.trips-ends { display: flex; flex-direction: column; gap: 8px; }
.trips-ends > .btn { align-self: flex-start; margin-top: 4px; }
.trips-ends-body { display: flex; flex-direction: column; gap: 8px; }
.trips-ends-value { font-weight: 500; color: var(--ink); }
.tp-panel .tp-sub { font-size: 13.5px; color: var(--ink-3); }
.tp-panel .tp-stat-label { font-size: var(--t-xs); color: var(--ink-3); }
.tp-note { margin: 0; font-size: var(--t-xs); color: var(--ink-3); }
.tp-kv { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 8px 14px; margin: 0; padding: 0 4px; font-size: 13.5px; align-items: center; }
.tp-kv dt { color: var(--ink-3); }
.tp-kv dd { margin: 0; text-align: right; overflow-wrap: anywhere; }
.tp-kv .mono { font-size: var(--t-xs); }
@media (max-width: 767.98px) {
  .trips-side { max-width: none; flex-basis: 100%; }
  .trips-filter { flex: 1 1 100%; }
  .trips-filter > .input { flex: 1 1 auto; width: auto; }
}

/* v4 kit ---------------------------------------------------------------------
   Console v4's component kit (D173, M4-0484): shadcn/ui's look and behaviour
   in the console's own CSS, one block, with the class names of the canvas's
   Components.dc.html (between its kit:start and kit:end) so a screen copies
   markup from the canvas as it stands. lib/ui.js builds each one accessibly.
   Colours are tokens only, so every component follows the dark theme; the
   canvas's hex values approximate them. Two deliberate differences from the
   canvas: the focus ring is --focus-ring (a surface gap and a 2 px accent ring,
   7.45:1 / 8.12:1 — the canvas's 35% honey wash is the 1.2:1 ring the
   2026-10-15 audit removed, G5-03), and the phone sizes start below 768 px,
   the console's phone breakpoint, rather than the canvas's 600.
   It sits last so it wins over the older generic rules; a page's own compound
   selectors still win over it. */

.sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.fleet-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); margin: 0; }
.fleet-toolbar[hidden] { display: none; }
.fleet-chips { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.fleet-chips .chip-count { font-variant-numeric: tabular-nums; }
.fleet-list { padding: var(--s-3) var(--s-7); }
.fleet-list .tbl-wrap { overflow-x: auto; }
.fleet-list .pager[hidden] { display: none; }
.fleet-list .device-list-note { flex: 1 1 100%; }
.fleet-list .cell-link.user-name { display: inline-flex; align-items: center; gap: 10px; font-weight: 500; }
.fleet-avatar {
  width: var(--av, 32px); height: var(--av, 32px); flex: none; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--bg-sunk); color: var(--ink-2);
  font-family: var(--f-body); font-size: calc(var(--av, 32px) * 0.38); font-weight: 600;
}
.fleet-avatar-honey { background: var(--honey-soft); color: var(--accent-text); }
.fleet-avatar-attention { background: var(--red-soft); color: var(--red); }
.user-status { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 2px; }
.user-status-why { font-size: var(--t-xs); color: var(--ink-3); }
.config-behind { color: var(--accent-text); }
.config-failed { color: var(--red); }

/* The panel: one card for the selected row (and Users' reading notes). */
.fleet-panel-host { display: contents; }
.fleet-panel-card { display: flex; flex-direction: column; gap: var(--s-6); min-width: 0; }
.fleet-panel-head { display: flex; align-items: center; gap: var(--s-5); min-width: 0; }
.fleet-panel-name { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.fleet-panel-name .card-title { margin: 0; overflow-wrap: anywhere; }
.fleet-panel-sub { font-size: var(--t-sm); color: var(--ink-3); overflow-wrap: anywhere; }
.fleet-tile-icon {
  width: 48px; height: 48px; flex: none; border-radius: var(--r-lg);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--bg-sunk); color: var(--ink-2);
}
.fleet-tile-icon.is-attention { background: var(--red-soft); color: var(--red); }
.fleet-tracked { margin: 0; font-size: var(--t-sm); color: var(--ink-3); }
.fleet-tracked.is-tracked { color: var(--green); }
.fleet-tracked:empty { display: none; }
.fleet-map-box .minimap-wrap, .fleet-map-box .mm-host { border-radius: var(--r-lg); }
.fleet-map-box .minimap { max-height: 180px; }
.fleet-map-box .minimap-foot:empty { display: none; }
.fleet-tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-4); }
.fleet-tile { min-width: 0; padding: 12px 14px; border-radius: var(--r-lg); background: var(--bg-sunk); }
.fleet-tile-label { font-size: var(--t-xs); color: var(--ink-3); }
.fleet-tile-value { margin-top: 2px; font-size: 18px; letter-spacing: -0.01em; overflow-wrap: anywhere; }
.fleet-tile-note { font-size: var(--t-sm); color: var(--ink-3); }
.fleet-actions { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.fleet-actions > .btn { flex: 1 1 0; }
.fleet-links { padding-top: var(--s-5); border-top: 1px solid var(--line); }
.fleet-links > .btn { flex: 0 1 auto; }
.fleet-danger { display: flex; flex-wrap: wrap; gap: var(--s-3); padding-top: var(--s-5); border-top: 1px solid var(--line); }
.fleet-danger > .btn { flex: 1 1 0; }
.fleet-danger[hidden] { display: none; }
.fleet-note { margin: 0; font-size: var(--t-xs); line-height: 1.5; color: var(--ink-3); }
.fleet-note:empty { display: none; }
.fleet-page-link { align-self: flex-start; }
.btn.btn-wrap { height: auto; min-height: 40px; padding-block: 8px; white-space: normal; line-height: 1.3; text-align: left; }
.fleet-facts {
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 8px 14px; margin: 0;
  padding-top: var(--s-5); border-top: 1px solid var(--line); font-size: var(--t-sm);
}
.fleet-facts dt { color: var(--ink-3); }
.fleet-facts dd { margin: 0; min-width: 0; text-align: right; overflow-wrap: anywhere; }
.fleet-facts .health-cell { align-items: flex-end; }
.fleet-alert-body { display: flex; flex-direction: column; gap: var(--s-3); min-width: 0; }
.fleet-alert-body p { margin: 0; }
.fleet-error { font-size: var(--t-xs); line-height: 1.5; color: var(--ink-2); overflow-wrap: anywhere; }
.fleet-fix { display: flex; flex-direction: column; gap: var(--s-3); }
.fleet-fix p { margin: 0; font-size: var(--t-sm); line-height: 1.5; color: var(--ink-2); }
.fleet-fix-title { font-weight: 500; }
.fleet-code {
  display: block; padding: 12px 14px; border-radius: var(--r-lg);
  background: var(--ink); color: var(--bg); font-family: var(--f-mono); font-size: var(--t-sm);
  overflow-wrap: anywhere; user-select: all;
}
.fleet-reading p { margin: var(--s-3) 0 0; font-size: var(--t-sm); line-height: 1.55; color: var(--ink-2); }
.device-error-fix { margin-top: var(--s-5); }
.device-error-fix > p { margin: 0 0 var(--s-3); }

/* ---- the project Overview v4 (M4-0473, D173; Main.dc.html) ----------------- */
.ov-page { display: flex; flex-direction: column; gap: 16px; }
.ov-top { display: flex; justify-content: flex-end; }
.ov-asof { margin: 0; font-size: 13.5px; color: var(--ink-3); }
.ov-status { padding: 4px 4px 2px; }
.ov-headline { margin: 0; font-size: 28px; font-weight: 400; letter-spacing: -0.02em; }
.ov-headline:focus { outline: none; }
.ov-status-line { margin: 6px 0 0; font-size: 14.5px; color: var(--ink-2); }
.ov-body { display: flex; flex-direction: column; gap: 16px; }
.ov-layout { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start; }
.ov-main > section + section { margin-top: 0; }
.ov-main { flex: 999 1 560px; min-width: 0; display: flex; flex-direction: column; gap: 16px; }
.ov-aside { flex: 1 1 320px; max-width: 380px; min-width: 0; display: flex; flex-direction: column; gap: 16px; }
.ov-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; }
.ov-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; }
.ov-stat { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.ov-stat-label { font-size: 14px; color: var(--ink-2); }
.ov-stat-value { font-size: 40px; font-weight: 400; letter-spacing: -0.03em; line-height: 1; }
.ov-stat-of { font-size: 17px; letter-spacing: 0; color: var(--ink-2); }
.ov-stat-note { font-size: 13.5px; color: var(--ink-3); }
a.ov-card { color: inherit; text-decoration: none; }
a.ov-card:hover { border-color: var(--line-strong); color: inherit; }
a.ov-card:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.ov-meter { display: block; height: 8px; border-radius: var(--r-pill); background: var(--bg-sunk); overflow: hidden; }
.ov-meter-fill { display: block; height: 100%; border-radius: var(--r-pill); background: var(--honey); }
.ov-section-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
.ov-section-note { font-size: 13.5px; font-weight: 400; color: var(--ink-2); margin-left: 6px; }
.ov-head-link { margin-right: -12px; padding: 0 12px; color: var(--accent-text); }
.ov-foot { margin: 8px 0 0; font-size: 12.5px; color: var(--ink-3); }
.ov-empty { margin: 8px 0; font-size: 13.5px; color: var(--ink-3); }
/* Needs attention */
.ov-attention { display: flex; flex-direction: column; gap: 4px; margin: 0; }
.ov-attention:focus { outline: none; }
.ov-attn { list-style: none; margin: 0; padding: 0; }
.ov-attn-item { display: flex; gap: 12px; padding: 12px 0; }
.ov-attn-item + .ov-attn-item { border-top: 1px solid color-mix(in oklch, var(--honey) 30%, transparent); }
.ov-attn-num { flex: 0 0 auto; width: 26px; height: 26px; border-radius: 50%; background: var(--ink); color: var(--bg); font-size: 13.5px; font-weight: 600; display: flex; align-items: center; justify-content: center; }
.ov-attn-num.is-severe { background: var(--red); color: var(--bg-elev); }
.ov-attn-main { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.ov-attn-title { font-size: 14.5px; font-weight: 500; }
.ov-attn-body { font-size: 13.5px; line-height: 1.5; color: var(--ink-2); overflow-wrap: anywhere; }
.ov-attn-body code { font-family: var(--f-mono); font-size: 12.5px; }
.ov-attn-sub { font-size: 12.5px; color: var(--ink-3); }
.ov-attn-action { margin-top: 7px; }
.ov-attn-none { margin: 0; font-size: 14px; color: var(--ink-2); }
/* Points per day (M4-0473 follow-up, D173: the data-viz spec) */
.ov-chart { padding: 20px 20px 16px; display: flex; flex-direction: column; gap: 4px; }
.ov-chart-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px 12px; }
.ov-chart-head .ov-head-link { white-space: nowrap; }
.ov-chart-sub { margin: 0 0 8px; font-size: 13.5px; color: var(--ink-3); }
.ov-chart-total { color: var(--ink); font-weight: 500; }
/* The day-by-day chart (lib/barchart.js, M4-0483, D173): the Overview's
   Points per day and every Usage chart. Series colours are set as --bc by
   class, so a mark, its hatch and its legend swatch share one value. */
.bc-s-bar { --bc: var(--chart-bar); }
.bc-s-alt { --bc: var(--series-2); }
.bc-s-bad { --bc: var(--red); }
.bc-box { overflow-x: auto; overflow-y: hidden; contain: inline-size; }
.bc-plot { position: relative; }
.bc-svg { position: absolute; inset: 0 auto auto 0; display: block; pointer-events: none; overflow: visible; }
.bc-svg text { font-family: var(--f-body); }
.bc-axis { font-size: 12px; fill: var(--ink-3); font-variant-numeric: tabular-nums; }
.bc-grid { stroke: var(--chart-grid); stroke-width: 1; shape-rendering: crispEdges; }
.bc-baseline { stroke: var(--line); stroke-width: 1; shape-rendering: crispEdges; }
.bc-mark:not(.is-today) { fill: var(--bc); }
.bc-hatch-fill, .bc-swatch-fill { fill: var(--bc); }
.bc-hatch-stripe { fill: var(--bg-elev); fill-opacity: 0.45; }
.bc-line { fill: none; stroke: var(--bc); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
/* A line's markers: today's always (hatched, ringed in its colour), any
   other day's only while its band is hovered or focused. */
.bc-dot { stroke: var(--bg-elev); stroke-width: 2; opacity: 0; }
.bc-dot:not(.is-today) { fill: var(--bc); }
.bc-dot.is-today { stroke: var(--bc); stroke-width: 1.5; opacity: 1; }
.bc-day.is-active .bc-dot { opacity: 1; }
.bc-reset-line { stroke: var(--line); stroke-width: 1; shape-rendering: crispEdges; }
.bc-reset-label { font-size: 11.5px; fill: var(--ink-3); }
.bc-ref-line { stroke: var(--ink-3); stroke-width: 1; shape-rendering: crispEdges; }
.bc-ref-label { font-size: 12px; fill: var(--ink-2); paint-order: stroke; stroke: var(--bg-elev); stroke-width: 4px; stroke-linejoin: round; }
.bc-value {
  font-size: 13px; font-weight: 500; fill: var(--ink); font-variant-numeric: tabular-nums;
  paint-order: stroke; stroke: var(--bg-elev); stroke-width: 4px; stroke-linejoin: round;
}
.bc-sofar { font-weight: 400; fill: var(--ink-3); }
.bc-day-label { font-size: 12.5px; fill: var(--ink-2); }
.bc-day-label.is-today, .bc-day-label.is-active { fill: var(--ink); font-weight: 500; }
.bc-bands { position: absolute; inset: 0; }
.bc-band { position: absolute; top: 0; bottom: 0; outline: none; cursor: default; }
.bc-band::before {
  content: ""; position: absolute; left: max(1px, min(6px, 15%)); right: max(1px, min(6px, 15%)); top: 8px; bottom: 34px;
  border-radius: var(--r-md); background: transparent; transition: background-color var(--dur-fast) var(--ease);
}
.bc-band.is-active::before { background: var(--bg-sunk); }
.bc-band:focus-visible::before { box-shadow: var(--focus-ring); }
.bc-tip { position: absolute; z-index: 5; white-space: nowrap; pointer-events: none; font-size: 13px; font-weight: 400; line-height: 1.45; }
.bc-tip-day { font-weight: 500; }
.bc-tip-note { color: color-mix(in oklch, var(--bg) 78%, var(--ink)); }
.bc-legend { margin: 4px 0 0; display: flex; flex-wrap: wrap; align-items: center; gap: 6px 16px; font-size: 12.5px; color: var(--ink-3); }
.bc-key { display: inline-flex; align-items: center; gap: 6px; }
.bc-swatch { flex: 0 0 auto; }
@media (prefers-reduced-motion: reduce) { .bc-band::before { transition: none; } }
/* Recently active devices, latest events */
.ov-rows { display: flex; flex-direction: column; }
.ov-device-row { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; padding: 12px 0; border-top: 1px solid var(--bg-sunk); color: inherit; text-decoration: none; }
.ov-device-row:hover { color: inherit; background: var(--bg); }
.ov-device-row:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--r-md); }
.ov-device-name { flex: 1 1 auto; min-width: 0; font-size: 14.5px; overflow-wrap: anywhere; }
.ov-device-id { font-size: 12.5px; color: var(--ink-2); }
.ov-device-user { color: var(--ink-3); }
.ov-device-when { font-size: 13.5px; color: var(--ink-2); min-width: 84px; text-align: right; }
.ov-event-row { display: flex; align-items: center; gap: 12px; padding: 12px 0; }
.ov-event-row + .ov-event-row { border-top: 1px solid var(--bg-sunk); }
.ov-event-icon { flex: 0 0 auto; width: 36px; height: 36px; border-radius: 12px; display: flex; align-items: center; justify-content: center; background: var(--bg-sunk); color: var(--ink-2); }
.ov-event-trip .ov-event-icon { background: var(--green-soft); color: var(--green); }
.ov-event-geofence .ov-event-icon { background: var(--honey-soft); color: var(--accent-text); }
.ov-event-billing .ov-event-icon { background: var(--blue-soft); color: var(--blue); }
.ov-event-text { flex: 1 1 auto; min-width: 0; font-size: 14.5px; overflow-wrap: anywhere; }
.ov-event-type { color: var(--ink-3); }
.ov-event-when { font-size: 13.5px; color: var(--ink-2); text-align: right; }
@media (max-width: 767px) {
  .ov-aside { max-width: none; }
  .ov-stat-value { font-size: 34px; }
}
