/* ==========================================================================
   tasmofleet stylesheet, design v2.
   Tokens, fonts and metrics are the Claude Design v2 prototype's (binding
   source: design/v2/README.md, docs/spec/design-system.md),
   with the app's --color- prefix. Values are copied verbatim, oklch() included;
   there is no ramp generator.

   Theme selection: data-theme="light|dark" on <html> (theme.js resolves
   "system" from prefers-color-scheme); data-theme-auto is the no-JS fallback.
   Token names are the same in both themes.
   ========================================================================== */

/* ---- fonts: self-hosted Geist and Geist Mono (variable wght, SIL OFL 1.1, geist-OFL.txt) */
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("geist-latin-ext.woff2") format("woff2");
  unicode-range:
    U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
    U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("geist-latin.woff2") format("woff2");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
    U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("geist-mono-latin-ext.woff2") format("woff2");
  unicode-range:
    U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
    U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("geist-mono-latin.woff2") format("woff2");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
    U+2215, U+FEFF, U+FFFD;
}

/* ---- tokens that do not depend on the theme ----------------------------- */
:root {
  /* type */
  --font-sans: "Geist", ui-sans-serif, system-ui, sans-serif;
  --font-mono: "Geist Mono", ui-monospace, Menlo, monospace;
  --fs-base: 14px; /* body */
  --fs-h1: 22px; /* page title */
  --fs-h2-lg: 15px; /* page section and card headings */
  --fs-h2: 14px; /* sub headings */
  --fs-h2-sm: 13px; /* headings inside small cards */
  --fs-body: 13.5px; /* list rows, form text, controls */
  --fs-ui: 13px; /* buttons, nav, tabs, table cells */
  --fs-meta: 12.5px; /* secondary lines, crumbs */
  --fs-small: 12px; /* hints, field help, table headers */
  --fs-badge: 11.5px; /* badges, counters, pills */
  --fs-stat: 28px; /* stat tile number: 600 28px/1, tabular (measured in WP F4) */
  --lh-tight: 1.15; /* @kind other */
  --lh-body: 1.45; /* @kind other */
  --grid-cols: 12; /* @kind other */

  /* space: the prototype is not on a 4px grid; one token per pixel value its markup uses */
  --sp-1: 1px;
  --sp-2: 2px;
  --sp-3: 3px;
  --sp-4: 4px;
  --sp-5: 5px;
  --sp-6: 6px;
  --sp-7: 7px;
  --sp-8: 8px;
  --sp-9: 9px;
  --sp-10: 10px;
  --sp-11: 11px;
  --sp-12: 12px;
  --sp-14: 14px;
  --sp-16: 16px;
  --sp-18: 18px;
  --sp-20: 20px;
  --sp-22: 22px;
  --sp-24: 24px;
  --sp-28: 28px;
  --sp-32: 32px;
  --sp-36: 36px;
  --sp-40: 40px;

  /* shape */
  --r-control: 7px; /* buttons, inputs, selects, segmented controls */
  --r-card: 10px; /* sections, cards, tables */
  --card-gap: var(
    --sp-12
  ); /* between sibling cards, both ways (device overview, pages of cards) */
  --r-panel: 12px; /* auth card, dialogs, menus */
  --r-banner: 9px;
  --r-code: 8px; /* pre, console, diff */
  --r-sm: 6px;
  --r-badge: 5px;
  --r-xs: 4px;
  --r-pill: 999px;
  --hair-w: 1px; /* borders and dividers */

  /* controls: one height per size */
  --ctl: 34px;
  --ctl-sm: 32px;
  --ctl-lg: 40px;
  --ctl-pad: 14px;
  --ctl-pad-sm: 10px;

  /* layout; --page-pad: without a size container, cqw is the viewport width with the scrollbar,
     which is what the prototype's root measures (its scrollbar sits inside the root) */
  --gutter: var(--sp-16);
  --container: 1640px;
  --page-pad: clamp(12px, 2.4cqw, 32px);

  /* layers, lowest to highest: the only z-index scale. Page content has none (auto). Values of 1 to 4
     inside one component (pinned cells, column handles) stay local and lie below --z-sticky.
     header.top is a stacking context of its own, so everything that must lie over the page content
     below the header (menus of the toolbar, rows and tables) has to rank under --z-header, and
     the header's own menus (--z-header-menu) only compete inside it. */
  --z-sticky: 10; /* @kind other: sticky table parts, bulk bar, save bar */
  --z-menu: 20; /* @kind other: toolbar and row menus, tooltips */
  --z-header: 30; /* @kind other: the top bar and the phone action bar */
  --z-header-menu: 40; /* @kind other: the top bar's menus (user menu) */
  --z-dialog: 1000; /* @kind other: dialogs and drawers (their own top layer, kept for fallbacks) */
  --z-toast: 1001; /* @kind other: toasts */

  /* values the prototype writes inline, the same in both themes */
  --backdrop: rgba(10, 14, 18, 0.5);
  --shadow-knob: 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow-bar: 0 -6px 20px rgba(0, 0, 0, 0.08);
  --color-console-rule: rgba(255, 255, 255, 0.08);
  --color-console-line: rgba(255, 255, 255, 0.14);
}

/* ---- light theme (default) --------------------------------------------- */
:root,
[data-theme="light"] {
  color-scheme: light;
  --color-bg: #f4f5f7;
  --color-surface: #fff;
  --color-surface-2: #f0f2f4;
  --color-surface-3: #e5e8ec;
  --color-border: #dfe3e7;
  --color-border-strong: #c2c9d0;
  --color-text: #13171c;
  --color-text-2: #48535e;
  --color-text-3: #5c6772;
  --color-accent: oklch(0.5 0.13 250);
  --color-accent-hover: oklch(0.44 0.13 250);
  --color-accent-bg: oklch(0.95 0.025 250);
  --color-accent-fg: oklch(0.45 0.13 250);
  --color-on-accent: #fff;
  --color-ok: oklch(0.63 0.14 155);
  --color-ok-fg: oklch(0.44 0.1 155);
  --color-ok-bg: oklch(0.95 0.035 155);
  --color-warn: oklch(0.76 0.14 75);
  --color-warn-fg: oklch(0.47 0.1 65);
  --color-warn-bg: oklch(0.96 0.045 85);
  --color-danger: oklch(0.58 0.19 27);
  --color-danger-fg: oklch(0.49 0.17 27);
  --color-danger-bg: oklch(0.96 0.025 27);
  --color-danger-solid: oklch(0.52 0.19 27);
  --color-neutral: #98a1ab;
  --color-neutral-fg: #4f5964;
  --color-neutral-bg: #eceff2;
  --color-term-bg: #12161b;
  --color-term-fg: #d3d9df;
  --color-term-dim: #8b95a1;
  --color-term-cmd: #93c3ff;
  --color-term-err: #ff9d8f;
  --color-grip-hover: color-mix(in oklab, var(--color-accent) 30%, transparent);
  --shadow-lg:
    0 14px 44px rgba(16, 24, 32, 0.18), 0 2px 6px rgba(16, 24, 32, 0.08);
}

/* ---- dark theme: system preference without JS -------------------------
   theme.js resolves "system" to data-theme="light|dark" and removes
   data-theme-auto; pages put data-theme-auto on <html> as the no-JS fallback.
   The --color-term-* tokens other than the background are not redefined. */
@media (prefers-color-scheme: dark) {
  :root[data-theme-auto] {
    color-scheme: dark;
    --color-bg: #0d1013;
    --color-surface: #14181c;
    --color-surface-2: #1a1f24;
    --color-surface-3: #242a31;
    --color-border: #262c33;
    --color-border-strong: #3a424b;
    --color-text: #e4e7ea;
    --color-text-2: #a9b2bb;
    --color-text-3: #939da7;
    --color-accent: oklch(0.7 0.12 250);
    --color-accent-hover: oklch(0.77 0.11 250);
    --color-accent-bg: oklch(0.27 0.05 250);
    --color-accent-fg: oklch(0.8 0.1 250);
    --color-on-accent: #0b1015;
    --color-ok: oklch(0.72 0.15 155);
    --color-ok-fg: oklch(0.8 0.13 155);
    --color-ok-bg: oklch(0.27 0.05 155);
    --color-warn: oklch(0.8 0.14 80);
    --color-warn-fg: oklch(0.85 0.12 85);
    --color-warn-bg: oklch(0.29 0.05 75);
    --color-danger: oklch(0.68 0.18 25);
    --color-danger-fg: oklch(0.8 0.13 25);
    --color-danger-bg: oklch(0.29 0.07 25);
    --color-danger-solid: oklch(0.55 0.17 25);
    --color-neutral: #6b7581;
    --color-neutral-fg: #b0b8c1;
    --color-neutral-bg: #242a31;
    --color-term-bg: #090c0f;
    --color-grip-hover: color-mix(
      in oklab,
      var(--color-accent) 30%,
      transparent
    );
    --shadow-lg: 0 14px 44px rgba(0, 0, 0, 0.6);
  }
}
/* ---- dark theme: explicit override ------------------------------------- */
:root[data-theme="dark"],
[data-theme="dark"] {
  color-scheme: dark;
  --color-bg: #0d1013;
  --color-surface: #14181c;
  --color-surface-2: #1a1f24;
  --color-surface-3: #242a31;
  --color-border: #262c33;
  --color-border-strong: #3a424b;
  --color-text: #e4e7ea;
  --color-text-2: #a9b2bb;
  --color-text-3: #939da7;
  --color-accent: oklch(0.7 0.12 250);
  --color-accent-hover: oklch(0.77 0.11 250);
  --color-accent-bg: oklch(0.27 0.05 250);
  --color-accent-fg: oklch(0.8 0.1 250);
  --color-on-accent: #0b1015;
  --color-ok: oklch(0.72 0.15 155);
  --color-ok-fg: oklch(0.8 0.13 155);
  --color-ok-bg: oklch(0.27 0.05 155);
  --color-warn: oklch(0.8 0.14 80);
  --color-warn-fg: oklch(0.85 0.12 85);
  --color-warn-bg: oklch(0.29 0.05 75);
  --color-danger: oklch(0.68 0.18 25);
  --color-danger-fg: oklch(0.8 0.13 25);
  --color-danger-bg: oklch(0.29 0.07 25);
  --color-danger-solid: oklch(0.55 0.17 25);
  --color-neutral: #6b7581;
  --color-neutral-fg: #b0b8c1;
  --color-neutral-bg: #242a31;
  --color-term-bg: #090c0f;
  --color-grip-hover: color-mix(in oklab, var(--color-accent) 30%, transparent);
  --shadow-lg: 0 14px 44px rgba(0, 0, 0, 0.6);
}

/* ==========================================================================
   Base
   ========================================================================== */
*,
*::before,
*::after {
  box-sizing: border-box;
}
html {
  -webkit-text-size-adjust: 100%;
  /* The scrollbar's room is kept on every page, so nothing moves sideways between a short and a long
     page (v0.10.1; 0.10.0 had dropped it for the empty strip it leaves right of the top bar on short
     pages with classic scrollbars, and the whole layout jumped on every page switch instead).
     That strip is clipped away from the header and the fixed status bar (they end at its left edge);
     only the canvas colour paints there. On a page that does not scroll it used to show the page
     colour, so the bars looked as if they ended ~15px before the window edge (#395). It is the bars'
     colour now, and body (page colour) fills the window height so only the strip stays that colour. */
  scrollbar-gutter: stable;
  background: var(--color-surface);
}
body {
  min-height: 100dvh;
  padding-bottom: calc(var(--sb-h) + var(--sp-16));
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font: 400 var(--fs-base) / var(--lh-body) var(--font-sans);
  -webkit-font-smoothing: antialiased;
}
h1,
h2,
h3,
h4 {
  font-family: var(--font-sans);
  line-height: var(--lh-tight);
  letter-spacing: -0.015em;
  margin: 0 0 var(--sp-12);
}
h1 {
  font-size: var(--fs-h1);
  font-weight: 600;
}
h2 {
  font-size: var(--fs-h2-lg);
  font-weight: 600;
}
h3 {
  font-size: var(--fs-h2);
  font-weight: 600;
}
h4 {
  font-size: var(--fs-h2-sm);
  font-weight: 600;
}
p {
  margin: 0 0 var(--sp-12);
}
a {
  color: var(--color-accent-fg);
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
}
a:hover,
a.is-hover {
  text-decoration-thickness: 2px;
}
:focus-visible,
.is-focus {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
::selection {
  background: color-mix(in srgb, var(--color-accent) 30%, transparent);
}
small,
.small {
  font-size: var(--fs-small);
}
.muted {
  color: var(--color-text-2);
}
.lead {
  color: var(--color-text-2);
  max-width: 70ch;
}
code,
pre,
kbd {
  font-family: var(--font-mono);
  font-size: 0.92em;
}
code {
  background: var(--color-surface-2);
  padding: 0.05em 0.3em;
  overflow-wrap: anywhere;
}
pre {
  background: var(--color-surface-2);
  border: var(--hair-w) solid var(--color-border);
  padding: var(--sp-12) var(--sp-16);
  overflow-x: auto;
  white-space: pre-wrap;
}
hr,
.hr {
  border: 0;
  border-top: var(--hair-w) solid var(--color-border);
  margin: var(--sp-24) 0;
}
.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;
}
[hidden] {
  display: none !important;
}
.ico {
  /* inline Lucide SVG: stroke="currentColor", stroke-width 2 */
  width: 16px;
  height: 16px;
  flex: none;
  vertical-align: -3px;
}

/* ==========================================================================
   Layout: top bar, page container, page header, columns
   ========================================================================== */

main {
  max-width: var(--container);
  margin: 0 auto;
  padding: var(--sp-16) var(--page-pad) 0;
}

/* Page header: title left, at most one primary action right (more → secondary
   buttons or an Actions menu). The crumb sits above the title. */
.page-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "crumb crumb" "title actions";
  align-items: center;
  gap: var(--sp-4) var(--sp-16);
  margin: 0 0 var(--sp-24);
}
.page-head h1 {
  grid-area: title;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-8) var(--sp-12);
  margin: 0;
  min-height: var(--ctl);
}
.page-head .actions {
  grid-area: actions;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--sp-8);
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-8);
  align-items: center;
}
.actions form {
  margin: 0;
}

/* Columns: 12-column modular grid. */
.grid {
  /* NEW: explicit modular grid, children span with .span-N */
  display: grid;
  grid-template-columns: repeat(var(--grid-cols), minmax(0, 1fr));
  gap: var(--gutter);
}
.span-3 {
  grid-column: span 3;
}
.span-4 {
  grid-column: span 4;
}
.span-6 {
  grid-column: span 6;
}
.span-8 {
  grid-column: span 8;
}
.span-12 {
  grid-column: 1 / -1;
}
.cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0 var(--gutter);
  align-items: start;
}
@media (min-width: 68.75rem) {
  .cols {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .cols.wide-left {
    grid-template-columns: 7fr 5fr;
  }
}

/* ==========================================================================
   Buttons. Labels are centered (compact and in-table buttons); only .block
   buttons (full width) are flush left. One fixed height per size.
   ========================================================================== */
button,
.button {
  font: inherit;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-body);
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: var(--ctl);
  padding: 0 var(--ctl-pad);
  border-radius: var(--r-control);
  border: var(--hair-w) solid var(--color-border-strong);
  background: var(--color-surface);
  color: var(--color-text);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  vertical-align: middle;
}
button:hover,
button.is-hover,
.button:hover,
.button.is-hover {
  background: color-mix(in srgb, var(--color-text) 7%, var(--color-surface));
  border-color: var(--color-text);
}
button:active,
button.is-active,
.button:active,
.button.is-active {
  background: color-mix(in srgb, var(--color-text) 14%, var(--color-surface));
}
button.primary,
.button.primary {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-on-accent);
}
button.primary:hover,
button.primary.is-hover,
.button.primary:hover,
.button.primary.is-hover {
  background: var(--color-accent-hover);
  border-color: var(--color-accent-hover);
}
button.primary:active,
button.primary.is-active,
.button.primary:active,
.button.primary.is-active {
  background: var(--color-accent-hover);
  border-color: var(--color-accent-hover);
}
button.danger,
.button.danger {
  color: var(--color-danger-fg);
  border-color: var(--color-danger);
}
button.danger:hover,
button.danger.is-hover,
.button.danger:hover,
.button.danger.is-hover {
  background: var(--color-danger-bg);
  border-color: var(--color-danger);
}
button.danger:active,
button.danger.is-active,
.button.danger:active,
.button.danger.is-active {
  background: color-mix(in srgb, var(--color-danger) 22%, var(--color-surface));
}
button.ghost,
.button.ghost {
  background: transparent;
  border-color: transparent;
  color: var(--color-accent-fg);
}
button.ghost:hover,
button.ghost.is-hover,
.button.ghost:hover,
.button.ghost.is-hover {
  background: var(--color-surface-2);
  border-color: transparent;
}
button.sm,
.button.sm {
  height: var(--ctl-sm);
  padding: 0 var(--ctl-pad-sm);
  font-size: var(--fs-ui);
}
button.lg,
.button.lg {
  /* NEW: large size, e.g. sign-in */
  height: var(--ctl-lg);
  padding: 0 18px;
}
button.block,
.button.block {
  /* NEW: full-width button, flush-left label (Modernist) */
  display: flex;
  width: 100%;
  justify-content: flex-start;
}
button.icon,
.button.icon {
  /* NEW: icon-only square button; needs aria-label */
  width: var(--ctl);
  padding: 0;
}
button.icon.sm,
.button.icon.sm {
  width: var(--ctl-sm);
}
button[aria-pressed="true"],
.button.is-current {
  background: var(--color-text);
  border-color: var(--color-text);
  color: var(--color-surface);
}
button[disabled],
button[aria-disabled="true"],
.button[aria-disabled="true"],
.button.is-off {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}
button[aria-busy="true"] {
  cursor: progress;
}
button[aria-busy="true"]::after {
  content: "";
  width: 10px;
  height: 10px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%; /* a spinner glyph, not a corner */
  animation: ds-spin 0.8s linear infinite;
}
@keyframes ds-spin {
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  button[aria-busy="true"]::after {
    animation: none;
  }
}

/* ==========================================================================
   Forms
   ========================================================================== */
label {
  display: grid;
  gap: var(--sp-4);
  font-size: var(--fs-ui);
  font-weight: 600;
  color: var(--color-text);
}
label > .lbl {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-4);
}
label small,
.hint {
  font-size: var(--fs-small);
  font-weight: 400;
  color: var(--color-text-2);
}
input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
select,
textarea {
  font: inherit;
  font-weight: 400;
  font-size: var(--fs-body);
  color: var(--color-text);
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: var(--r-control);
  height: var(--ctl);
  padding: 0 10px;
  min-width: 0;
  width: 100%;
}
textarea {
  height: auto;
  min-height: 84px;
  padding: 8px 10px;
  line-height: var(--lh-body);
}
select {
  appearance: none;
  padding-right: 30px;
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position:
    calc(100% - 15px) 50%,
    calc(100% - 10px) 50%;
  background-size: 5px 5px;
  background-repeat: no-repeat;
}
/* the native popup of a select takes its rows' colours from the options, not from the theme: a
   select with a transparent ground (the filter pills) left the popup white under light option
   text in the dark theme (v0.11.1 feedback 3). Every option paints the theme's surface and text. */
option,
optgroup {
  color: var(--color-text);
  background-color: var(--color-surface);
}
input::placeholder,
textarea::placeholder {
  color: var(--color-text-2);
  opacity: 1;
}
input:not([type="checkbox"]):not([type="radio"]):hover,
input:not([type="checkbox"]):not([type="radio"]).is-hover,
select:hover,
select.is-hover,
textarea:hover,
textarea.is-hover {
  border-color: var(--color-text);
}
input:not([type="checkbox"]):not([type="radio"]):focus-visible,
input:not([type="checkbox"]):not([type="radio"]).is-focus,
select:focus-visible,
select.is-focus,
textarea:focus-visible,
textarea.is-focus {
  outline: 2px solid var(--color-accent);
  outline-offset: -1px;
  border-color: var(--color-accent);
}
input[disabled],
select[disabled],
textarea[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}
/* a locked row's checkbox stays readable: the browser's own disabled look is nearly invisible on a light ground */
input[type="checkbox"][disabled] {
  opacity: 1;
  outline: 1px solid var(--color-text-3);
  outline-offset: -1px;
}
/* a locked checked box is drawn by hand: the native one greys its check mark into the ground in the light theme */
input[type="checkbox"][disabled]:checked {
  appearance: none;
  border-radius: 3px;
  outline: 0;
  background-color: var(--color-text-2);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 8.5l2.5 2.5L12 5.5' fill='none' stroke='white' stroke-width='2'/%3E%3C/svg%3E");
  background-size: 100% 100%;
}
input[aria-invalid="true"],
select[aria-invalid="true"],
textarea[aria-invalid="true"] {
  border-color: var(--color-danger);
  box-shadow: inset 3px 0 0 var(--color-danger);
}
input[type="checkbox"],
input[type="radio"] {
  accent-color: var(--color-accent);
  width: 18px;
  height: 18px;
  margin: 0;
}
label.inline {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-8);
  font-weight: 400;
  font-size: var(--fs-body);
}
fieldset {
  /* legend-style frame, same as a card */
  margin: var(--sp-24) 0 var(--sp-16);
  padding: var(--sp-20) var(--sp-16) var(--sp-16);
  border: var(--hair-w) solid var(--color-border);
  border-top: var(--hair-w) solid var(--color-border);
  display: grid;
  gap: var(--sp-12);
  min-width: 0;
}
legend {
  padding: 0 var(--sp-8);
  margin-left: calc(var(--sp-8) * -1);
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: var(--fs-ui);
}
.stack {
  display: grid;
  gap: var(--sp-16);
  max-width: 34rem;
}
.stack.wide {
  max-width: none;
}
/* cards stacked in a .stack: the stack's gap is the only space between them, the standard card gap
   (the cards' own margins would add up to about 70px) */
.stack:has(> section.card) {
  gap: var(--sp-32);
}
.stack > section.card {
  margin-bottom: 0;
}
.stack > section.card:not(:first-child) {
  margin-top: 0;
}
/* the row header of a comparison table keeps its words on one line and carries the row rule like the cells */
@media (min-width: 45rem) {
  table.dense tbody th[scope="row"] {
    white-space: nowrap;
    text-align: left;
    border-bottom: var(--hair-w) solid var(--color-border);
  }
}
.filters label.grow select {
  width: 100%;
}
.filters {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: var(--sp-12);
  margin: 0 0 var(--sp-16);
}
.filters label {
  flex: 1 1 10rem;
  max-width: 16rem;
}
.filters label.grow {
  flex: 3 1 16rem;
  max-width: 28rem;
}
.field-error,
small.error {
  /* field-level error text (small.error exists in app.css) */
  color: var(--color-danger-fg);
  font-size: var(--fs-small);
  font-weight: 600;
}
.hit {
  /* checkbox hit area in tables */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  min-height: 28px;
  cursor: pointer;
}

/* ==========================================================================
   Cards: framed title on the top rule (legend style)
   ========================================================================== */
.card {
  position: relative;
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  padding: var(--sp-24) var(--sp-16) var(--sp-16);
}
section.card {
  margin: var(--sp-32) 0 var(--sp-20);
}
section.card:first-child {
  margin-top: var(--sp-12);
}
.card > h2:first-child,
.card > .card-head,
.card > details.fold.fold-h:first-child > summary {
  position: absolute;
  top: calc(var(--hair-w) / -2);
  left: var(--sp-12);
  transform: translateY(-50%);
  max-width: calc(100% - 2 * var(--sp-12));
  margin: 0;
  padding: 0 var(--sp-8);
  background: var(--color-surface);
  font-family: var(--font-sans);
  font-size: var(--fs-h2-lg);
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.card > .card-head {
  display: flex;
  align-items: center;
  gap: var(--sp-8);
}
.card > *:last-child {
  margin-bottom: 0;
}
.card .card-kicker {
  /* NEW (from Modernist): small uppercase label above content */
  display: block;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-accent-fg);
  margin-bottom: var(--sp-4);
}

/* facts: label/value list (device identity) */
.facts {
  display: grid;
  grid-template-columns: minmax(7rem, max-content) minmax(0, 1fr);
  gap: 0;
  margin: 0;
  font-size: var(--fs-ui);
}
.facts dt,
.facts dd {
  padding: 6px 0;
  border-bottom: var(--hair-w) solid var(--color-border);
  margin: 0;
}
.facts dt {
  color: var(--color-text-2);
  padding-right: var(--sp-16);
}
.facts dt:last-of-type,
.facts dt:last-of-type + dd {
  border-bottom: 0;
}
.facts dd small {
  color: var(--color-text-2);
}

/* ==========================================================================
   Stat tiles (dashboard)
   ========================================================================== */
.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0;
  list-style: none;
  margin: 0 0 var(--sp-12);
  padding: 0;
  border-left: var(--hair-w) solid var(--color-border);
}
.stats > li {
  display: flex;
}
.stat {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  padding: var(--sp-12) var(--sp-16) var(--sp-16);
  color: var(--color-text);
  text-decoration: none;
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-left: 0;
  border-top: 4px solid var(--tone, var(--color-neutral));
}
a.stat:hover,
a.stat.is-hover {
  background: var(--color-surface-2);
}
a.stat:hover .what,
a.stat.is-hover .what {
  color: var(--color-text);
  text-decoration: underline;
}
.stat .num {
  font-family: var(--font-sans);
  font-size: var(--fs-stat);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.03em;
}
.stat .what {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--color-text-2);
  font-size: var(--fs-ui);
  font-weight: 600;
}
.stat .what::before {
  content: attr(data-glyph);
  color: var(--tone-text, var(--color-text-2));
  font-weight: 800;
  width: 1em;
  text-align: center;
}
.stat.zero .num {
  color: var(--color-text-3);
}
.stat.zero {
  border-top-color: var(--color-border);
}
.tone-ok {
  --tone: var(--color-ok);
  --tone-text: var(--color-ok-fg);
}
.tone-warn {
  --tone: var(--color-warn);
  --tone-text: var(--color-warn-fg);
}
.tone-bad {
  --tone: var(--color-danger);
  --tone-text: var(--color-danger-fg);
}
.tone-info {
  --tone: var(--color-accent);
  --tone-text: var(--color-accent-fg);
}
.tone-neutral {
  --tone: var(--color-neutral);
  --tone-text: var(--color-neutral-fg);
}
.summary {
  margin: var(--sp-8) 0 0;
  font-size: var(--fs-ui);
}

/* ==========================================================================
   Status badge and tag. Always glyph + word + colour (CVD safe).
   ========================================================================== */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
  height: 20px;
  padding: 0 7px 0 6px;
  border-radius: var(--r-badge);
  font-size: var(--fs-small);
  font-weight: 700;
  line-height: 1;
  background: var(--color-neutral-bg);
  color: var(--color-neutral-fg);
  border: var(--hair-w) solid transparent;
  vertical-align: middle;
}
.badge::before {
  content: "\2013";
  font-weight: 800;
  width: 0.9em;
  text-align: center;
}
.badge.tone-ok {
  background: var(--color-ok-bg);
  color: var(--color-ok-fg);
}
.badge.tone-ok::before {
  content: "\2713";
}
.badge.tone-warn {
  background: var(--color-warn-bg);
  color: var(--color-warn-fg);
}
.badge.tone-warn::before {
  content: "!";
}
.badge.tone-bad {
  background: var(--color-danger-bg);
  color: var(--color-danger-fg);
}
.badge.tone-bad::before {
  content: "\2715";
}
.badge.tone-info {
  background: var(--color-accent-bg);
  color: var(--color-accent-fg);
}
.badge.tone-info::before {
  content: "\2192";
}
.badge.st-unknown::before {
  content: "?";
}
.badge.accent {
  /* NEW: the one accent badge (e.g. "recommended"); not a status */
  background: transparent;
  border-color: var(--color-accent);
  color: var(--color-accent-fg);
}
.badge.accent::before {
  content: "\2605";
}
.page-head .badge {
  height: 22px;
  font-size: var(--fs-ui);
}
.tag {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 6px;
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--color-text-2);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-badge);
  background: var(--color-surface);
  white-space: nowrap;
  text-indent: 0; /* inherited from pre.log > span (hanging indent), it pushed the text out of the box */
}

/* ==========================================================================
   Inline alerts: success (.notice), info (.notice.plain), warning, error.
   Glyph + colour + a 3px status rule on the left.
   ========================================================================== */
.notice,
.warning,
.error:not(span):not(small) {
  position: relative;
  display: block;
  margin: var(--sp-12) 0;
  padding: 10px var(--sp-12) 10px 40px;
  max-width: 76ch;
  font-size: var(--fs-body);
  border: var(--hair-w) solid;
  border-left: 4px solid;
  border-radius: var(--r-banner);
}
.notice::before,
.warning::before,
.error:not(span):not(small)::before {
  position: absolute;
  left: 12px;
  top: 11px;
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  font-size: 12px;
  color: var(--color-surface);
}
.notice {
  border-color: var(--color-ok);
  border-left-color: var(--color-ok);
  background: var(--color-ok-bg);
  color: var(--color-ok-fg);
}
.notice::before {
  background: var(--color-ok);
  content: "\2713";
}
.notice.plain {
  border-color: var(--color-accent);
  border-left-color: var(--color-accent);
  background: var(--color-accent-bg);
  color: var(--color-accent-fg);
}
.notice.plain::before {
  background: var(--color-accent);
  content: "i";
  font-family: Georgia, serif;
  font-style: italic;
}
.warning {
  border-color: var(--color-warn);
  border-left-color: var(--color-warn);
  background: var(--color-warn-bg);
  color: var(--color-warn-fg);
}
.warning::before {
  background: var(--color-warn);
  content: "!";
}
.error:not(span):not(small) {
  border-color: var(--color-danger);
  border-left-color: var(--color-danger);
  background: var(--color-danger-bg);
  color: var(--color-danger-fg);
}
.error:not(span):not(small)::before {
  background: var(--color-danger);
  content: "\2715";
}
.notice a,
.warning a,
.error a {
  color: inherit;
  font-weight: 700;
}
span.error {
  color: var(--color-danger-fg);
  font-weight: 600;
}
span.error::before {
  content: "\2715\00a0";
}

/* ==========================================================================
   Empty state
   ========================================================================== */
.empty-state {
  display: grid;
  gap: var(--sp-4);
  justify-items: start;
  padding: var(--sp-20) var(--sp-16);
  background: var(--color-surface-2);
  border-left: var(--hair-w) solid var(--color-border);
  color: var(--color-text-2);
  font-size: var(--fs-ui);
}
.empty-state strong {
  color: var(--color-text);
  font-size: var(--fs-body);
}
.empty-state .button,
.empty-state button {
  margin-top: var(--sp-8);
}

/* ==========================================================================
   Tables. table.dense = the admin list. Row selection, sortable headers,
   row actions (at most two buttons, otherwise an Actions menu).
   ========================================================================== */
.table-scroll {
  overflow-x: auto;
}
table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-ui);
}
thead th {
  text-align: left;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--color-text-2);
  padding: 8px 10px;
  border-bottom: var(--hair-w) solid var(--color-border);
  white-space: nowrap;
  vertical-align: bottom;
}
td {
  padding: 6px 10px;
  border-bottom: var(--hair-w) solid var(--color-border);
  vertical-align: middle;
}
table.dense td {
  height: 44px;
}
tbody tr:hover td,
tbody tr.is-hover td {
  background: var(--color-surface-2);
}
tbody tr.is-selected td,
tbody tr:has(> td.sel input:checked) td {
  background: var(--color-accent-bg);
}
tbody tr.is-selected td:first-child,
tbody tr:has(> td.sel input:checked) td:first-child {
  box-shadow: inset 3px 0 0 var(--color-accent);
}
thead th.sel {
  vertical-align: middle;
}
td.rowacts {
  /* NEW: the row-actions cell; on phones it joins the first line */
  text-align: right;
  white-space: nowrap;
}
td.sel,
th.sel {
  width: 40px;
  padding-left: 6px;
  padding-right: 0;
}
td.p1 a,
td a:not(.button) {
  color: var(--color-text);
  font-weight: 600;
  text-decoration: none;
}
td.p1 a:hover,
td.p1 a.is-hover,
td a:not(.button):hover,
td a:not(.button).is-hover {
  color: var(--color-accent-fg);
  text-decoration: underline;
}
td.empty {
  padding: var(--sp-24) var(--sp-16);
  color: var(--color-text-2);
}
td.end,
th.end {
  text-align: right;
}
td.nowrap,
th.nowrap {
  white-space: nowrap;
}
td.hash code {
  display: inline-block;
  max-width: 14ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}
/* sortable headers */
th .sortlink {
  color: inherit;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
th .sortlink:hover,
th .sortlink.is-hover {
  color: var(--color-text);
}
.sortmark {
  display: inline-block;
  width: 8px;
  height: 10px;
  opacity: 0.35;
  background:
    linear-gradient(to bottom right, transparent 50%, currentColor 50%) top
      left / 50% 40% no-repeat,
    linear-gradient(to bottom left, transparent 50%, currentColor 50%) top
      right / 50% 40% no-repeat,
    linear-gradient(to top right, transparent 50%, currentColor 50%) bottom
      left / 50% 40% no-repeat,
    linear-gradient(to top left, transparent 50%, currentColor 50%) bottom
      right / 50% 40% no-repeat;
}
.sortlink:hover .sortmark,
.sortlink.is-hover .sortmark {
  opacity: 0.7;
}
.sortlink.is-asc,
.sortlink.is-desc {
  color: var(--color-text);
}
.sortlink.is-asc .sortmark {
  opacity: 1;
  background:
    linear-gradient(to bottom right, transparent 50%, var(--color-accent) 50%)
      top left / 50% 40% no-repeat,
    linear-gradient(to bottom left, transparent 50%, var(--color-accent) 50%)
      top right / 50% 40% no-repeat;
}
.sortlink.is-desc .sortmark {
  opacity: 1;
  background:
    linear-gradient(to top right, transparent 50%, var(--color-accent) 50%)
      bottom left / 50% 40% no-repeat,
    linear-gradient(to top left, transparent 50%, var(--color-accent) 50%)
      bottom right / 50% 40% no-repeat;
}
/* row actions: at most two buttons; more go into details.rowmenu */
.row-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-4);
  white-space: nowrap;
}
.row-actions form {
  margin: 0;
}
/* table toolbar above a list */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-8);
  margin: 0 0 var(--sp-12);
}
.toolbar .muted {
  font-size: var(--fs-ui);
}
/* cell content helpers */
.chan {
  font-size: 11px;
  font-weight: 700;
  padding: 0 4px;
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-xs);
}
.chan-bad {
  color: var(--color-danger-fg);
  border-color: var(--color-danger);
}
.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4);
  margin-top: var(--sp-12);
  font-size: var(--fs-ui);
}
.pager .muted {
  margin-left: var(--sp-12);
}

/* ==========================================================================
   Power toggle: on / off / unknown. Ink, not a status colour: power is a
   state the user sets, not a health signal.
   ========================================================================== */
.relay-state {
  font-size: var(--fs-ui);
  color: var(--color-text-2);
}

/* ==========================================================================
   Overlays: menu, Actions menu, tooltip, popover, dialog. One step above the
   page: overlay surface, visible 1px border, shadow, in both themes.
   ========================================================================== */
.menu,
.rowmenu-body,
.popover,
.tooltip,
dialog.dlg {
  background: var(--color-surface);
  color: var(--color-text);
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: var(--r-panel);
  box-shadow: var(--shadow-lg);
}

.menu,
.rowmenu-body {
  min-width: 12rem;
  max-width: calc(100vw - 2 * var(--sp-12));
  padding: var(--sp-4) 0;
  display: grid;
}
.menu a,
.menu button,
.rowmenu-body a,
.rowmenu-body button {
  /* menu items are list rows: flush-left, full width */
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--sp-8);
  width: 100%;
  height: 36px;
  padding: 0 var(--sp-12);
  border: 0;
  background: transparent;
  color: var(--color-text);
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--fs-ui);
  text-decoration: none;
  white-space: nowrap;
}
.menu a:hover,
.menu a.is-hover,
.menu button:hover,
.menu button.is-hover,
.rowmenu-body a:hover,
.rowmenu-body a.is-hover,
.rowmenu-body button:hover,
.rowmenu-body button.is-hover {
  background: var(--color-surface-2);
  border: 0;
}
.menu a:active,
.menu a.is-active,
.rowmenu-body a:active,
.rowmenu-body a.is-active,
.rowmenu-body button:active,
.rowmenu-body button.is-active {
  background: var(--color-surface-3);
}
.menu a:focus-visible,
.menu a.is-focus,
.menu button:focus-visible,
.menu button.is-focus,
.rowmenu-body a:focus-visible,
.rowmenu-body a.is-focus,
.rowmenu-body button:focus-visible,
.rowmenu-body button.is-focus {
  outline-offset: -2px;
}
.menu a[aria-current="page"],
.rowmenu-body [aria-current="true"] {
  font-weight: 700;
  box-shadow: inset 3px 0 0 var(--color-accent);
}
.menu [aria-disabled="true"],
.rowmenu-body [disabled],
.rowmenu-body [aria-disabled="true"] {
  opacity: 0.45;
  pointer-events: none;
}
.rowmenu-body .danger,
.menu .danger {
  color: var(--color-danger-fg);
}
.menu hr,
.rowmenu-body hr {
  margin: var(--sp-4) 0;
  border: 0;
  border-top: var(--hair-w) solid var(--color-border);
}

.rowmenu-body form {
  margin: 0;
}
details.rowmenu {
  position: relative;
  display: inline-block;
}
details.rowmenu > summary {
  list-style: none;
}
details.rowmenu > summary::-webkit-details-marker {
  display: none;
}
details.rowmenu > summary::after {
  content: "";
  width: 5px;
  height: 5px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
}
details.rowmenu > summary.icon::after {
  display: none;
}
details.rowmenu[open] > summary {
  background: var(--color-text);
  border-color: var(--color-text);
  color: var(--color-surface);
}
details.rowmenu > .rowmenu-body {
  position: absolute;
  z-index: var(--z-menu);
  right: 0;
  top: calc(100% + 4px);
}
.tooltip {
  position: fixed;
  z-index: var(--z-menu);
  max-width: 32ch;
  padding: 6px 10px;
  font: 400 var(--fs-small) / 1.45 var(--font-sans);
  box-shadow: var(--shadow-lg);
  white-space: pre-line;
  pointer-events: none;
}
.tooltip.is-static {
  /* NEW, previews only: a tooltip in the flow */
  position: relative;
  display: inline-block;
}
.popover {
  /* NEW: generic overlay panel (column picker, filters) */
  padding: var(--sp-12) var(--sp-16);
  min-width: 16rem;
  display: grid;
  gap: var(--sp-8);
}
dialog.dlg {
  width: min(40rem, calc(100vw - 2 * var(--sp-16)));
  max-height: 85vh;
  padding: 0;
  overflow: hidden;
}
dialog.dlg[open] {
  display: flex;
  flex-direction: column;
}
dialog.dlg::backdrop,
.backdrop {
  background: var(--backdrop);
}
.backdrop {
  /* NEW, previews only: the ::backdrop as an element */
  position: absolute;
  inset: 0;
}
.dlg-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-16);
  padding: var(--sp-12) var(--sp-16);
  border-bottom: var(--hair-w) solid var(--color-border);
}
.dlg-head h2 {
  margin: 0;
  font-size: var(--fs-h2-lg);
  font-weight: 800;
  display: flex;
  align-items: center;
  gap: var(--sp-8);
}
.dlg-body {
  padding: var(--sp-16);
  overflow: auto;
}
.dlg-foot {
  /* NEW: dialog actions, primary rightmost */
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-8);
  padding: var(--sp-12) var(--sp-16);
  border-top: var(--hair-w) solid var(--color-border);
}

/* ==========================================================================
   Bulk-action bar: sticks to the bottom while rows are selected
   ========================================================================== */
.bulkbar {
  position: sticky;
  bottom: calc(var(--sb-h) + var(--sp-16));
  z-index: var(--z-sticky);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-8);
  margin: var(--sp-16) 0 0;
  padding: var(--sp-8) var(--sp-12);
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border-strong);
  border-top: 2px solid var(--color-accent);
  box-shadow: var(--shadow-lg);
  border-radius: var(--r-panel);
}
.bulk-count {
  margin-right: auto;
  font-size: var(--fs-ui);
}
.bulk-count strong {
  font-family: var(--font-sans);
  font-weight: 800;
  font-size: var(--fs-h2-lg);
  margin-right: 2px;
}

/* ==========================================================================
   Tabs
   ========================================================================== */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  margin: 0 0 var(--sp-20);
  border-bottom: var(--hair-w) solid var(--color-border);
}
.tabs a {
  display: flex;
  align-items: center;
  height: 38px;
  padding: 0 var(--sp-16);
  margin-bottom: -1px;
  color: var(--color-text-2);
  font-weight: 600;
  font-size: var(--fs-body);
  text-decoration: none;
  border-bottom: 3px solid transparent;
}
.tabs a:hover,
.tabs a.is-hover {
  color: var(--color-text);
  background: var(--color-surface-2);
}
.tabs a:active,
.tabs a.is-active {
  background: var(--color-surface-3);
}
.tabs a:focus-visible,
.tabs a.is-focus {
  outline-offset: -2px;
}
.tabs a[aria-current="page"],
.tabs a[aria-selected="true"] {
  color: var(--color-text);
  border-bottom-color: var(--color-accent);
}
.tabs a[aria-disabled="true"] {
  opacity: 0.45;
  pointer-events: none;
}
.tabs .count {
  /* NEW: number after a tab label */
  margin-left: 6px;
  font-size: var(--fs-small);
  color: var(--color-text-2);
}

/* ==========================================================================
   Collapsible section. Collapsed = header only.
   details.fold: a section with a rule; .fold-h inside a card: the summary is the framed title.
   ========================================================================== */
details.fold {
  border-top: var(--hair-w) solid var(--color-border);
  margin: var(--sp-16) 0;
}
details.fold > summary {
  display: flex;
  align-items: center;
  gap: var(--sp-8);
  min-height: 40px;
  cursor: pointer;
  list-style: none;
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: var(--fs-body);
}
details.fold > summary::-webkit-details-marker {
  display: none;
}
details.fold > summary::after {
  /* one chevron app-wide (same as the navigation): down when closed, up when open */
  content: "";
  flex: none;
  width: 6px;
  height: 6px;
  margin: 0 4px 0 auto;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform 0.12s;
}
details.fold > summary:has(.meta)::after {
  margin-left: var(--sp-12);
}
details.fold[open] > summary::after {
  transform: translateY(2px) rotate(-135deg);
}
details.fold > summary:hover,
details.fold > summary.is-hover {
  background: var(--color-surface-2);
}
details.fold > summary .meta {
  /* NEW: right-aligned summary info */
  margin-left: auto;
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: var(--fs-ui);
  color: var(--color-text-2);
}
details.fold[open] > summary {
  margin-bottom: var(--sp-8);
}
/* the fold inside a card: the summary is the framed title */
.card > details.fold.fold-h:first-child {
  border: 0;
  margin: 0;
}
.card > details.fold.fold-h:first-child > summary {
  min-height: 0;
  margin: 0;
}
.card > details.fold-h:first-child > summary::after {
  margin-left: var(--sp-8);
}
.card > details.fold.fold-h > summary > h2 {
  margin: 0;
  font: inherit;
  display: inline;
}
.card:has(> details.fold-h:first-child:not([open])) {
  /* collapsed: the same framed card, just without a body (one card component, folded or not) */
  padding-bottom: var(--sp-4);
  margin-bottom: var(--sp-16);
}
@media (prefers-reduced-motion: reduce) {
  details.fold > summary::after {
    transition: none;
  }
}

/* ==========================================================================
   Theme switch (NEW, preview pages only; theme.js)
   ========================================================================== */
.theme-switch {
  display: inline-flex;
  gap: 0;
}
.theme-switch button {
  margin-left: -1px;
}

/* ==========================================================================
   Utilities
   ========================================================================== */
.m-only {
  display: none;
}
.nowrap {
  white-space: nowrap;
}
.mono {
  font-family: var(--font-mono);
}

/* ==========================================================================
   Phones (up to 44.99rem, the prototype switches at 720px)
   ========================================================================== */
@media (max-width: 44.99rem) {
  :root {
    /* touch targets: every control 40px on phones (both sizes) */
    --ctl: 40px;
    --ctl-sm: 40px;
  }
  .menu a,
  .menu button,
  .rowmenu-body a,
  .rowmenu-body button,
  .tabs a {
    height: 40px;
  }
  .hit {
    min-width: 40px;
    min-height: 40px;
  }
  .m-only {
    display: revert;
  }
  .m-hide,
  .d-only {
    display: none !important;
  }

  /* top bar: brand and user on row 1, navigation scrolls on row 2 */

  main {
    padding-top: var(--sp-16);
  }
  .page-head {
    grid-template-columns: minmax(0, 1fr) auto;
    margin-bottom: var(--sp-16);
  }
  .page-head h1 {
    font-size: 22px;
  }
  /* more than one action: actions go under the title */
  .page-head:has(.actions > :nth-child(2)) {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "crumb" "title" "actions";
    row-gap: var(--sp-12);
  }
  .page-head:has(.actions > :nth-child(2)) .actions {
    justify-content: flex-start;
  }

  .stats {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .card {
    padding: var(--sp-20) var(--sp-12) var(--sp-12);
  }

  .facts {
    grid-template-columns: minmax(0, 1fr);
  }
  .facts dt {
    border-bottom: 0;
    padding-bottom: 0;
    font-size: var(--fs-small);
  }

  /* dense tables become one block per row (flex, wrapping):
     [sel] p1 (name) .................. p2 (power)
           s  s  s  s   (secondary cells, inline, no labels)
           Label  value (one line per other cell) */
  table.dense,
  table.dense tbody {
    display: block;
  }
  table.dense colgroup,
  table.dense thead {
    display: none;
  }
  table.dense tbody tr {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px var(--sp-12);
    padding: var(--sp-8) 0;
    border-bottom: var(--hair-w) solid var(--color-border);
  }
  table.dense tbody tr::before {
    /* line break after p1 + p2 */
    content: "";
    order: 3;
    flex-basis: 100%;
    height: 0;
  }
  table.dense tbody tr:has(> td.sel) {
    padding-left: 44px;
  }
  table.dense td {
    order: 5;
    display: flex;
    align-items: center;
    gap: var(--sp-8);
    flex-basis: 100%;
    height: auto;
    min-height: 24px;
    padding: 0;
    border: 0;
    background: transparent !important;
    box-shadow: none !important;
  }
  table.dense
    td[data-label]:not(.p1):not(.p2):not(.sel):not(.s):not(.rowacts)::before {
    content: attr(data-label);
    flex: none;
    width: 6.5rem;
    font-size: var(--fs-small);
    color: var(--color-text-2);
  }
  table.dense td.s {
    order: 4;
    flex-basis: auto;
    color: var(--color-text-2);
  }
  /* td.s.lab: a secondary cell whose label is what tells the cells apart (Before / After, Old / New),
     so it stays a labelled line of its own instead of an inline value */
  table.dense td.s.lab {
    order: 5;
    flex-basis: 100%;
  }
  table.dense td.s.lab[data-label]::before {
    content: attr(data-label);
    flex: none;
    width: 6.5rem;
    font-size: var(--fs-small);
    color: var(--color-text-2);
  }
  table.dense td.sel {
    position: absolute;
    left: 0;
    top: var(--sp-8);
    width: auto;
    padding: 0;
  }
  table.dense td.p1 {
    order: 1;
    flex: 1 1 0;
    min-width: 0;
    font-size: var(--fs-body);
    min-height: 40px;
  }
  table.dense td.p2 {
    order: 2;
    flex: none;
    justify-content: flex-end;
  }
  /* A status cell that holds a Details fold (policy devices): the badge stays on its line
     and the fold takes a line of its own, wrapping long findings instead of widening the card. */
  table.dense td.p2:has(> details[open]) {
    flex: 1 1 100%;
    min-width: 0;
    flex-wrap: wrap;
    justify-content: flex-start;
  }
  table.dense td.p2 > details {
    flex: 1 1 100%;
    min-width: 0;
  }
  table.dense td.p2 > details li {
    overflow-wrap: anywhere;
  }
  table.dense tbody tr.is-selected,
  table.dense tbody tr:has(> td.sel input:checked) {
    background: var(--color-accent-bg);
    box-shadow: inset 3px 0 0 var(--color-accent);
  }
  table.dense td.rowacts {
    order: 2;
    flex: none;
  }
  table.dense td.empty {
    display: block;
  }
  table.dense td.end {
    justify-content: flex-start;
  }

  .bulkbar {
    bottom: var(--sp-8);
  }
  .bulkbar .bulk-count {
    flex: 1 1 auto;
  }
  .bulkbar button.ghost {
    flex: none;
  }
  .bulkbar button:not(.ghost) {
    flex: 1 1 45%;
  }

  .filters label,
  .filters label.grow {
    max-width: none;
    flex-basis: 100%;
  }
  .filters button,
  .filters a.button {
    flex: 1 1 100%;
  }

  dialog.dlg {
    width: 100vw;
    max-width: 100vw;
    max-height: 92vh;
    margin: auto 0 0;
  }
}

/* ==========================================================================
   Small helpers used by the app markup
   ========================================================================== */
a.ext {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
a.ext .ico {
  width: 12px;
  height: 12px;
  opacity: 0.6;
}
a.ext:hover .ico,
a.ext.is-hover .ico {
  opacity: 1;
}
td a.ext {
  font-weight: 400;
}

/* ==== nav-patterns (PORT.md) ==== */
/* tasmofleet DS: navigation and in-page patterns (proposal, port 1:1 into styles.css)
   Loads after ../../styles.css. Uses only existing tokens; no new colours or fonts.

   NEW CLASSES / TOKENS (everything else below refines existing classes)
   --bulkbar-h                 token: height of the fixed bulk bar; body gets this as bottom padding
   details.navpanel            phone navigation: compact "Menu" button + one full-width panel (m-only)
   .navpanel-body              the panel (overlay surface, full width under the top bar)
   .navpanel-sec               one group in the panel (Dashboard / Devices / Updates / System / user)
   .navpanel-label             group heading in the panel; .navpanel-sec.is-active = current group
   .crumb.trail                breadcrumb with "›" separators, last item = current page (span[aria-current]);
                               on phones only the parent remains, as a "←" back link
   .tabs .count, .count.accent count in a tab (Backups 3); accent = needs attention (Discovered)
   .bulk-actions               the bulk bar's action buttons (inline, ≥ 60rem)
   details.rowmenu.bulk-more   the same actions as one "Actions" menu (< 60rem)
   details.rowmenu.up          menu body opens upward (bulk bar, last rows of a table)
   .devtable col.c-act         width of the devices table's Actions column
   .confirm                    confirmation page = the dialog layout as a card (head, facts, foot)        #23 #55

   APP CLASSES MISSING IN styles.css (app.css has them; ported here in the DS look, same names)
   td.rowacts                  row-actions cell (styles.css calls it td.rowact: rename there)
   details.colmenu > summary.colbtn + .colpanel (.colhint .colrow .collabel .colmoves), .tabletools
                               the Columns chooser that tableprefs.go writes                              #3
   .tablewrap                  the wrapper tableprefs.go puts around a prefs-managed table
   td .cell > .dev             device-name lists (firmware "devref"): a name is one run while it fits, then wraps;
                               the comma stays with the name, 24px targets on phones                     #34 #84
   pre.log                     log box (update_batch.html): content height up to 16rem, then a scroll
                               fade; with one <span> per line the wrapped lines hang                     #40 #71
   .chips, .chips.scroll       chip lists wrap to their full height (no cut-off scroll box)              #65

   QA v0.9.0 refinements (no new names): one control height per toolbar and pager (#7), optical centring
   with text-box trim (#8), 40px row/form action links on phones (#13), open menus hide tooltips (#3),
   error glyph "!" on an octagon and info badge "i" instead of X / arrow (#24), empty cells show "-" (#51),
   ellipsis instead of hard clipping in phone rows and form controls (#65 #67 #70), all dropdowns
   left-aligned except the user menu (#75).

   Refined (no new names): header.top/.user (Sign out inside the user menu),
   td.rowacts, .table-scroll, .bulkbar (fixed to the viewport), details.fold (chevron right of
   the title), .card (content offset under the framed title, 40px hit area on fold titles). */

:root {
  --bulkbar-h: 58px;
  /* status bar (#307): --sb-line is its row, --sb-h the whole bar with the phone's safe area.
     Everything fixed to the window's bottom edge lies above it: bottom: var(--sb-h) + its gap. */
  --sb-line: 32px;
  --sb-h: calc(var(--sb-line) + env(safe-area-inset-bottom, 0px));
}

/* ---- top bar --------------------------------------------------------------- */

.menu form {
  margin: 0;
}

/* ---- page header: breadcrumb trail ---------------------------------------- */

/* ---- tabs ------------------------------------------------------------------ */
.tabs .count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 18px;
  padding: 0 5px;
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-pill);
  background: var(--color-surface-2);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.tabs .count.accent {
  border-color: transparent;
  background: var(--color-accent-bg);
  color: var(--color-accent-fg);
}
.tabs a[aria-disabled="true"] {
  color: var(--color-text-3);
  pointer-events: none;
}
/* the page you are on is never drawn as unavailable, even when the device cannot use it right now */
.tabs a[aria-disabled="true"][aria-current="page"] {
  opacity: 1;
  color: var(--color-text);
}

/* ---- row actions ------------------------------------------------------------ */
td.rowacts {
  /* app name (styles.css: td.rowact): last cell, right-aligned, shrink-to-fit, vertically centred */
  width: 1%;
  text-align: right;
  white-space: nowrap;
  vertical-align: middle;
}
td.rowacts > .actions {
  display: inline-flex;
  gap: var(--sp-8);
  justify-content: flex-end;
  margin: 0;
}
td.rowacts details.rowmenu {
  vertical-align: middle;
}
/* import: the plan's and the result's closing actions keep their distance from the table above */
#import-result .card > form.stack,
#import-result .card > p.actions {
  margin-top: var(--sp-16);
}
/* a filter that is one checkbox: the button stays beside it instead of at the end of a 16rem label */
.filters > label.inline {
  flex: 0 0 auto;
}
/* proposals: the cell shrinks to fit, so wrapping would stack Ignore above Add device; they stay on one line */
table[data-table="discovery"] td.rowacts > .actions {
  flex-wrap: nowrap;
}
.devtable col.c-act {
  width: 6.5rem;
}
.rowmenu-body a[aria-disabled="true"] {
  color: var(--color-text-3);
  pointer-events: none;
}
.table-scroll:has(details.rowmenu[open]) {
  overflow: visible;
}
details.rowmenu.up > .rowmenu-body {
  top: auto;
  bottom: calc(100% + 4px);
}

/* ---- bulk bar: fixed to the viewport, content padded by its height ---------- */
.bulkbar {
  position: fixed;
  left: var(--page-pad);
  right: var(--page-pad);
  bottom: calc(var(--sb-h) + var(--sp-12));
  max-width: calc(var(--container) - 2 * var(--page-pad));
  min-height: var(--bulkbar-h);
  margin: 0 auto;
  flex-wrap: nowrap;
}
.bulkbar[hidden] {
  display: none;
}
body:has(.bulkbar:not([hidden])) {
  padding-bottom: calc(var(--sb-h) + var(--bulkbar-h) + 2 * var(--sp-12));
}
.bulkbar .bulk-count {
  margin-right: 0;
  white-space: nowrap;
}
.bulk-actions {
  display: flex;
  gap: var(--sp-8);
  margin-left: auto;
}
details.rowmenu.bulk-more {
  display: none;
  margin-left: auto;
}
@media (max-width: 60rem) {
  .bulkbar:has(.bulk-more) .bulk-actions {
    display: none;
  }
  details.rowmenu.bulk-more {
    display: inline-block;
  }
}

/* ---- collapsible sections ---------------------------------------------------- */
details.fold > summary::after {
  order: 1;
  margin: 0 4px 0 var(--sp-4);
}
details.fold > summary .meta {
  order: 2;
}
details.fold > summary:has(.meta)::after {
  margin-left: var(--sp-4);
}
.card {
  /* content starts below the framed title: title height + 0.75rem */
  padding-top: calc(var(--fs-h2-lg) * 1.2 + 0.75rem);
}
.card > details.fold-h:first-child > summary::before {
  /* 40px hit area around the framed title, without covering the rule */
  content: "";
  position: absolute;
  inset: -10px 0;
}
.card:has(> details.fold-h:first-child[open]) > details > summary + * {
  margin-top: 0;
}

/* ---- QA v0.9.0 ---------------------------------------------------------------- */
/* #7 one height per size, one size per toolbar: toolbars and pagers are always sm */
.toolbar button,
.toolbar .button,
.toolbar select,
.pager button,
.pager .button,
.pager select {
  height: var(--ctl-sm);
  padding: 0 var(--ctl-pad-sm);
  font-size: var(--fs-ui);
}
.toolbar > *,
.pager > * {
  flex: none;
}
.toolbar form {
  margin: 0;
}
.toolbar > details.rowmenu:last-child {
  margin-left: auto;
}
/* #8 labels centred in their box: label.button flex-centred, caps trimmed optically */
label.button {
  display: inline-flex;
  align-items: center;
  width: auto;
}
button,
.button,
.badge,
.tag,
.tabs a,
.tabs .count {
  text-box: trim-both cap alphabetic;
}
/* #3 an open menu hides every floating tooltip; menus never clip their labels */
body:has(details[open] > .rowmenu-body, details[open] > .menu)
  .tooltip:not(.is-static) {
  display: none;
}
/* Columns chooser: tableprefs.go markup (the prototype's chooser, D19); offered from 45rem,
   where the table (not the phone list) shows, like in tables.js */
.colmenu,
.tabletools {
  display: none;
}
.tablewrap {
  position: relative;
}
@media (min-width: 45rem) {
  .tabletools {
    display: flex;
    justify-content: flex-end;
    margin: 0 0 var(--sp-8);
  }
  .colmenu {
    display: block;
    position: relative;
    z-index: var(--z-menu);
  }
  .toolbar > .colmenu {
    margin-left: auto;
  }
  .table-scroll:has(details.colmenu[open]),
  .table-fit:has(details.colmenu[open]) {
    overflow: visible;
  }
  .colbtn {
    /* a plain sm button with a visible label; no title tooltip (#3) */
    list-style: none;
    display: inline-flex;
    align-items: center;
    gap: var(--sp-8);
    height: var(--ctl-sm);
    padding: 0 var(--ctl-pad-sm);
    border: var(--hair-w) solid var(--color-border-strong);
    border-radius: var(--r-control);
    background: var(--color-surface);
    color: var(--color-text);
    font: 600 var(--fs-ui) / 1 var(--font-sans);
    cursor: pointer;
  }
  .colbtn::-webkit-details-marker {
    display: none;
  }
  .colbtn::after {
    content: "";
    width: 5px;
    height: 5px;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: translateY(-2px) rotate(45deg);
  }
  .colbtn:hover {
    background: var(--color-surface-2);
  }
  .colpanel {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    width: 400px;
    max-width: calc(100vw - 32px);
    padding: 6px;
    background: var(--color-surface);
    /* the prototype's --border is below the 1.3:1 overlay guard (layout guard g); strong as every menu */
    border: var(--hair-w) solid var(--color-border-strong);
    border-radius: 10px;
    box-shadow: var(--shadow-lg);
    font: 400 13px / 1.3 var(--font-sans);
    color: var(--color-text);
  }
  .colpanel .colhint {
    margin: 0;
    padding: 6px 8px 8px;
    font-size: 12px;
    line-height: 1.45;
    color: var(--color-text-2);
  }
  .colpanel .colrow {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 36px;
    padding: 0 4px 0 8px;
    border-radius: 6px;
  }
  .colpanel .colrow[draggable="true"]:hover {
    background: var(--color-surface-2);
  }
  .colpanel .colrow.is-fixed {
    gap: 8px;
    padding: 0 8px;
    color: var(--color-text-2);
  }
  .colpanel .colrow.drop-t {
    box-shadow: inset 0 2px 0 var(--color-accent);
  }
  .colpanel .colrow.drop-b {
    box-shadow: inset 0 -2px 0 var(--color-accent);
  }
  .colpanel .colgrip {
    flex: none;
    width: 14px;
    color: var(--color-text-3);
    letter-spacing: -2px;
    cursor: grab;
  }
  .colpanel label {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1;
    min-width: 0;
    font-weight: 400;
    color: var(--color-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    cursor: pointer;
  }
  .colpanel .is-fixed label {
    cursor: default;
  }
  .colpanel input[type="checkbox"] {
    accent-color: var(--color-accent);
  }
  .colpanel .colfix {
    display: inline-flex;
    align-items: center;
    height: 20px;
    padding: 0 7px;
    border-radius: 5px;
    font: 500 11.5px var(--font-sans);
    white-space: nowrap;
    background: var(--color-accent-bg);
    color: var(--color-accent-fg);
  }
  .colpanel .colpin {
    flex: none;
    min-height: 0;
    height: 26px;
    padding: 0 6px;
    border-radius: 5px;
    border: var(--hair-w) solid var(--color-border);
    background: transparent;
    color: var(--color-text-2);
    font: 500 11px var(--font-sans);
    white-space: nowrap;
    box-shadow: none;
  }
  .colpanel .colpin[aria-pressed="true"] {
    background: var(--color-accent-bg);
    color: var(--color-accent-fg);
  }
  .colpanel .colfoot {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
    margin-top: 6px;
    padding: 6px 4px 2px 8px;
    border-top: var(--hair-w) solid var(--color-border);
    font-size: 11.5px;
    color: var(--color-text-3);
  }
  .colpanel .colreset {
    flex: none;
    min-height: 0;
    height: 30px;
    padding: 0 10px;
    border: 0;
    border-radius: 7px;
    background: none;
    box-shadow: none;
    color: var(--color-accent-fg);
    font: 500 13px var(--font-sans);
    white-space: nowrap;
  }
}
/* #24 error glyph: "!" on an octagon (a dismiss "X" no longer); info badge: "i" */
.error:not(span):not(small)::before {
  content: "!";
  clip-path: polygon(
    30% 0,
    70% 0,
    100% 30%,
    100% 70%,
    70% 100%,
    30% 100%,
    0 70%,
    0 30%
  );
}
span.error::before {
  content: "!\00a0";
}
.badge.tone-info::before {
  content: "i";
  font-family: Georgia, serif;
  font-style: italic;
}
/* #51 empty cells read "-" in every table */
td:empty:not(.rowacts):not(.sel)::after {
  content: "-";
  color: var(--color-text-3);
}
/* #34 #84 device-name lists (firmware devref): a name stays in one run while it fits its box, then wraps
   anywhere (never an ellipsis, see "long unbreakable text" below); the comma stays with the name */
.dev {
  display: inline-block;
  max-width: 100%;
  vertical-align: bottom;
}
.dev > a {
  color: var(--color-text);
  font-weight: 600;
  text-decoration: none;
}
.dev > a:hover,
.dev > a.is-hover {
  color: var(--color-accent-fg);
  text-decoration: underline;
}
.dev:not(:last-child)::after {
  content: ",";
  flex: none;
}
/* #40 #71 log box (pre.log): height follows the content up to 16rem, then scrolls with a fade */
pre.log {
  max-height: 16rem;
  overflow: auto;
  margin: var(--sp-8) 0 0;
  padding: var(--sp-8) var(--sp-12);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-code);
  font: 400 var(--fs-small) / 1.6 var(--font-mono);
  background:
    linear-gradient(transparent, var(--color-surface-2) 60%) center bottom /
      100% 28px no-repeat local,
    linear-gradient(
        transparent,
        color-mix(in srgb, var(--color-text) 16%, transparent)
      )
      center bottom / 100% 14px no-repeat scroll,
    var(--color-surface-2);
}
/* a device's reply is data, not prose: it keeps its lines and tokens whole and scrolls sideways */
pre.log.reply {
  white-space: pre;
  overflow-wrap: normal;
}
pre.log > span {
  display: block;
  padding-left: 3ch;
  text-indent: -3ch;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
/* #23 #55 confirmation page */
.confirm {
  max-width: 40rem;
}
.confirm .facts {
  margin: var(--sp-12) 0;
}
.confirm .dlg-foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--sp-8);
  margin: var(--sp-16) calc(var(--sp-16) * -1) 0;
  padding: var(--sp-12) var(--sp-16) 0;
  border-top: var(--hair-w) solid var(--color-border);
}
@media (max-width: 44.99rem) {
  /* the card has 12px side padding here: the rule ends exactly at the card's border, not past it */
  .confirm .dlg-foot {
    margin-inline: calc(var(--sp-12) * -1);
    padding-inline: var(--sp-12);
  }
}
label.inline:has(> input[type="checkbox"]) {
  display: flex;
  align-items: flex-start;
}
label.inline > input[type="checkbox"] {
  flex: none;
  margin-top: 2px;
}
/* #65 chip lists wrap to full height (no cut-off scroll box); controls ellipsize */
.chips,
.chips.scroll {
  max-height: none;
  overflow: visible;
}
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  margin: 0;
  padding: 0;
  list-style: none;
}
input:not([type="checkbox"]):not([type="radio"]),
select {
  text-overflow: ellipsis;
}
/* hint under a row control instead of beside it (#52) */
td .hint {
  display: block;
  margin-top: 2px;
}

/* ---- phones (up to 44.99rem: the prototype switches at 720px) ---- */
@media (max-width: 44.99rem) {
  /* #13 links that act as row or form actions get a 40px hit area */
  td a:not(.button):not(.cell a),
  .toolbar a:not(.button),
  .dlg-foot a:not(.button),
  .confirm a:not(.button) {
    display: inline-flex;
    align-items: center;
    min-height: 40px;
  }
  td .cell {
    /* a list of names is one inline run: 24px targets, 8px apart (WCAG 2.5.8 spacing), normal pitch (#84) */
    display: flex;
    flex-wrap: wrap;
    gap: 0 var(--sp-8);
    line-height: 24px;
  }
  table.dense td.rowacts {
    order: 2;
    flex: none;
    width: auto;
    min-width: max-content;
  }
  table.dense
    td.rowacts[data-label]:not(.p1):not(.p2):not(.sel):not(.s)::before {
    content: none;
  }
  td.rowacts > .actions {
    justify-content: flex-start;
  }
  /* #67 #70 phone rows: ellipsis instead of hard clipping; rows align with or without a checkbox */
  table.dense td.p1 > a,
  table.dense td.p1 > span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  table.dense td.p1 > span > a {
    display: inline;
  }
  table.dense:has(td.sel) tbody tr:not(:has(> td.sel)) {
    padding-left: 44px;
  }
  table.dense td {
    min-width: 0;
    overflow-wrap: anywhere;
  }
  table.dense td > :not(.badge):not(.relay):not(details):not(.row-actions) {
    min-width: 0;
  }

  .card {
    padding-top: calc(40px / 2 + 0.75rem);
  }

  .bulkbar {
    left: 0;
    right: 0;
    bottom: var(--sb-h);
    max-width: none;
    border-radius: 0;
    border-left: 0;
    border-right: 0;
    border-bottom: 0;
    padding-bottom: var(--sp-8);
  }
  body:has(.bulkbar:not([hidden])) {
    padding-bottom: calc(var(--sb-h) + var(--bulkbar-h) + var(--sp-12));
  }
  .bulkbar .bulk-count,
  .bulkbar button.ghost,
  .bulkbar button:not(.ghost) {
    flex: none;
  }
}

/* ==== page rules: classes of one page or one component that the design system has no rule for ==== */
.pre {
  white-space: pre-wrap;
}
button.htmx-request[disabled],
a[aria-busy="true"] {
  cursor: progress;
}
button.htmx-request[disabled]::after {
  content: "";
  width: 0.85em;
  height: 0.85em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: ds-spin 0.8s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  button.htmx-request[disabled]::after {
    animation: none;
  }
}
/* a relay switch is a grid: a ring would be a second item that pushes the knob out of place, so a
   busy switch only dims (disabled) and says aria-busy (#538) */
.devrelay-sw[aria-busy="true"]::after,
.devrelay-sw.htmx-request[disabled]::after,
.pwr[aria-busy="true"]::after,
.pwr.htmx-request[disabled]::after {
  content: none;
}
.stack > .actionbar > p {
  margin: 0;
}
.inline-form {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  align-items: center;
  margin: 0;
}
td form.inline-form {
  display: flex;
}
td.wrap {
  min-width: 12rem;
}

@media (max-width: 44.99rem) {
  td form.inline-form {
    justify-content: flex-end;
  }
  td.wrap {
    min-width: 0;
  }
}

.picker th:first-child,
.picker td:first-child {
  width: 2.5rem;
}
.picker label {
  display: inline;
  font-weight: 550;
  font-size: inherit;
}
.picker tr:has(input:disabled) td {
  color: var(--color-text-2);
}
.picker tr:has(input:checked) td {
  background: var(--color-accent-bg) !important;
}
@media (min-width: 45rem) {
  table td.wrap code {
    white-space: pre-wrap;
  }
}
.summary-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.6rem;
  align-items: center;
  margin: 0 0 1rem;
  min-height: 2.35rem;
}
.jobs {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.6rem;
}
.job {
  min-width: 0;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--r-card);
  padding: 0.7rem 1rem;
}
.job-head {
  display: grid;
  grid-template-columns: minmax(9rem, 16rem) minmax(0, 1fr) 8.5rem 5.5rem 5.5rem;
  gap: 0.5rem 1rem;
  align-items: center;
  min-height: 2.9rem;
}
.job-dev {
  font-weight: 600;
  min-width: 0;
  overflow-wrap: break-word;
}
.job-prog {
  min-width: 0;
}
.bar {
  height: 0.5rem;
  border-radius: var(--r-xs);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  overflow: hidden;
}
.bar i {
  display: block;
  height: 100%;
  background: var(--color-accent);
  border-radius: var(--r-xs);
  transition: width 0.6s ease;
}
.bar.bad {
  border-color: var(--color-danger);
}
.bar i.s0 {
  width: 0;
}
.bar i.s1 {
  width: 12.5%;
}
.bar i.s2 {
  width: 25%;
}
.bar i.s3 {
  width: 37.5%;
}
.bar i.s4 {
  width: 50%;
}
.bar i.s5 {
  width: 62.5%;
}
.bar i.s6 {
  width: 75%;
}
.bar i.s7 {
  width: 87.5%;
}
.bar i.s8 {
  width: 100%;
  background: var(--color-ok);
}
.bar.bad i {
  width: 100%;
  background: var(--color-danger);
} /* a failed job is a full danger bar, not an empty pill */
.job-step {
  margin-top: 0.25rem;
  font-size: 0.86rem;
  color: var(--color-text-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-height: 1.3em;
}
.job-state {
  min-width: 0;
}
.job-time {
  text-align: right;
  color: var(--color-text-2);
  font-variant-numeric: tabular-nums;
}
.job-act {
  text-align: right;
  min-height: 1.9rem;
}
details.job-log {
  margin-top: 0.4rem;
  border-top: 1px solid var(--color-border);
  padding-top: 0.4rem;
}
details.job-log > summary {
  cursor: pointer;
  color: var(--color-text-2);
  font-size: 0.86rem;
  list-style-position: inside;
}
.job-last {
  display: inline-block;
  max-width: calc(100% - 2rem);
  vertical-align: bottom;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.log-tools {
  display: flex;
  justify-content: flex-end;
  margin-top: 0.3rem;
}
@media (max-width: 44.99rem) {
  .job-head {
    grid-template-columns: minmax(0, 1fr) auto;
  }
  .job-prog {
    grid-column: 1 / -1;
    order: 3;
  }
  .job-state {
    justify-self: end;
  }
  .job-time {
    text-align: left;
  }
  .job-act {
    grid-column: 2;
    justify-self: end;
  }
}
@media (prefers-reduced-motion: reduce) {
  .bar i,
  .badge {
    transition: none;
  }
}
.devtable col.c-sel {
  width: 2.75rem;
}
.devtable col.c-status {
  width: 12.5rem;
}
.devtable col.c-fw {
  width: 9rem;
}
.devtable col.c-fam {
  width: 6.5rem;
}
.devtable col.c-addr {
  width: 10.5rem;
}
.devtable col.c-up {
  width: 6rem;
}
.devtable col.c-wifi {
  width: 6rem;
}
.devtable col.c-restart {
  width: 9rem;
}
.devtable col.c-ota {
  width: 16rem;
}
.devtable col.c-gt {
  width: 8rem;
}
.devtable col.c-pow {
  width: 9rem;
}
/* A name wraps (anywhere, so even an unbroken one) instead of setting the table's width: a nowrap name
   was the table's minimum width and pushed the Actions column past the content edge (QA v0.9.0). */
@media (min-width: 45rem) {
  .devtable td.wrap {
    overflow-wrap: anywhere;
  }
}
@media (min-width: 45rem) {
  .devtable col.c-name {
    width: 14rem;
  }
}
@media (min-width: 68.75rem) {
  .cols.side {
    grid-template-columns: 16rem minmax(0, 1fr);
  }
  .cols .sticky {
    position: sticky;
    top: 1rem;
  }
}

.audittable {
  table-layout: fixed;
}
.audittable col.c-time {
  width: 9.5rem;
}
.audittable col.c-actor {
  width: 11rem;
}
.audittable col.c-action {
  width: 12.5rem;
}
.audittable col.c-target {
  width: 9rem;
}
.audittable col.c-target {
  width: 8rem;
}
.audittable col.c-res {
  width: 6rem;
}
.audittable col.c-det {
  width: 6.5rem;
}
.audittable td.nowrap {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.audittable td.res {
  white-space: normal;
  overflow-wrap: anywhere;
}
.audittable td.res .badge {
  display: -webkit-box;
  width: fit-content;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
  white-space: normal;
  border-radius: 0.8rem;
  max-width: 100%;
}
.audittable td.detail {
  position: relative;
  overflow: visible;
}
.audittable summary {
  cursor: pointer;
  list-style: none;
}
.audittable summary::-webkit-details-marker {
  display: none;
}
.audittable details[open] > summary {
  background: var(--color-accent-bg);
  color: var(--color-accent-fg);
  border-color: var(--color-accent);
}
.audittable .tip {
  display: none;
  position: absolute;
  z-index: var(--z-menu);
  right: 0;
  top: calc(100% - 0.2rem);
  width: min(34rem, 80vw);
  padding: 0.4rem 0.6rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-lg);
}
.audittable .tip pre {
  margin: 0;
  border: 0;
  padding: 0;
  background: none;
  font-size: 0.8rem;
  max-height: 15lh;
  overflow: hidden;
}
.audittable td.detail:hover .tip,
.audittable td.detail:focus-within .tip {
  display: block;
}
.audittable td.detail:has(details[open]) .tip {
  display: none;
}
.audittable tr.detail-row {
  display: none;
}
.audittable tr:has(details[open]) + tr.detail-row {
  display: table-row;
}
.audittable tr.detail-row td {
  background: var(--color-surface-2);
}
.audittable tr.detail-row p {
  margin: 0 0 0.4rem;
  overflow-wrap: anywhere;
}
.audittable tr.detail-row pre {
  margin: 0;
  font-size: 0.82rem;
  max-height: 24rem;
  overflow: auto;
}
@media (max-width: 72rem) {
  .audittable {
    table-layout: auto;
  }
}
@media (max-width: 44.99rem) {
  .audittable colgroup {
    display: none;
  }
}
.cols.side .summary-bar {
  flex-direction: column;
  align-items: flex-start;
}
@media (min-width: 68.75rem) {
  .cols.opts {
    grid-template-columns: 23rem minmax(0, 1fr);
  }
}
.cols.opts .stack {
  max-width: none;
}
.cols.opts fieldset {
  margin-top: 0;
}
@media (max-width: 68.74rem) {
  .cols.opts .sticky {
    margin-bottom: 1rem;
  }
}
.sysform fieldset.fgrid {
  background: var(--color-surface);
  padding: 1rem 1.25rem 1.25rem;
}
@media (min-width: 68.75rem) {
  .sysform fieldset.fgrid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem 1.5rem;
  }
}
.sysform label {
  display: grid;
  gap: 0.3rem;
}
.sysform input:not([type="checkbox"]),
.sysform select,
.sysform textarea {
  width: 100%;
}
.savebar {
  position: sticky;
  bottom: var(--sb-h);
  z-index: var(--z-sticky);
  margin: 1rem 0 0;
  padding: 0.7rem 0;
  background: var(--color-bg);
  border-top: 1px solid var(--color-border);
}
.savebar[hidden] {
  display: none !important;
}
@media (min-width: 68.75rem) {
  .cols.disco {
    grid-template-columns: minmax(0, 1fr);
  }
}
@media (min-width: 96rem) {
  .cols.disco {
    grid-template-columns: 23rem minmax(0, 1fr);
    gap: 0 1.25rem;
  }
}
.rules-layout {
  display: grid;
  gap: 1.25rem;
  align-items: start;
}
.rules-main,
.rules-side {
  display: grid;
  gap: 1.25rem;
  min-width: 0;
}
@media (min-width: 88rem) {
  .rules-layout {
    grid-template-columns: minmax(0, 3fr) minmax(20rem, 2fr);
  }
  .rules-side {
    position: sticky;
    top: 1rem;
  }
}
.rules-layout section.card {
  margin: 0;
}
.rule-editor {
  display: grid;
  gap: 0.9rem;
}
.rule-editor .field {
  display: grid;
  gap: 0.3rem;
}
.rule-editor input:not([type="checkbox"]):not([type="radio"]),
.rule-editor select,
.rule-editor textarea {
  width: 100%;
}
.rule-editor p {
  margin: 0;
}
label.chip {
  display: inline-flex;
  gap: 0.35rem;
  align-items: center;
  padding: 0.1rem 0.55rem;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--r-control);
  background: var(--color-surface);
  font-weight: 450;
  font-size: 0.9rem;
  cursor: pointer;
  /* a chip never gets wider than its list: a long unbroken device name wraps inside it */
  max-width: 100%;
  min-width: 0;
  overflow-wrap: anywhere;
}
label.chip > input {
  flex: none;
}
label.chip:has(input:checked) {
  background: var(--color-accent-bg);
  color: var(--color-accent-fg);
  border-color: var(--color-accent);
}
label.chip:has(input:focus-visible) {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
.tag-except {
  border-style: dashed;
}
.shortid {
  border-bottom: 1px dotted var(--color-text-2);
  cursor: help;
  font-family: var(--font-mono);
  font-size: 0.9em;
}
@media (max-width: 44.99rem) {
  main:has(.actionbar) {
    padding-bottom: calc(8rem + var(--sb-h));
  }
  .actionbar {
    position: fixed;
    z-index: var(--z-header);
    left: 0;
    right: 0;
    bottom: var(--sb-h);
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
    justify-content: flex-end;
    margin: 0;
    padding: 0.55rem 0.75rem;
    background: var(--color-surface);
    border-top: 1px solid var(--color-border-strong);
    box-shadow: 0 -4px 14px rgb(0 0 0 / 0.1);
  }
  .actionbar p {
    margin: 0;
  }
  .actionbar > [data-summary] {
    flex: 1 1 100%;
    font-size: 0.84rem;
    line-height: 1.3;
  }
  .actionbar > button,
  .actionbar > .button {
    flex: 1 1 0;
    min-width: 0;
  }
  .actionbar > details {
    flex: none;
  }
  table.dense td small:not(.keep) {
    display: none;
  }
  table.dense td.p1 br,
  table.dense td.ctx > .p1 br {
    display: none;
  }
  table.dense td.ctx > .p1 strong ~ .lbl {
    display: none;
  }
  table.dense td.ctx {
    display: contents;
  }
  table.dense td.ctx > .p1 {
    order: 1;
    flex: 1 1 0;
    min-width: 0;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  table.dense.rules td.ctx > .p1 {
    white-space: normal;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
  }
  table.dense.rules td.s.grow {
    flex: 1 1 auto;
    overflow: visible;
    white-space: normal;
  }
  table.dense.rules td.s .chips {
    flex-wrap: wrap;
  }
  table.dense td.ctx > details {
    order: 4;
    flex: none;
    font-size: 0.84rem;
    color: var(--color-text-2);
  }
  table.dense td.ctx > details[open] {
    order: 5;
    flex: 1 0 100%;
  }
  table.dense td.ctx > .error {
    order: 5;
    flex: 1 0 100%;
  }
  label.chip {
    padding: 0 0.5rem;
    font-size: 0.86rem;
  }
  .rule-editor {
    gap: 0.4rem;
  }
  .rule-editor .notice,
  .rule-editor .error {
    margin: 0.2rem 0;
    padding: 0.4rem 0.6rem;
    font-size: 0.88rem;
    line-height: 1.35;
  }
  .rule-editor .notice,
  .rule-editor .error:not(span):not(small) {
    padding-left: 40px; /* room for the ::before glyph, as in the base callout */
  }
  .rule-editor .muted:has(+ .chips),
  .rule-editor .muted > small {
    display: none;
  }
  .rules-layout,
  .rules-main,
  .rules-side {
    gap: 0.75rem;
    grid-template-columns: minmax(0, 1fr);
  }

  .cols.opts {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.75rem;
  }
  .picker label {
    font-weight: 550;
  }
  .picker td.p1 {
    white-space: normal;
  }
  .picker.dense tbody tr::after {
    display: none;
  }
  .picker.dense td.p2,
  .picker.dense td.s {
    order: 2;
  }
  .picker.dense .badge.st-online,
  .picker.dense td.s small {
    display: none;
  }
  .cols.opts fieldset {
    border: 0;
    padding: 0;
  }
  .cols.opts legend {
    padding: 0;
    font-size: 1.05rem;
  }
  .picker td.p1 label {
    display: inline;
  }
}
@media (max-width: 44.99rem) {
  .audittable.dense td.p1 {
    flex: none;
  }
  .audittable.dense td.p2 {
    max-width: 45%;
  }
  .audittable.dense td.p1x {
    display: block;
    order: 4; /* under the action line, after time and user: what was changed, on a line of its own */
    flex: 1 0 100%;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--color-text-2);
    font-size: 0.88rem;
  }
  /* the target needs no label here: it is the line under time and user */
  .audittable.dense
    td.p1x[data-label]:not(.p1):not(.p2):not(.sel):not(.s):not(
      .rowacts
    )::before {
    content: none;
  }
  /* the kind chip (Proxy, SSO) is for the desktop column; the name says who */
  .audittable.dense .who:not(.who-system) .tag {
    display: none;
  }
  .audittable.dense td.res .badge {
    -webkit-line-clamp: 1;
    line-clamp: 1;
  }
  .audittable.dense td.detail {
    overflow: visible;
  }
  .audittable.dense td.detail .tip {
    display: none !important;
  }
  .audittable.dense summary {
    min-height: 1.6rem;
    padding: 0.1rem 0.5rem;
    font-size: 0.82rem;
  }
  .audittable.dense tr.detail-row,
  .audittable.dense tbody tr.detail-row:nth-child(even) {
    display: none;
  }
  .audittable.dense tr:has(details[open]) + tr.detail-row,
  .audittable.dense
    tbody
    tr:has(details[open])
    + tr.detail-row:nth-child(even) {
    display: block;
    padding: 0.4rem 0.75rem;
    background: var(--color-surface-2);
  }
  .audittable.dense tr.detail-row td {
    display: block;
  }
}
@media (max-width: 44.99rem) {
  .actionbar details.rowmenu {
    position: relative;
  }
  .actionbar details.rowmenu > summary {
    list-style: none;
  }
  .actionbar details.rowmenu > summary::-webkit-details-marker {
    display: none;
  }
  .actionbar .rowmenu-body {
    position: absolute;
    right: 0;
    bottom: calc(100% + 0.6rem);
    min-width: 11rem;
    margin: 0;
    padding: 0.4rem;
    justify-items: stretch;
    background: var(--color-surface);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--r-panel);
    box-shadow: var(--shadow-lg);
  }
  .actionbar .rowmenu-body a,
  .actionbar .rowmenu-body button {
    justify-content: flex-start;
    width: 100%;
  }
}
@media (min-width: 45rem) {
  .colpanel .colmove {
    flex: none;
    min-height: 0;
    width: 26px;
    height: 26px;
    padding: 0;
    border-radius: 5px;
    font-size: 13px;
    line-height: 1;
    box-shadow: none;
    border: 0;
    background: none;
    color: var(--color-text-2);
  }
  .colpanel .colmove:hover:not([disabled]) {
    background: var(--color-surface-3);
  }
  .colpanel .colmove[disabled] {
    opacity: 0.35;
    cursor: default;
  }
  th.drop-l {
    box-shadow: inset 3px 0 0 var(--color-accent);
  }
  th.drop-r {
    box-shadow: inset -3px 0 0 var(--color-accent);
  }
  table.tfix {
    table-layout: fixed;
  }
  table.tfix > thead > tr > th {
    overflow: hidden;
    text-overflow: ellipsis;
  }
  table.tfix > tbody > tr > td:not(.detail) {
    overflow: hidden;
    overflow-wrap: anywhere;
  }
  /* the row menu opens inside its cell: a cell that cuts off its content would hide the opened menu (v0.10.1) */
  table.tfix > tbody > tr > td.rowacts {
    overflow: visible;
  }
  table.tfix .c-fam,
  table.tfix .c-up,
  table.tfix .c-wifi,
  table.tfix .c-restart,
  table.tfix .c-ota,
  table.tfix .c-gt {
    display: table-cell;
  }
  table.tfix .c-fam .tag {
    white-space: nowrap;
  }
  table.hc1 > :is(thead, tbody) > tr > :is(th, td):nth-child(1):not([colspan]) {
    display: none !important;
  }
  table.hc2 > :is(thead, tbody) > tr > :is(th, td):nth-child(2):not([colspan]) {
    display: none !important;
  }
  table.hc3 > :is(thead, tbody) > tr > :is(th, td):nth-child(3):not([colspan]) {
    display: none !important;
  }
  table.hc4 > :is(thead, tbody) > tr > :is(th, td):nth-child(4):not([colspan]) {
    display: none !important;
  }
  table.hc5 > :is(thead, tbody) > tr > :is(th, td):nth-child(5):not([colspan]) {
    display: none !important;
  }
  table.hc6 > :is(thead, tbody) > tr > :is(th, td):nth-child(6):not([colspan]) {
    display: none !important;
  }
  table.hc7 > :is(thead, tbody) > tr > :is(th, td):nth-child(7):not([colspan]) {
    display: none !important;
  }
  table.hc8 > :is(thead, tbody) > tr > :is(th, td):nth-child(8):not([colspan]) {
    display: none !important;
  }
  table.hc9 > :is(thead, tbody) > tr > :is(th, td):nth-child(9):not([colspan]) {
    display: none !important;
  }
  table.hc10
    > :is(thead, tbody)
    > tr
    > :is(th, td):nth-child(10):not([colspan]) {
    display: none !important;
  }
  table.hc11
    > :is(thead, tbody)
    > tr
    > :is(th, td):nth-child(11):not([colspan]) {
    display: none !important;
  }
  table.hc12
    > :is(thead, tbody)
    > tr
    > :is(th, td):nth-child(12):not([colspan]) {
    display: none !important;
  }
  table.hc13
    > :is(thead, tbody)
    > tr
    > :is(th, td):nth-child(13):not([colspan]) {
    display: none !important;
  }
  table.hc14
    > :is(thead, tbody)
    > tr
    > :is(th, td):nth-child(14):not([colspan]) {
    display: none !important;
  }
  table.hc15
    > :is(thead, tbody)
    > tr
    > :is(th, td):nth-child(15):not([colspan]) {
    display: none !important;
  }
  table.hc16
    > :is(thead, tbody)
    > tr
    > :is(th, td):nth-child(16):not([colspan]) {
    display: none !important;
  }
  .colhandle {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 10px;
    z-index: 0; /* below the pinned columns (z-index 1): the handle of a column scrolled under them must not show through */
    cursor: col-resize;
    touch-action: none;
    user-select: none;
  }
  .colhandle::after {
    content: "";
    position: absolute;
    top: 26%;
    bottom: 26%;
    right: 4px;
    width: 1px;
    background: var(--color-border-strong);
    transition: background-color 0.12s;
  }
  .colhandle:hover::after,
  .colhandle:focus-visible::after,
  .colhandle.is-drag::after {
    background: var(--color-accent);
  }
  .colhandle:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: -2px;
    border-radius: 2px;
  }
  body.is-resizing {
    cursor: col-resize;
    user-select: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .colhandle::after {
    transition: none;
  }
}
.chip[title] {
  cursor: help;
}
@media (max-width: 44.99rem) {
  td.none {
    display: none;
  }
}
.summary-line {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.6rem;
  align-items: center;
  margin: 0 0 0.75rem;
}
@media (max-width: 44.99rem) {
  label.chip,
  label.inline,
  label:has(> input[type="checkbox"]:not([aria-label])) {
    min-height: 2.5rem;
    display: flex;
    align-items: center;
  }
  .devtable.dense td.s small:not(.keep) {
    display: none;
  }
  .devtable.dense td.c-up:has(> .muted:only-child),
  .devtable.dense td.c-wifi:has(> .muted:only-child) {
    display: none;
  }
  .picker.dense tbody tr {
    padding-block: 0.1rem;
  }
  .audittable.dense tbody tr:not(.detail-row) {
    position: relative;
    min-height: 3.1rem;
    padding-left: var(--sp-12); /* the card has no side padding of its own */
    padding-right: 5.6rem;
  }
  .audittable.dense td.detail {
    position: absolute;
    right: 0.75rem;
    top: 50%;
    transform: translateY(-50%);
  }
}
.setgrid {
  display: grid;
  gap: 1.5rem 1.5rem;
  align-items: start;
}
@media (min-width: 72rem) {
  .setgrid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.setgrid > .card,
.setgrid > section.card:first-child {
  margin: 0;
  scroll-margin-top: 4rem;
} /* the first card too: section.card:first-child would drop it 12px below its neighbour */
.kv {
  display: grid;
  gap: 0.3rem 0;
}
.kv-row {
  display: grid;
  grid-template-columns: minmax(7.5rem, 15rem) minmax(0, 1fr);
  gap: 0.5rem 1rem;
  align-items: center;
  padding: 0.15rem 0;
  border-bottom: 1px solid var(--color-border);
}
.kv-row:last-child {
  border-bottom: 0;
}
.kv-key {
  display: flex;
  align-items: center;
  gap: 0.1rem;
  min-width: 0;
  font-weight: 550;
}
.kv-key label {
  overflow-wrap: anywhere;
}
.kv-val {
  min-width: 0;
  display: grid;
  gap: 0.2rem;
}
.kv-val input:not([type="checkbox"]),
.kv-val select {
  width: 100%;
}
.kv-short .kv-val input {
  max-width: 9rem;
}
.kv-row.is-changed .kv-val > input:not([type="checkbox"]),
.kv-row.is-changed .kv-val > select {
  border-color: var(--color-accent);
  box-shadow: inset 3px 0 0 var(--color-accent);
}
@media (max-width: 36rem) {
  .kv-row {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.15rem;
    padding: 0.35rem 0;
  }
}
label.is-changed > input:not([type="checkbox"]),
label.is-changed > select {
  border-color: var(--color-accent);
  box-shadow: inset 3px 0 0 var(--color-accent);
}
form[data-track-changes] .savebar .actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
  margin: 0;
}
.statchips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 1.1rem;
}
.statchips a.badge {
  text-decoration: none;
  padding-block: 0.3rem;
  font-size: 0.92rem;
}
.statchips a.badge.is-on {
  outline: 2px solid var(--color-text);
  outline-offset: 1px;
}
@media (max-width: 44.99rem) {
  .statchips a.badge,
  .statchips .button {
    display: inline-flex;
    align-items: center;
    min-height: 2.5rem;
  }
}
@media (max-width: 87.99rem) {
  .rules-layout {
    grid-auto-flow: row;
  }
  .rules-layout > .rules-main,
  .rules-layout > .rules-side {
    display: contents;
  }
  .rules-layout #editor {
    order: -1;
  }
}
.sysform .fld {
  display: grid;
  gap: 0.3rem;
  align-content: start;
}
.sysform .fld label.reset {
  font-weight: 400;
  font-size: 0.9rem;
  color: var(--color-text-2);
}
@media (max-width: 44.99rem) {
  #txtable tbody tr {
    position: relative;
  }
  #txtable td.p1 > a:first-child::after {
    content: "";
    position: absolute;
    inset: 0;
  }
  #txtable td.p1 small a {
    position: relative;
    z-index: 1;
  }
}
.chan-ok {
  color: var(--color-ok-fg);
}
.colslot {
  display: none;
}

details.job-log > summary {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  list-style: none;
}
details.job-log > summary::-webkit-details-marker {
  display: none;
}
details.job-log > summary::after {
  content: "";
  flex: none;
  box-sizing: border-box;
  width: 0.45rem;
  height: 0.45rem;
  margin: 0 0.2rem 0 auto;
  border-right: 2px solid var(--color-text-2);
  border-bottom: 2px solid var(--color-text-2);
  transform: translateY(-0.12rem) rotate(45deg);
}
details.job-log[open] > summary::after {
  transform: translateY(0.1rem) rotate(225deg);
}
details.job-log > summary {
  display: flex;
}
.notes,
#detail li {
  overflow-wrap: anywhere;
}

@media (max-width: 44.99rem) {
  table.has-sel tbody tr:not(:has(> td.sel)) {
    padding-left: 3rem;
  }
}
@media (max-width: 44.99rem) {
  table.dense td.s.note {
    flex: 1 0 100%;
    white-space: normal;
  }
  .cols.disco {
    row-gap: 0;
  }
}
label.invalid {
  outline: 2px solid var(--color-danger);
  outline-offset: 0.2rem;
  border-radius: var(--r-sm);
}
.kv-key {
  display: block;
}
.kv-key label {
  display: inline;
}
table.rules td[data-label="Findings"] {
  white-space: nowrap;
}
@media (max-width: 44.99rem) {
  .rule-editor input,
  .rule-editor select {
    text-overflow: ellipsis;
  }
}
.audittable col.c-time {
  width: 11.75rem;
}
.audittable col.c-actor {
  width: 12rem;
}
.audittable col.c-action {
  width: 10.5rem;
}
.audittable col.c-target {
  width: 13rem;
}
.audittable col.c-res {
  width: 7.5rem;
}
.audittable td.res .badge {
  white-space: nowrap;
}
.audittable td.res .badge:not(:has(+ *)) {
  white-space: normal;
}
@media (max-width: 44.99rem) {
  .audittable.dense td.p1x > a {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    line-height: 2.5rem;
    min-height: 0;
  }
}
.savebar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.9rem;
}
.savebar small {
  flex: 1 1 14rem;
}
@media (max-width: 44.99rem) {
  .cols.side {
    row-gap: 0.5rem;
  }
}
.setup-intro {
  margin-bottom: 1.4rem;
}
@supports selector(::details-content) {
  @media (min-width: 45rem) {
    .stack > .actionbar {
      display: contents;
    }
  }
}

/* ==== phone cards for tables without .dense (Users, Firmware files this fleet needs): one block per row, label before value ==== */
@media (max-width: 44.99rem) {
  table:not(.dense),
  table:not(.dense) thead,
  table:not(.dense) tbody,
  table:not(.dense) tr,
  table:not(.dense) th,
  table:not(.dense) td {
    display: block;
  }
  table:not(.dense) thead {
    position: absolute;
    left: -9999px;
  }
  table:not(.dense) tbody tr,
  table:not(.dense) tbody tr:hover {
    margin: 0 0 var(--sp-12);
    padding: var(--sp-4) 0;
    background: var(--color-surface);
    border: var(--hair-w) solid var(--color-border);
  }
  table:not(.dense) tbody td,
  table:not(.dense) tbody tr:hover td {
    background: none;
  }
  table:not(.dense) td {
    display: flex;
    flex-wrap: wrap; /* bare children (links, tags, small notes) wrap instead of widening the page */
    gap: var(--sp-12);
    justify-content: space-between;
    align-items: baseline;
    border: 0;
    padding: var(--sp-4) var(--sp-12);
    text-align: right;
  }
  table:not(.dense) td[data-label]::before {
    content: attr(data-label);
    color: var(--color-text-2);
    font-size: var(--fs-ui);
    font-weight: 500;
    text-align: left;
    flex: none;
  }
  table:not(.dense) td.empty {
    display: block;
    text-align: center;
  }
  table:not(.dense) td.wrap {
    min-width: 0;
  }
  table:not(.dense) td .cell,
  table:not(.dense) td .chips {
    min-width: 0;
    flex: 1 1 0;
    justify-content: flex-end;
    overflow-wrap: anywhere;
  }
}

/* ==== phone targets (QA #13 #84) ==== */
@media (max-width: 44.99rem) {
  table.dense td.sel .hit {
    min-width: 40px;
  }
  td .cell .dev a {
    display: inline-block;
    min-height: 24px;
    line-height: 24px;
  }
}

/* ==== column resize handles (tables.js) sit on the right edge of their header cell ==== */
thead th {
  position: relative;
}

/* ==== phone targets, second pass: rules above lose to patterns.css's min-width: 0 ==== */
@media (max-width: 44.99rem) {
  table.dense td.sel > .hit {
    min-width: 40px !important;
  }
  table.dense td .cell {
    flex: 1 1 0;
    min-width: 0;
  }
  details.job-log > summary {
    min-height: 40px;
  }
}

/* the bulk bar lies above the Columns button and row menus that scroll under it */
.bulkbar {
  z-index: calc(var(--z-menu) + 1);
}

/* a bare checkbox in a selection cell sits on the middle of the row */
td.sel > input[type="checkbox"] {
  display: block;
  margin: 0 auto;
}

@media (max-width: 44.99rem) {
  /* cached files: the file name keeps its width, release and chip go on the line below */
  table[data-table="firmware_files"] td.p1 {
    flex-wrap: wrap;
  }
  table[data-table="firmware_files"] td.p1 > small.keep {
    flex: 0 0 100%;
    /* Geist is wider than Archivo was: "16.1.0 · esp32c3 / tasmota" no longer fits the 130px file column
       in one run and would run into the Size column, so on a phone it may wrap between its words */
    white-space: normal;
  }
}

/* an audit result badge wraps instead of clamping: a -webkit-box has no flex gap and no centred label */
.audittable td.res .badge {
  display: inline-flex;
  -webkit-line-clamp: unset;
  line-clamp: unset;
  overflow: visible;
}

td.sel > label.hit {
  display: flex;
  margin-inline: auto;
}

/* phone device rows: two lines (name, power, Actions / status with its word), tight padding */
@media (max-width: 44.99rem) {
  .devtable.dense tbody tr {
    padding: var(--sp-4) 0;
    gap: 0 var(--sp-12);
  }
  .devtable.dense td.s {
    min-height: 20px;
  }
  .devtable.dense td.sel {
    top: var(--sp-4);
  }
}

/* phone density: one-line selection rows are exactly one touch target high; card folds sit closer */
@media (max-width: 44.99rem) {
  table.picker.dense tbody tr {
    padding-block: 0;
  }
  table.picker.dense td.sel {
    top: 0;
  }
}

@media (max-width: 44.99rem) {
  table:not(.dense) td.rowacts {
    width: auto;
    white-space: normal;
  }
}

/* phone density: tight row padding in every dense list */
@media (max-width: 44.99rem) {
  table.dense tbody tr {
    padding-block: var(--sp-4);
  }
  table.dense td.s {
    min-height: 20px;
  }
  table.dense td.sel {
    top: var(--sp-4);
  }
}
@media (max-width: 44.99rem) {
  section.card {
    margin: var(--sp-24) 0 var(--sp-12);
  }
  section.card:first-child {
    margin-top: var(--sp-12);
  }
}

@media (max-width: 44.99rem) {
  table[data-table="policy_devices"] tbody tr {
    padding-block: 2px;
  }
  table[data-table="policy_devices"] td.sel {
    top: 2px;
  }
}

.card > details.fold-h > .tablewrap,
.card > details.fold-h > table {
  margin-top: var(--sp-12);
}
.card > details.fold-h > form,
.card > details.fold-h > summary + p {
  margin-top: var(--sp-12);
}

/* H2 pages */
/* the pager is the same at every width: Previous, position, Next, then the page sizes (#71) */
.pager {
  justify-content: flex-start;
  row-gap: var(--sp-8);
}
.pager-of {
  font-weight: 600;
  padding-inline: var(--sp-8);
}

.pager-size {
  margin-left: auto;
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  align-items: center;
}
@media (max-width: 44.99rem) {
  .pager-size {
    margin-left: 0;
    width: 100%;
  }
}
/* QA #94: the version string (v0.8.0-18-g0782517) stays inside its card at 375 px */

/* QA #79: "Finish the setup" follows the last step at every width (not sticky, not inside a step) */
.setup-finish {
  margin: var(--sp-16) 0 0;
}
.setup-finish > p {
  margin: 0;
}
/* the intro line of a framed fold keeps its distance from the title (layout guard f) */
section.card > details.fold-h > summary + p.fold-intro {
  margin-top: var(--sp-12);
}
/* phone: the page sizes of the pager are 40 px targets like every other control */
@media (max-width: 44.99rem) {
  .pager-size .button {
    min-width: 2.5rem;
    justify-content: center;
  }
}

/* QA v0.9.0 phone rows that hid what identifies them */
@media (max-width: 44.99rem) {
  /* proposals: name and kind badge share the first line, facts are labelled lines, Ignore and Add device get the last line */
  table[data-table="discovery"] td.rowacts {
    order: 6;
    flex: 0 0 100%;
  }
  /* policy devices: the name wraps instead of being clipped (a clipped name cannot be told from its neighbours);
     status and Details stay beside a short name; a name too long for that takes the first line and wraps (flex-basis auto
     breaks the line by the name's own width), with status and Details on the line below */
  table[data-table="policy_devices"] td.p1 {
    flex: 1 1 auto;
  }
  table[data-table="policy_devices"] td.p1 > a {
    white-space: normal;
    overflow-wrap: anywhere;
  }
}

/* Bulk restart confirmation: a long device name wraps instead of widening the card. */
table[data-table="bulk_command"] td.p1 a {
  overflow-wrap: anywhere;
}

/* ---- package I: users, system settings, audit ---------------------------------- */
/* The row menu holds action items only; the one field in it (new password) is padded like an item. */
.rowmenu-body form.menu-field {
  display: grid;
  gap: var(--sp-4);
  padding: var(--sp-8) var(--sp-12);
}
.rowmenu-body form.menu-field label {
  font-weight: 500;
}
.rowmenu-body form.menu-field button {
  padding: 0;
}
/* Read-only (environment) settings read like editable ones: name, small key line, value, hint. */
.sysform .fld.ro {
  gap: 0.3rem;
}
.sysform .ro-label {
  font-size: var(--fs-ui);
  font-weight: 600;
  color: var(--color-text);
}
.sysform .ro-label small {
  font-size: var(--fs-small);
  font-weight: 400;
  color: var(--color-text-2);
}
.sysform .ro-value {
  overflow-wrap: anywhere;
}
.sysform .fld.ro > small {
  font-size: var(--fs-small);
  color: var(--color-text-2);
}
@media (max-width: 44.99rem) {
  /* an Actions cell with nothing in it (SSO row without sessions, proxy row) is not a row of its own */
  table td.rowacts:empty {
    display: none;
  }
}
/* ---- QA v0.9.0 follow-up: policy pages (packages K, L) ---------- */

/* L: closed card folds on a phone are one 40 px target each, also when they are stacked (Setup steps) */
@media (max-width: 44.99rem) {
  .card:has(> details.fold-h:first-child:not([open])) {
    padding-top: var(--sp-16);
  }
}

/* K: the card footer of a confirmation stays inside the card's padding box on a phone */
@media (max-width: 44.99rem) {
  .confirm .dlg-foot {
    margin-inline: calc(var(--sp-12) * -1);
    padding-inline: var(--sp-12);
  }
}

/* K: policy rules on a phone: the rule name is the title, the setting its subtitle, then labelled lines */
@media (max-width: 44.99rem) {
  table.dense.rules td.ctx {
    display: block;
    order: 1;
    flex: 1 1 0;
    min-width: 0;
  }
  table.dense.rules td.ctx > .p1 {
    display: block;
    overflow: visible;
    white-space: normal;
    -webkit-line-clamp: unset;
    line-clamp: unset;
  }
  table.dense.rules td.ctx > .p1 strong ~ .lbl {
    display: block;
    font-weight: 400;
    font-size: var(--fs-ui);
    color: var(--color-text-2);
  }
  table.dense.rules td.s {
    flex-basis: 100%;
  }
  table.dense.rules td.s[data-label]::before {
    content: attr(data-label);
    flex: none;
    width: 6.5rem;
    font-size: var(--fs-small);
    color: var(--color-text-2);
  }
  table.dense.rules td.s .chips {
    flex: 1 1 0;
    min-width: 0;
  }
  /* make compliant: the commands and what is not changed carry their label too */
  table[data-table="policy_fix"] td.s {
    flex-wrap: wrap;
    align-items: flex-start;
  }
  table[data-table="policy_fix"] td.s[data-label]::before {
    content: attr(data-label);
    flex: 1 0 100%;
    font-size: var(--fs-small);
    color: var(--color-text-2);
  }
  table[data-table="policy_fix"] td.s > div {
    flex: 1 0 100%;
    min-width: 0;
  }
}
/* K: where the values came from stays on one line; the Export button keeps its distance from the table */
table[data-table="policy_devices"] td[data-label="Source"] {
  white-space: nowrap;
}
p.actions.export-row {
  margin-top: var(--sp-12);
}

/* J: a selected row of the update picker on a phone. The row paints the highlight and the accent bar
   (table.dense tr:has(> td.sel input:checked)); the desktop rule above that gives every cell its own
   background left a band in the gaps between cells, and the cells hid the accent bar on all but
   the first line. */
@media (max-width: 44.99rem) {
  .picker.dense tbody tr:has(input:checked) td {
    background: transparent !important;
  }
}

/* a table with no row has no use for its header (sort, select all, resize): tableprefs.go marks it */
table.is-empty > thead {
  display: none;
}
/* cached-file meta (release, chip / variant) stays on one run; the line may wrap between the parts of the row, not inside it */
table[data-table="firmware_files"] small.keep {
  white-space: nowrap;
}
@media (max-width: 44.99rem) {
  /* names a release needs: a left-aligned run, like the lists above it */
  table[data-table="firmware_cached"] td .cell {
    justify-content: flex-start;
    text-align: left;
  }
}

/* ---- QA v0.9.0 follow-up: global CSS, native controls, dialogs ------------------------------- */
/* zero tiles stay readable: text-3 is for disabled things only */
.stat.zero .num {
  color: var(--color-text-2);
}

/* sort icons: at least 3:1 against the header */
.sortmark {
  opacity: 0.85;
}
.sortlink:hover .sortmark,
.sortlink.is-hover .sortmark {
  opacity: 0.9;
}

/* the select-all box sits on the same line as the row boxes below it */
th.sel > input[type="checkbox"]:not([hidden]) {
  display: block;
  margin: 0 auto;
}

/* stat-card legends: the part above the rule is the page, not the card */

/* an action row or form right below a table keeps a gap */
table + .actions,
table + form,
.table-fit + .actions,
.table-fit + form,
.tablewrap + .actions,
.tablewrap + form {
  margin-top: var(--sp-16);
}

.nw {
  white-space: nowrap;
}

/* phone menu: left-aligned rows, a user name as written, room under the last row */

/* checkbox and radio: an invalid one is outlined, not struck by the text field's red bar */
input[type="checkbox"][aria-invalid="true"],
input[type="radio"][aria-invalid="true"] {
  box-shadow: none;
  outline: 2px solid var(--color-danger);
  outline-offset: 1px;
}
label.invalid {
  outline-offset: 0.35rem;
  border-radius: 0;
}

/* native progress: flat, full width, accent fill */
progress {
  appearance: none;
  display: block;
  width: 100%;
  height: 0.5rem;
  margin: var(--sp-12) 0;
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: var(--r-xs);
  background: var(--color-surface);
  color: var(--color-accent);
  overflow: hidden;
}
progress::-webkit-progress-bar {
  background: transparent;
  border-radius: var(--r-xs);
}
progress::-webkit-progress-value {
  background: var(--color-accent);
  border-radius: var(--r-xs);
}
progress::-moz-progress-bar {
  background: var(--color-accent);
  border-radius: var(--r-xs);
}
progress:indeterminate {
  background: linear-gradient(90deg, var(--color-accent) 50%, transparent 50%) 0
    0 / 2rem 100% repeat-x;
  animation: tf-indeterminate 1s linear infinite;
}
@keyframes tf-indeterminate {
  to {
    background-position: 2rem 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  progress:indeterminate {
    animation: none;
  }
}

/* native file input: the button looks like button.sm */
input[type="file"] {
  font: inherit;
  font-size: var(--fs-body);
  color: var(--color-text);
  width: 100%;
  min-width: 0;
}
input[type="file"]::file-selector-button {
  height: var(--ctl-sm);
  margin-right: var(--sp-12);
  padding: 0 var(--ctl-pad-sm);
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: var(--r-control);
  background: var(--color-surface);
  color: var(--color-text);
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-ui);
  cursor: pointer;
}
input[type="file"]::file-selector-button:hover {
  background: color-mix(in srgb, var(--color-text) 7%, var(--color-surface));
  border-color: var(--color-text);
}

@media (max-width: 44.99rem) {
  input[type="file"]::file-selector-button {
    height: 40px;
  }

  /* a label that wraps keeps its checkbox on the first line (centred on it), not in the middle of the block */
  label.chip:has(> input[type="checkbox"]),
  label.inline:has(> input[type="checkbox"]),
  label:has(> input[type="checkbox"]:not([aria-label])) {
    align-items: flex-start;
    padding-block: calc((2.5rem - 1lh) / 2);
  }
  label:not(.hit) > input[type="checkbox"]:not([aria-label]) {
    margin-top: calc((1lh - 18px) / 2);
  }
}

/* ==== long unbreakable text (QA v0.10.0) ====
   A device name, an error chain or a URL can be longer than the screen and have no break opportunity.
   It wraps inside its box instead of widening the page. One rule for prose containers (headings,
   paragraphs, list items, notes, the breadcrumb, dialog text), written with :where() so any
   specific rule still wins; table cells are not in it, because an auto-layout table would squeeze a
   column to a single letter. Flex and grid parents get min-width: 0 so the text can shrink at all. */
:where(main, dialog.dlg, .confirm)
  :where(
    h1,
    h2,
    h3,
    h4,
    p,
    li,
    dd,
    summary,
    legend,
    small,
    .crumb,
    .reason
  ):not(td *) {
  overflow-wrap: anywhere;
}
.page-head h1,
.page-head .crumb,
.dlg-head h2,
.dlg-body,
.dlg-foot {
  min-width: 0;
  overflow-wrap: anywhere;
}
.dlg-head > :not(h2) {
  flex: none; /* Close stays whole and inside the frame */
}
dialog.dlg {
  max-width: calc(100vw - 2rem);
}
dialog.dlg > * {
  min-width: 0;
}
/* overflow-wrap is inherited: in a dialog table it would let auto layout squeeze a column until words
   break mid-letter ("cache|d"); table cells keep normal wrapping, the names in them (.dev) wrap themselves */
.dlg-body table {
  overflow-wrap: normal;
}
/* the device-name rules (.dev) wrap with the same overflow-wrap */
.dev > a {
  overflow-wrap: anywhere;
}
@media (max-width: 44.99rem) {
  dialog.dlg {
    max-width: 100vw;
  }
  /* phone cards: the name is the row's identifier, so it wraps (anywhere) instead of being cut */
  table.dense td.p1 > a,
  table.dense td.p1 > span,
  table.dense td.p1 > span > a {
    overflow: visible;
    text-overflow: clip;
    white-space: normal;
    overflow-wrap: anywhere;
  }
  /* stacked phone cells hold prose (error text, name lists): it wraps inside the card */
  table.dense td,
  table.dense td .reason,
  table.dense td small {
    min-width: 0;
    overflow-wrap: anywhere;
  }
  /* a name that wraps to several lines: the checkbox, Power and Actions stay on its first line (QA #67),
     the name starts at the top edge beside them */
  .devtable.dense tbody tr {
    align-items: flex-start;
  }
  /* a wrapped name in a cell (24px link box) keeps its comma beside the last line, not on a line of its own */
  td .cell .dev {
    display: inline-flex;
    align-items: last baseline;
  }
  td .cell .dev a {
    min-width: 0;
  }
  table[data-table="transaction_retry"] td.p2 {
    flex: 1 1 0;
    flex-wrap: wrap;
    justify-content: flex-start;
  }
}

/* ==========================================================================
   Shell (design v2, WP F3): top bar, sub-navigation, user menu, phone header,
   navigation drawer, crumbs, connection banners, toasts. Values from the
   prototype's markup (design/v2/template.html, the shell before <main>).
   ========================================================================== */
@keyframes tf-pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.3;
  }
}
header.top {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: var(--color-surface);
  border-bottom: var(--hair-w) solid var(--color-border);
}
/* the header's rows and main share one content cap; the backgrounds span the window */
.shell-in {
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 var(--page-pad);
}
.tb-in {
  height: 52px;
  display: flex;
  align-items: center;
  gap: var(--sp-22);
}
.brand {
  display: flex;
  align-items: center;
  gap: var(--sp-9);
  font: 600 15px var(--font-sans);
  letter-spacing: -0.01em;
  color: var(--color-text);
  text-decoration: none;
  white-space: nowrap;
}
.tb-brand {
  display: flex;
  align-items: baseline;
  gap: var(--sp-8);
  min-width: 0;
}
.tb-brand .brand {
  align-self: center;
}
.ver {
  font: 400 11.5px var(--font-mono);
  color: var(--color-text-3);
  text-decoration: none;
  white-space: nowrap;
}
.ver:hover {
  color: var(--color-text);
  text-decoration: underline;
}
.dr-brand .ver {
  margin-left: var(--sp-4);
  display: inline-flex;
  align-items: center;
  min-height: 40px; /* a touch target in the phone drawer */
}
.brand:hover {
  color: var(--color-text);
}
.brand-mark {
  flex: none;
  display: block;
}
.tb-groups {
  display: flex;
  gap: var(--sp-2);
}
.tb-group {
  display: inline-flex;
  align-items: center;
  height: 32px;
  padding: 0 var(--sp-12);
  border-radius: var(--r-control);
  font: 500 13.5px var(--font-sans);
  color: var(--color-text-2);
  text-decoration: none;
  white-space: nowrap;
}
.tb-group:hover {
  color: var(--color-text);
}
.tb-group[aria-current] {
  background: var(--color-surface-3);
  color: var(--color-text);
}
.tb-fill {
  flex: 1;
}

/* ==== status bar (#307, internal/web/ui/statusbar.templ) ====
   Fixed to the window's bottom edge on every page: Live, Broker, Jobs, Backup, Version. A wide row
   from 45rem; below, one 44px line (a details element) that opens into a sheet upwards. The top
   edge carries a 2px line that turns amber or red when something is wrong. data-live on <html>
   (live.js) picks the Live label and dims the items that may be stale. Everything else fixed to the
   bottom edge sits above it (--sb-h). */
@media (max-width: 44.99rem) {
  :root {
    --sb-line: 44px;
  }
}
.sb {
  position: fixed;
  z-index: var(--z-header);
  left: 0;
  right: 0;
  bottom: 0;
  background: var(--color-surface);
  border-top: 1px solid var(--color-border-strong);
  box-shadow: 0 -4px 14px rgb(0 0 0 / 0.07);
  font: 500 12px/1 var(--font-sans);
  color: var(--color-text-2);
  padding-bottom: env(safe-area-inset-bottom, 0px);
}
.sb-edge {
  position: absolute;
  left: 0;
  right: 0;
  top: -1px;
  height: 2px;
  background: transparent;
}
.sb-edge.sb-warn {
  background: var(--color-warn);
}
.sb-edge.sb-bad {
  background: var(--color-danger);
}
html[data-live="reconnecting"] .sb-edge:not(.sb-bad) {
  background: var(--color-warn);
}
html[data-live="offline"] .sb-edge {
  background: var(--color-danger);
}
.sb-wide {
  display: flex;
  align-items: stretch;
  height: var(--sb-line);
}
.sb-i {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-7);
  min-width: 0;
  padding: 0 var(--sp-12);
  white-space: nowrap;
  color: inherit;
  text-decoration: none;
  border-left: 1px solid var(--color-border);
}
/* the group has a visible start and end (#395): a divider before the first and after the last item,
   on the content column's edges, like the dividers between the items */
.sb-wide::before,
.sb-wide::after {
  content: "";
  flex: none;
  width: 1px;
  background: var(--color-border);
}
.sb-i:first-child {
  border-left: 0;
}
a.sb-i:hover {
  background: var(--color-surface-2);
  color: var(--color-text);
}
.sb-i b {
  font-weight: 600;
  color: var(--color-text);
}
.sb-i .mono {
  font: 400 11.5px var(--font-mono);
}
.sb-mut {
  color: var(--color-text-3);
  font-weight: 400;
}
.sb-grow {
  flex: 1;
  border: 0;
}
.sb-ver {
  font: 400 11.5px var(--font-mono);
}
.sb-live {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-7);
}
.sb-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: none;
  background: var(--color-ok);
}
.sb-dot.idle {
  background: transparent;
  box-shadow: inset 0 0 0 1.5px var(--color-text-3);
}
.sb-dot.run {
  background: var(--color-accent);
  animation: tf-pulse 1.6s ease-in-out infinite;
}
.sb-dot.warn {
  background: var(--color-warn);
}
.sb-dot.bad {
  background: var(--color-danger);
}
.sb-i.t-warn {
  background: var(--color-warn-bg);
  color: var(--color-warn-fg);
}
.sb-i.t-warn b {
  color: var(--color-warn-fg);
}
.sb-i.t-bad {
  background: var(--color-danger-bg);
  color: var(--color-danger-fg);
}
.sb-i.t-bad b {
  color: var(--color-danger-fg);
}
.sb-prog {
  display: inline-block;
  width: 54px;
  height: 4px;
  margin: 0 2px;
  border: 0;
  border-radius: 2px;
  background: var(--color-surface-3);
  vertical-align: middle;
}
.sb-prog::-webkit-progress-bar {
  background: var(--color-surface-3);
  border-radius: 2px;
}
.sb-prog::-webkit-progress-value,
.sb-prog::-moz-progress-bar {
  background: var(--color-accent);
  border-radius: 2px;
}
.sb-link {
  color: var(--color-accent-fg);
  font-weight: 600;
  white-space: nowrap;
  text-decoration: none;
}
.sb-sep {
  color: var(--color-text-3);
}
/* the running jobs: one line, the gap stands for the spaces between the words */
.sb-jobs {
  display: inline-flex;
  align-items: center;
  gap: 0.28em;
}

/* the Live label: one of three, by the stream's state */
.sb-l,
.sb-sum-live {
  display: none;
}
html:not([data-live]) .sb-l-ok,
html[data-live="live"] .sb-l-ok,
html[data-live="reconnecting"] .sb-l-re,
html[data-live="offline"] .sb-l-off {
  display: inline;
}
html[data-live="reconnecting"] .sb-live .sb-dot {
  background: var(--color-warn);
}
html[data-live="offline"] .sb-live .sb-dot {
  background: var(--color-danger);
}
html[data-live="reconnecting"] .sb-i:has(.sb-live) {
  background: var(--color-warn-bg);
  color: var(--color-warn-fg);
}
html[data-live="reconnecting"] .sb-i:has(.sb-live) b {
  color: var(--color-warn-fg);
}
html[data-live="offline"] .sb-i:has(.sb-live) {
  background: var(--color-danger-bg);
  color: var(--color-danger-fg);
}
html[data-live="offline"] .sb-i:has(.sb-live) b {
  color: var(--color-danger-fg);
}
/* a lost stream: what the bar says about the broker, jobs and backup may be stale */
html[data-live="reconnecting"] .sb-stale,
html[data-live="offline"] .sb-stale {
  opacity: 0.6;
}

/* phone: one line, tap to open the sheet */
.sb-narrow {
  display: none;
}
@media (max-width: 44.99rem) {
  .sb-wide {
    display: none;
  }
  .sb-narrow {
    display: block;
  }
  .sb-narrow summary {
    list-style: none;
    display: flex;
    align-items: center;
    gap: var(--sp-10);
    height: var(--sb-line);
    padding: 0 var(--sp-16);
    cursor: pointer;
    font-size: 13px;
  }
  .sb-narrow summary::-webkit-details-marker {
    display: none;
  }
  .sb-sum {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--color-text);
  }
  .sb-sum small {
    color: var(--color-text-3);
    font-weight: 400;
    font-size: 12px;
  }
  html[data-live="reconnecting"] .sb-sum-data,
  html[data-live="offline"] .sb-sum-data {
    display: none;
  }
  html[data-live="reconnecting"] .sb-sum-live.sb-l-re,
  html[data-live="offline"] .sb-sum-live.sb-l-off {
    display: inline;
  }
  html[data-live="reconnecting"] .sb-narrow summary .sb-dot {
    background: var(--color-warn);
    animation: none;
  }
  html[data-live="offline"] .sb-narrow summary .sb-dot {
    background: var(--color-danger);
    animation: none;
  }
  .sb-car {
    color: var(--color-text-3);
    font-size: 10px;
    transition: transform 0.15s;
  }
  .sb-narrow[open] .sb-car {
    transform: rotate(180deg);
  }
  .sb-sheet {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 100%;
    background: var(--color-surface);
    border-top: 1px solid var(--color-border-strong);
    box-shadow: 0 -10px 24px rgb(0 0 0 / 0.14);
    padding: var(--sp-4) 0 var(--sp-6);
    /* a landscape phone is short: the sheet scrolls instead of leaving the window at the top */
    max-height: calc(100dvh - var(--sb-h) - var(--sp-16));
    overflow-y: auto;
  }
  .sb-sheet .sb-i {
    display: flex;
    align-items: flex-start;
    border-left: 0;
    padding: var(--sp-12) var(--sp-16);
    font-size: 13px;
    white-space: normal;
    flex-wrap: wrap;
    row-gap: var(--sp-4);
  }
  .sb-sheet .sb-i + .sb-i {
    border-top: 1px solid var(--color-border);
  }
  .sb-sheet .sb-dot {
    margin-top: 4px;
  }
  .sb-lab {
    width: 4.6rem;
    color: var(--color-text-3);
    font-weight: 500;
    flex: none;
  }
  .sb-val {
    flex: 1;
    min-width: 0;
  }
  .sb-sheet .sb-prog {
    display: block;
    width: 100%;
    max-width: 220px;
    margin: 6px 0 0;
  }
}
/* a bar without a stream is a snapshot: nothing pulses */
.sb-static .sb-dot.run {
  animation: none;
}
@media (prefers-reduced-motion: reduce) {
  .sb-dot.run {
    animation: none;
  }
}

/* user menu: a details element (menu.js: one open, Escape, click outside) */
details.usermenu {
  position: relative;
}
summary.um-btn {
  list-style: none;
  height: 34px;
  display: flex;
  align-items: center;
  gap: var(--sp-8);
  padding: 0 var(--sp-8) 0 var(--sp-4);
  border-radius: var(--sp-8);
  border: var(--hair-w) solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text);
  font: 500 13px var(--font-sans);
  cursor: pointer;
}
summary.um-btn::-webkit-details-marker {
  display: none;
}
.avatar {
  position: relative;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--color-accent-bg);
  color: var(--color-accent-fg);
  display: grid;
  place-items: center;
  font: 600 11px var(--font-sans);
  flex: none;
}
.um-caret {
  color: var(--color-text-3);
  font-size: 10px;
}
.um-menu {
  position: absolute;
  right: 0;
  top: 42px;
  width: 240px;
  background: var(--color-surface);
  /* the prototype draws this one menu with --border; like every other overlay it gets the strong
     border, which layout guard (g) requires to be seen against the page (1.3:1) */
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-lg);
  padding: var(--sp-6);
  z-index: var(--z-header-menu);
}
.um-who {
  padding: var(--sp-8) var(--sp-10) var(--sp-10);
  border-bottom: var(--hair-w) solid var(--color-border);
  margin-bottom: var(--sp-6);
}
.um-name {
  font-weight: 600;
}
.um-role {
  font-size: 12px;
  color: var(--color-text-2);
}
.um-theme {
  padding: var(--sp-6) var(--sp-10);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-8);
  font-size: 13px;
}
.um-note {
  margin: 0;
  padding: 0 var(--sp-10) var(--sp-6);
  font-size: 11.5px;
  color: var(--color-text-3);
  text-align: right;
}
.um-menu form {
  margin: 0;
}
.um-menu .um-item {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  text-align: left;
  height: 34px;
  padding: 0 var(--sp-10);
  border: 0;
  border-radius: var(--sp-6);
  background: none;
  color: var(--color-text);
  font: 400 13px var(--font-sans);
  text-decoration: none;
  text-box: normal;
}
.um-menu .um-item:hover {
  background: var(--color-surface-2);
}

/* segmented Light / Dark / Auto (D11) */
.seg {
  display: flex;
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: var(--r-control);
  overflow: hidden;
}
.seg button {
  height: 28px;
  padding: 0 var(--sp-9);
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--color-text);
  font: 500 12px var(--font-sans);
  text-box: normal;
}
.seg button + button {
  border-left: var(--hair-w) solid var(--color-border-strong);
}
.seg button:hover {
  background: var(--color-surface-2);
}
.seg button[aria-pressed="true"] {
  background: var(--color-surface-3);
}

/* sub-navigation: the pages of the current group */
.sn {
  border-top: var(--hair-w) solid var(--color-border);
}
.sn-in {
  height: 40px;
  display: flex;
  align-items: stretch;
  gap: var(--sp-20);
}
.sn-item {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-6);
  padding: 0 var(--sp-2);
  font: 500 13px var(--font-sans);
  color: var(--color-text-2);
  text-decoration: none;
  white-space: nowrap;
}
.sn-item:hover {
  color: var(--color-text);
}
.sn-item[aria-current="page"] {
  color: var(--color-text);
  box-shadow: inset 0 -2px 0 var(--color-accent);
}
.sn-empty {
  display: none;
}

/* phone header (below 45rem) */
.ph {
  display: none;
}
.ph-btn {
  text-decoration: none;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  color: var(--color-text);
  flex: none;
}
.burger {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
.burger > span {
  width: 18px;
  height: 2px;
  border-radius: 1px;
  background: currentColor;
}
.ph-title {
  flex: 1;
  min-width: 0;
  font: 600 16px var(--font-sans);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ph-me .avatar {
  width: 30px;
  height: 30px;
}
@media (max-width: 44.99rem) {
  .tb,
  .sn {
    display: none;
  }
  .ph {
    height: 55px; /* 56 with the header's bottom border, as in the prototype (border-box) */
    display: flex;
    align-items: center;
    gap: var(--sp-6);
    padding: 0 var(--sp-8);
  }
}

/* navigation drawer (phones; /nav shows the same list as a page) */
dialog.drawer {
  position: fixed;
  inset: 0 auto 0 0;
  margin: 0;
  width: min(300px, 86%);
  height: 100%;
  max-height: none;
  max-width: none;
  padding: var(--sp-10) var(--sp-10) var(--sp-24);
  border: 0;
  border-right: var(--hair-w) solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text);
  overflow: auto;
}
dialog.drawer::backdrop {
  background: rgba(10, 14, 18, 0.45);
}
/* The backdrop stops at the reserved scrollbar gutter, which shows the canvas; the prototype's
   scrim covers the full width. While the drawer is open the canvas takes the scrim-mixed page
   colour, and the body (which then paints its own background) reaches the bottom of the window,
   so nothing is dimmed twice. */
html:has(dialog.drawer:modal) {
  background: color-mix(in srgb, var(--color-bg) 55%, rgb(10, 14, 18));
}
html:has(dialog.drawer:modal) body {
  min-height: 100vh;
}
.dr-brand {
  height: 48px;
  display: flex;
  align-items: center;
  gap: var(--sp-9);
  padding: 0 var(--sp-8);
  font: 600 15px var(--font-sans);
}
.dr-group {
  margin-top: var(--sp-12);
}
.dr-label {
  margin: 0;
  padding: 0 var(--sp-10) var(--sp-4);
  font: 600 11px var(--font-sans);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-3);
}
.dr .dr-item {
  width: 100%;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  text-align: left;
  padding: 0 var(--sp-10);
  border: 0;
  border-radius: var(--sp-8);
  background: transparent;
  color: var(--color-text);
  font: 500 14.5px var(--font-sans);
  text-decoration: none;
  text-box: normal;
}
.dr .dr-item:hover {
  background: var(--color-surface-2);
}
.dr .dr-item[aria-current="page"] {
  background: var(--color-accent-bg);
  color: var(--color-accent-fg);
}
.dr-foot {
  margin-top: var(--sp-16);
  border-top: var(--hair-w) solid var(--color-border);
  padding-top: var(--sp-12);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
.dr-foot form {
  margin: 0;
}
.dr-theme {
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-8);
  padding: 0 var(--sp-10);
  font: 500 14.5px var(--font-sans);
}

/* crumbs (ui.Crumbs) and the trails of pages not ported yet (.crumb.trail) */
nav.crumbs,
.crumb.trail {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-6);
  font-size: var(--fs-meta);
  color: var(--color-text-3);
  margin: 0 0 var(--sp-8);
  min-height: 20px;
}
nav.crumbs a,
.crumb.trail a {
  color: var(--color-text-2);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: var(--color-border-strong);
}
/* a long device name in a crumb link is cut like the last crumb, or the trail widens a phone page
   (the delete page, reached from the device list's Delete…, P2) */
nav.crumbs a {
  max-width: 60cqw;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.crumb-here,
.crumb.trail > [aria-current] {
  color: var(--color-text-2);
  max-width: 60cqw;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.crumb.trail > :not(:last-child)::after {
  content: "/";
  display: inline-block;
  margin-left: var(--sp-6);
  color: var(--color-text-3);
  text-decoration: none;
}
.page-head .crumb {
  grid-area: crumb;
}

/* inline banners: flash without JavaScript, the setup notice, the connection banners */
.banner {
  display: flex;
  gap: var(--sp-12);
  align-items: center;
  flex-wrap: wrap;
  padding: var(--sp-10) var(--sp-14);
  margin: 0 0 var(--sp-14);
  border-radius: var(--r-banner);
  border: var(--hair-w) solid var(--banner-line);
  background: var(--banner-bg);
  color: var(--banner-fg);
  font-size: 13px;
}
.banner.tone-ok {
  --banner-line: var(--color-ok);
  --banner-bg: var(--color-ok-bg);
  --banner-fg: var(--color-ok-fg);
}
.banner.tone-warn {
  --banner-line: var(--color-warn);
  --banner-bg: var(--color-warn-bg);
  --banner-fg: var(--color-warn-fg);
}
.banner.tone-danger {
  --banner-line: var(--color-danger);
  --banner-bg: var(--color-danger-bg);
  --banner-fg: var(--color-danger-fg);
}
.banner-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--banner-line);
  flex: none;
}
.banner-dot.pulse {
  animation: tf-pulse 0.9s ease-in-out infinite;
}
.banner-text {
  flex: 1;
  min-width: min(
    200px,
    100%
  ); /* in a narrow box (the style guide's sign-in card) it may not poke out of the border */
}
.banner-text b {
  font-weight: 600;
}
.banner-actions {
  display: flex;
  gap: var(--sp-8);
  flex-wrap: wrap;
}
.banner-text a {
  color: inherit;
}
.banner .banner-btn {
  height: var(--ctl);
  padding: 0 var(--sp-12);
  border-radius: var(--r-control);
  border: var(--hair-w) solid var(--banner-line);
  background: var(--color-surface);
  color: var(--banner-fg);
  font: 500 13px var(--font-sans);
  text-box: normal;
}
/* the connection banners float below the sticky header (header.top is their containing block):
   showing one never moves the page; z-index -1 keeps the header's own menus above them */
.conn-banners {
  position: absolute;
  z-index: -1;
  top: calc(100% + var(--sp-8));
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  padding: 0 var(--sp-16);
  pointer-events: none;
}
.conn-banner {
  display: none;
  margin: 0;
  max-width: 760px;
  box-shadow: var(--shadow-lg);
  pointer-events: auto;
}
html[data-live="reconnecting"] .conn-banner.conn-re,
html[data-live="offline"] .conn-banner.conn-off {
  display: flex;
}

/* toasts (toast.js): bottom right, outside every size container */
.toasts {
  position: fixed;
  right: 16px;
  bottom: calc(var(--sb-h) + 16px);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--sp-8);
  align-items: flex-end;
  pointer-events: none;
  max-width: calc(100% - 32px);
}
.toast {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: var(--sp-10);
  padding: var(--sp-10) var(--sp-14);
  background: var(--color-surface);
  color: var(--color-text);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-banner);
  box-shadow: var(--shadow-lg);
  font-size: 13px;
}
.toast.has-x {
  padding: var(--sp-8) var(--sp-8) var(--sp-8) var(--sp-14);
}
.toast-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
  background: var(--color-ok);
}
.toast.tone-info .toast-dot {
  background: var(--color-accent);
}
.toast.tone-warn .toast-dot {
  background: var(--color-warn);
}
.toast.tone-danger .toast-dot {
  background: var(--color-danger);
}
.toast-text {
  min-width: 0;
  overflow-wrap: anywhere;
}
.toast .toast-x {
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-text-3);
  font: 400 16px var(--font-sans);
  text-box: normal;
}
@media (prefers-reduced-motion: reduce) {
  .sb-dot.run,
  .banner-dot.pulse {
    animation: none;
  }
}

/* ==========================================================================
   Components (design v2, WP F4): internal/web/ui/components.templ, one block
   per component, values from the prototype's markup (design/v2/template.html).
   Every component is on /styleguide; docs/spec/design-system.md describes it.
   ========================================================================== */
@keyframes tf-shimmer {
  0% {
    background-position: -300px 0;
  }
  100% {
    background-position: 300px 0;
  }
}

/* page header: title and subtitle left, the page's buttons right */
.pghead {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-12);
  flex-wrap: wrap;
  margin-bottom: var(--sp-16);
}
.pghead-t {
  min-width: 0;
}
.pghead h1,
.pghead .h1-sample {
  margin: 0;
  font: 600 22px/1.2 var(--font-sans);
  letter-spacing: -0.015em;
}
.pghead-sub {
  margin: 4px 0 0;
  color: var(--color-text-2);
  font-size: 13px;
}
.pghead-acts {
  display: flex;
  gap: var(--sp-8);
  flex-wrap: wrap;
}
.pghead-acts:empty {
  display: none;
}

/* buttons: default, primary, danger (outlined), danger-solid, ghost, link, small, icon */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-6);
  height: var(--ctl);
  padding: 0 var(--sp-12);
  border-radius: var(--r-control);
  border: var(--hair-w) solid var(--color-border-strong);
  background: var(--color-surface);
  color: var(--color-text);
  font: 500 13px var(--font-sans);
  text-box: normal;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
.btn:hover,
.btn:active {
  background: var(--color-surface-2);
  border-color: var(--color-border-strong);
  color: var(--color-text);
}
.btn.btn-primary {
  padding: 0 var(--sp-14);
  border-color: var(--color-accent);
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-weight: 600;
}
.btn.btn-primary:hover,
.btn.btn-primary:active {
  border-color: var(--color-accent-hover);
  background: var(--color-accent-hover);
  color: var(--color-on-accent);
}
.btn.btn-danger,
.btn.btn-danger:hover {
  color: var(--color-danger-fg);
}
.btn.btn-danger-solid {
  padding: 0 var(--sp-14);
  border-color: var(--color-danger-solid);
  background: var(--color-danger-solid);
  color: #fff;
  font-weight: 600;
}
.btn.btn-danger-solid:hover,
.btn.btn-danger-solid:active {
  border-color: var(--color-danger);
  background: var(--color-danger);
  color: #fff;
}
.btn.btn-ghost {
  border-color: transparent;
  background: none;
}
.btn.btn-ghost:hover {
  border-color: transparent;
  background: var(--color-surface-2);
}
.btn.btn-link {
  height: auto;
  min-height: var(--ctl-sm);
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-accent-fg);
}
.btn.btn-link:hover {
  background: none;
  color: var(--color-accent-hover);
}
.btn.btn-sm {
  height: var(--ctl-sm);
}
.btn.btn-icon {
  width: var(--ctl);
  min-width: 32px;
  padding: 0;
  color: var(--color-text-2);
  font: 600 16px var(--font-sans);
}
.btn.btn-icon.btn-sm {
  width: var(--ctl-sm);
}
.btn.btn-tall {
  height: calc(var(--ctl) + 4px);
}
.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: default;
  pointer-events: none;
}
.btn-row {
  display: flex;
  gap: var(--sp-8);
  flex-wrap: wrap;
  align-items: center;
}

/* badges, status dots, avatar: the tone classes (.t-ok, .t-warn, .t-danger, .t-neutral: About, F3) plus the accent */
.t-accent {
  --t: var(--color-accent);
  --t-bg: var(--color-accent-bg);
  --t-fg: var(--color-accent-fg);
}
.bdg {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 7px;
  border-radius: var(--r-badge);
  font: 500 11.5px var(--font-sans);
  white-space: nowrap;
  background: var(--t-bg);
  color: var(--t-fg);
}
.sdot {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-6);
  font-size: 13px;
}
.sdot-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
  background: var(--t);
}
.uavatar {
  display: inline-grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  flex: none;
  background: var(--t-bg);
  color: var(--t-fg);
  font: 600 11px var(--font-sans);
}

/* cards: a padded section, a list card (header row, rows), key/value rows */
.ucard {
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  padding: var(--sp-16);
  display: flex;
  flex-direction: column;
  gap: var(--sp-10);
  min-width: 0;
}
.ucard > h2 {
  margin: 0 0 2px;
  font: 600 14px var(--font-sans);
  letter-spacing: normal;
}
.ucard-sub {
  margin: 0 0 4px;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.ucard > .banner {
  margin: 0;
  gap: var(--sp-10);
}
.ucard > .banner .banner-text {
  min-width: 180px;
}
.ulist {
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  min-width: 0;
}
.ulist-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-12) var(--sp-14);
  border-bottom: var(--hair-w) solid var(--color-border);
}
.ulist-head h2 {
  margin: 0;
  font: 600 14px var(--font-sans);
  letter-spacing: normal;
}
.ukv {
  margin: 0;
}
.ukv-row {
  display: grid;
  grid-template-columns: 118px minmax(0, 1fr);
  gap: var(--sp-10);
  padding: var(--sp-6) 0;
  border-top: var(--hair-w) solid var(--color-border);
  font-size: 13px;
}
.ukv dt {
  color: var(--color-text-2);
}
/* the info icon of a label (#680, ui.Info) is centred on the label's line, not hung from its baseline:
   "middle" is the x-height centre, which sits 1px below the centre of the line the label text fills */
.ukv dt .uinfo {
  vertical-align: middle;
  position: relative;
  top: -1px;
}
.ukv dd {
  margin: 0;
  overflow-wrap: anywhere;
}
.ukv dd.mono {
  font: 400 12.5px var(--font-mono);
  overflow-wrap: anywhere;
}

/* inline banners: the info tone and an action that is a link (the other tones are the shell's) */
.banner.tone-accent {
  --banner-line: var(--color-accent);
  --banner-bg: var(--color-accent-bg);
  --banner-fg: var(--color-accent-fg);
}
.banner a.banner-btn {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}
.err-summary {
  max-width: 820px;
}

/* toasts in the flow (style guide) and the toast's action */
.sg-toasts {
  display: flex;
  flex-direction: column;
  gap: var(--sp-8);
  align-items: flex-end;
  padding: var(--sp-16);
  border-radius: var(--r-banner);
  background: var(--color-surface-2);
}
.toast .toast-act {
  height: 30px;
  padding: 0 var(--sp-8);
  border: 0;
  background: none;
  color: var(--color-accent-fg);
  font: 500 13px var(--font-sans);
  text-box: normal;
}
.toast .toast-act:hover {
  background: none;
  color: var(--color-accent-hover);
}
.sg-toasts .toast {
  max-width: 100%;
}
.sg-toasts .toast-text {
  overflow-wrap: anywhere;
}

/* the live indicator in one fixed state (style guide) */
.live-pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 30px;
  padding: 0 var(--sp-12);
  border-radius: var(--r-pill);
  border: var(--hair-w) solid var(--color-border);
  background: var(--color-surface);
  font: 500 12px var(--font-sans);
  color: var(--color-text-2);
  min-width: 150px;
}
.live-pill-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
}
.lp-ok .live-pill-dot {
  background: var(--color-ok);
  animation: tf-pulse 2.4s ease-in-out infinite;
}
.lp-re .live-pill-dot {
  background: var(--color-warn);
  animation: tf-pulse 0.9s ease-in-out infinite;
}
.lp-off .live-pill-dot {
  background: var(--color-danger);
}

/* form fields: label above, help or the server's error below */
.ufield {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  font: 500 12.5px var(--font-sans);
  color: var(--color-text-2);
  min-width: 0;
}
.ufield-wide {
  grid-column: 1 / -1;
}
.ufield input.mono:not([type="checkbox"]):not([type="radio"]) {
  font-family: var(--font-mono);
}
/* as specific as the base input rule's :not() chain, which would otherwise keep its grey border */
.ufield input[aria-invalid="true"]:not([type="checkbox"]):not([type="radio"]) {
  border-color: var(--color-danger);
  box-shadow: none;
}
.ufield-err {
  font: 400 12px var(--font-sans);
  color: var(--color-danger-fg);
}
.ufield-help {
  font: 400 12px var(--font-sans);
  color: var(--color-text-3);
}
.ucheck {
  display: flex;
  align-items: center;
  gap: var(--sp-10);
  min-height: 36px;
  font: 400 13px var(--font-sans);
  color: var(--color-text);
}
.ucheck input {
  width: 16px;
  height: 16px;
  flex: none; /* a two-line label must not squeeze the box narrower than its neighbours */
}
.ucheck-text {
  display: block;
  font-size: 12px;
  color: var(--color-text-2);
}

/* radio cards: label > input, the checked card in the accent tint */
.rcards {
  margin: 0;
  padding: 0;
  border: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-8);
}
.rcards legend {
  margin: 0 0 var(--sp-5);
  padding: 0;
  font: 500 12.5px var(--font-sans);
  color: var(--color-text-2);
}
.rcard {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: var(--sp-10);
  padding: var(--sp-12) var(--sp-14);
  border-radius: var(--r-banner);
  border: var(--hair-w) solid var(--color-border);
  background: var(--color-surface);
  font: 400 13px var(--font-sans);
  cursor: pointer;
}
.rcard:has(input:checked) {
  border-color: var(--color-accent);
  background: var(--color-accent-bg);
}
.rcard input {
  width: 16px;
  height: 16px;
  margin-top: 2px;
}
.rcard-body b {
  display: block;
  font-weight: 600;
  font-size: 13.5px;
}
.rcard-body span {
  display: block;
  font-size: 12.5px;
  color: var(--color-text-2);
}

/* segmented control: radio inputs in one frame */
.useg {
  display: inline-flex;
  width: fit-content;
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: var(--r-control);
  overflow: hidden;
}
.useg label {
  display: block;
  position: relative;
}
.useg input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}
.useg span {
  display: flex;
  align-items: center;
  height: var(--ctl);
  padding: 0 var(--sp-12);
  background: var(--color-surface);
  color: var(--color-text);
  font: 500 13px var(--font-sans);
  cursor: pointer;
}
.useg label + label span {
  border-left: var(--hair-w) solid var(--color-border-strong);
}
.useg input:checked + span {
  background: var(--color-surface-3);
}
.useg input:focus-visible + span {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

/* switches: a setting (with its title and line) and the power toggle of a row */
.utoggle {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--sp-12);
  padding: var(--sp-12) var(--sp-14);
  border-radius: var(--r-banner);
  border: var(--hair-w) solid var(--color-border);
  background: var(--color-surface);
  font: 400 13px var(--font-sans);
  color: var(--color-text);
  cursor: pointer;
}
.utoggle input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}
.utoggle .sw,
.pwr .sw {
  position: relative;
  flex: none;
  width: 38px;
  height: 22px;
  border-radius: 11px;
  background: var(--color-border-strong);
}
.utoggle .sw::after,
.pwr .sw::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-knob);
  transition: left 0.15s;
}
.utoggle input:checked + .sw {
  background: var(--color-accent);
}
.utoggle input:checked + .sw::after {
  left: 19px;
}
.utoggle input:focus-visible + .sw {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
.utoggle-text b {
  display: block;
  font-weight: 600;
  font-size: 13.5px;
}
.utoggle-text span {
  display: block;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.pwr {
  display: inline-grid;
  place-items: center;
  width: 48px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.pwr:hover,
.pwr:active {
  background: none;
}
.pwr .sw {
  width: 34px;
  height: 20px;
  border-radius: 10px;
}
.pwr .sw::after {
  left: 2px;
  width: 14px;
  height: 14px;
}
.pwr[aria-checked="true"] .sw {
  background: var(--color-accent);
}
.pwr[aria-checked="true"] .sw::after {
  left: 18px;
}
.pwr[aria-pressed="true"],
.pwr[aria-pressed="true"]:hover {
  border: 0;
  background: none;
  color: inherit;
}
.pwr:disabled {
  opacity: 0.45;
  cursor: default;
}

/* stat tiles and the fleet health bar */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sp-10);
}
@media (max-width: 44.99rem) {
  /* two across on a phone, wherever the grid sits (the style guide's sample box is narrower than the dashboard) */
  .stat-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  /* an odd count leaves no hole: the last tile takes the whole row (#438) */
  .stat-grid > .stat-tile:last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }
}
/* Where the dashboard's tiles do not fit one row, two balanced rows instead of a lone last tile
   (#438): seven (admins) as 4 + 3 below 75rem, six (operators) as 3 + 3 below 65rem. The bounds
   are where auto-fit fits them in one row (150px minimum, the 10px gap, the page gutters and a
   classic scrollbar); re-measure them when the page's width or gutters change. */
@media (min-width: 45rem) and (max-width: 74.99rem) {
  .stat-grid:has(> .stat-tile:nth-child(7):last-child) {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}
@media (min-width: 45rem) and (max-width: 64.99rem) {
  .stat-grid:has(> .stat-tile:nth-child(6):last-child) {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
.stat-tile {
  display: flex;
  flex-direction: column;
  gap: var(--sp-8);
  padding: var(--sp-14);
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  color: var(--color-text);
  text-decoration: none;
}
.stat-tile:hover {
  border-color: var(--color-border-strong);
  color: var(--color-text);
}
.stat-label {
  display: flex;
  align-items: center;
  gap: 7px;
  font: 500 12.5px var(--font-sans);
  color: var(--color-text-2);
}
.stat-value {
  font: 600 var(--fs-stat) / 1 var(--font-sans);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
.stat-hint {
  font-size: 12px;
  color: var(--color-text-3);
}
.health-bar {
  display: block;
  width: 100%;
  height: 8px;
  margin: var(--sp-12) 0 var(--sp-18);
  border-radius: 4px;
  background: var(--color-surface-2);
}
.health-bar .bar-seg,
.seg-bar .bar-seg {
  fill: var(--t);
}
.seg-bar .bar-seg.t-accent {
  opacity: 0.55;
}

/* "Needs attention" rows, transaction card and history rows */
.att-row {
  display: flex;
  align-items: center;
  gap: var(--sp-10);
  flex-wrap: wrap;
  padding: var(--sp-10) var(--sp-14);
  border-bottom: var(--hair-w) solid var(--color-border);
  min-height: 44px;
  color: var(--color-text);
  text-decoration: none;
}
a.att-row:hover {
  background: var(--color-surface-2);
  color: var(--color-text);
}
.att-prop {
  gap: var(--sp-6) var(--sp-10);
  padding: var(--sp-8) var(--sp-10) var(--sp-8) var(--sp-14);
  min-height: 52px;
}
.att-item {
  align-items: flex-start;
  flex-wrap: nowrap;
}
.att-item .sdot-dot {
  margin-top: 6px;
}
.att-text {
  flex: 1 1 180px;
  min-width: 0;
}
.att-name {
  display: block;
  font-weight: 500;
  overflow-wrap: anywhere;
}
.att-why {
  display: block;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.att-acts {
  display: flex;
  gap: var(--sp-6);
}
.att-acts form {
  margin: 0;
}
.att-go {
  color: var(--color-text-3);
}
.txn-card {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: var(--sp-14);
  border-bottom: var(--hair-w) solid var(--color-border);
  color: var(--color-text);
  text-decoration: none;
}
.txn-card:hover {
  background: var(--color-surface-2);
  color: var(--color-text);
}
.txn-head {
  display: flex;
  align-items: center;
  gap: var(--sp-8);
  flex-wrap: wrap;
}
.txn-id {
  font: 500 12px var(--font-mono);
  color: var(--color-text-2);
}
.txn-title {
  font-weight: 600;
}
.txn-sum {
  font-size: 12.5px;
  color: var(--color-text-2);
}
.seg-bar {
  display: block;
  width: 100%;
  height: 6px;
  border-radius: 3px;
  background: var(--color-surface-3);
}
.txn-row-title {
  flex: 1;
  min-width: 140px;
}
.txn-when {
  font-size: 12px;
  color: var(--color-text-3);
}
.mini-bar {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-8);
}
.mini-bar svg {
  display: block;
  width: 80px;
  height: 4px;
  border-radius: 2px;
}
.mini-bg {
  fill: var(--color-surface-3);
}
.mini-fill {
  fill: var(--color-accent);
}
.mini-pct {
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--color-text-2);
}

/* tabs: underline tabs; a disabled tab carries its reason as the title */
.utabs {
  display: flex;
  gap: var(--sp-20);
  border-bottom: var(--hair-w) solid var(--color-border);
  margin-bottom: var(--sp-16);
  overflow-x: auto;
}
.utab {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-6);
  height: calc(var(--ctl) + 6px);
  padding: 0 2px;
  font: 500 13.5px var(--font-sans);
  color: var(--color-text-2);
  text-decoration: none;
  white-space: nowrap;
  box-shadow: inset 0 -2px 0 transparent;
}
.utab:hover {
  color: var(--color-text);
}
.utab[aria-current="page"] {
  color: var(--color-text);
  box-shadow: inset 0 -2px 0 var(--color-accent);
}
.utab[aria-disabled="true"] {
  color: var(--color-text-3);
  cursor: default;
}

/* row menu panel (ui.RowMenu); the legacy row menus keep their look until their page is ported */
details.umenu[open] > summary.btn {
  background: var(--color-surface-2);
  border-color: transparent;
  color: var(--color-text);
}
.umenu .menu-panel {
  display: block;
  width: 250px;
  min-width: 0;
  padding: 5px;
  background: var(--color-surface);
  /* the prototype draws --color-border; the strong border keeps the 1.3:1 overlay guard (g), as the
     user menu does (F3) */
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-lg);
}
.umenu .menu-panel .menu-item {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: stretch;
  gap: 1px;
  width: 100%;
  height: auto;
  min-height: var(--ctl);
  padding: 6px 10px;
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--color-text);
  font: 400 13px var(--font-sans);
  text-align: left;
  text-decoration: none;
  white-space: normal;
  opacity: 1;
}
.umenu .menu-panel .menu-item:hover {
  background: var(--color-surface-2);
}
.umenu .menu-panel .menu-item.danger {
  color: var(--color-danger-fg);
}
.umenu .menu-panel .menu-item[aria-disabled="true"] {
  color: var(--color-text-3);
  cursor: default;
  pointer-events: none;
}
.menu-why {
  font-size: 11.5px;
  color: var(--color-text-3);
}
.umenu .menu-panel hr {
  height: 1px;
  margin: 5px 0;
  border: 0;
  background: var(--color-border);
}

/* dialogs: confirm (with a change list or a command input) and step-up */
dialog.udlg {
  width: min(480px, calc(100% - 32px));
  max-height: 90%;
  overflow: auto;
  padding: var(--sp-20);
  /* a dialog taller than the screen scrolls inside itself: a soft shadow on the cut edge says there
     is more (the "local" fades cover it once the end is reached, as for pre.log) */
  background:
    linear-gradient(var(--color-surface) 30%, transparent) center top / 100%
      24px no-repeat local,
    linear-gradient(transparent, var(--color-surface) 70%) center bottom / 100%
      24px no-repeat local,
    linear-gradient(
        color-mix(in srgb, var(--color-text) 14%, transparent),
        transparent
      )
      center top / 100% 12px no-repeat scroll,
    linear-gradient(
        transparent,
        color-mix(in srgb, var(--color-text) 14%, transparent)
      )
      center bottom / 100% 12px no-repeat scroll,
    var(--color-surface);
  color: var(--color-text);
  border: var(--hair-w) solid var(--color-border-strong); /* overlay guard (g), see the menu panel */
  border-radius: var(--r-panel);
  box-shadow: var(--shadow-lg);
}
dialog.udlg::backdrop {
  background: var(--backdrop);
}
/* as for the drawer: the canvas in the reserved scrollbar gutter takes the scrim-mixed page
   colour while a dialog is open (P8: "Confirm it's you" left a light strip on the right) */
html:has(dialog.udlg:modal) {
  background: color-mix(in srgb, var(--color-bg) 50%, rgb(10, 14, 18));
}
html:has(dialog.udlg:modal) body {
  min-height: 100vh;
}
dialog.stepup {
  width: min(420px, calc(100% - 32px));
}
.udlg-in {
  display: flex;
  flex-direction: column;
  gap: var(--sp-14);
  margin: 0;
}
.udlg-in h2,
.stepup h2 {
  margin: 0;
  font: 600 16px/1.3 var(--font-sans);
  letter-spacing: normal;
}
.udlg-body {
  margin: 0;
  color: var(--color-text-2);
  text-wrap: pretty;
}
.udlg-body b {
  color: var(--color-text);
  font-weight: 600;
}
.udlg-changes {
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-code);
  max-height: 220px;
  overflow: auto;
}
.udlg-changes > div {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4) var(--sp-10);
  justify-content: space-between;
  padding: var(--sp-8) var(--sp-12);
  border-bottom: var(--hair-w) solid var(--color-border);
  font-size: 13px;
}
.udlg-changes span:first-child {
  /* a readable width: a long command wraps below the name instead of squeezing it */
  flex: 1 1 10rem;
  min-width: 0;
  overflow-wrap: anywhere;
}
.udlg-changes .mono {
  font: 400 12px var(--font-mono);
  color: var(--color-text-2);
  min-width: 0;
  max-width: 100%;
  overflow-wrap: anywhere;
}
.udlg-foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-8);
  flex-wrap: wrap;
  margin: 0;
}
.udlg-foot .btn {
  padding: 0 var(--sp-14);
}
.stepup-who {
  display: flex;
  align-items: center;
  gap: var(--sp-10);
}
.stepup-sub {
  font-size: 12.5px;
  color: var(--color-text-2);
}
.stepup .udlg-body {
  font-size: 13.5px;
}
.or-rule {
  display: flex;
  align-items: center;
  gap: var(--sp-10);
  font-size: 12px;
  color: var(--color-text-3);
}
.or-rule::before,
.or-rule::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--color-border);
}
.confirm-card {
  max-width: 760px;
  gap: var(--sp-14);
}
.confirm-card > h2 {
  font: 600 16px/1.3 var(--font-sans);
}

/* empty state, "no match" row, loading skeleton */
.uempty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-10);
  padding: var(--sp-40) var(--sp-20);
  text-align: center;
  background: var(--color-surface);
  border: var(--hair-w) dashed var(--color-border-strong);
  border-radius: var(--r-panel);
}
.uempty-icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: var(--r-panel);
  background: var(--color-surface-2);
}
.uempty-icon span {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid var(--color-text-3);
}
.uempty h2 {
  margin: 0;
  font: 600 17px var(--font-sans);
  letter-spacing: normal;
}
.uempty p {
  margin: 0;
  max-width: 420px;
  color: var(--color-text-2);
  text-wrap: pretty;
}
.uempty .btn-row {
  justify-content: center;
  margin-top: var(--sp-6);
}
.nomatch {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-10);
  padding: var(--sp-24);
  font-size: 13px;
  color: var(--color-text-2);
  text-align: center;
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
}
.skel {
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  overflow: hidden;
}
.skel-head {
  padding: var(--sp-10) var(--sp-14);
  font-size: 12.5px;
  color: var(--color-text-2);
  border-bottom: var(--hair-w) solid var(--color-border);
}
.skel-row {
  display: flex;
  align-items: center;
  gap: var(--sp-14);
  height: 44px;
  padding: 0 var(--sp-14);
  border-bottom: var(--hair-w) solid var(--color-border);
}
.skel-row:last-child {
  border-bottom: 0;
}
.skel-box {
  width: 16px;
  height: 16px;
  border-radius: var(--r-xs);
  background: var(--color-surface-3);
}
.skel-line {
  height: 10px;
  width: 60%;
  max-width: 260px;
  border-radius: var(--r-badge);
  background: linear-gradient(
    90deg,
    var(--color-surface-2) 0,
    var(--color-surface-3) 50%,
    var(--color-surface-2) 100%
  );
  background-size: 600px 100%;
  animation: tf-shimmer 1.4s linear infinite;
}
.skel-fill {
  flex: 1;
}
.skel-end {
  height: 10px;
  width: 70px;
  border-radius: var(--r-badge);
  background: var(--color-surface-2);
}

/* error page: code, title, explanation, the failed path, two ways on */
.errpage {
  min-height: 440px;
  display: flex;
  align-items: center;
  padding: var(--sp-24) 0;
}
.errpage-in {
  max-width: 560px;
  display: flex;
  flex-direction: column;
  gap: var(--sp-12);
  align-items: flex-start;
}
.errpage-code {
  font: 500 13px var(--font-mono);
  color: var(--color-text-3);
}
.errpage h1,
.errpage .h1-sample {
  margin: 0;
  font: 600 26px/1.2 var(--font-sans);
  letter-spacing: -0.02em;
  text-wrap: balance;
}
.errpage p {
  margin: 0;
  color: var(--color-text-2);
  font-size: 14.5px;
  text-wrap: pretty;
}
.errpage-path {
  font: 400 12.5px var(--font-mono);
  color: var(--color-text-2);
  background: var(--color-surface-2);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-control);
  padding: 7px var(--sp-10);
  overflow-wrap: anywhere;
}
.errpage .btn-row {
  margin-top: var(--sp-6);
}

/* sign-in card: brand above, the card, the version footer */
.uauth {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-18);
}
.auth-card {
  width: min(420px, 100%);
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-panel);
  padding: var(--sp-24);
  display: flex;
  flex-direction: column;
  gap: var(--sp-14);
}
.auth-card h1,
.auth-card .h1-sample {
  margin: 0;
  font: 600 20px/1.2 var(--font-sans);
  letter-spacing: -0.015em;
}
.auth-card > .banner {
  margin: 0;
}
.auth-foot {
  font: 400 12px var(--font-mono);
  color: var(--color-text-3);
  text-align: center;
  text-wrap: balance;
}
.auth-foot > span {
  display: inline-block; /* version and host wrap as units, never inside */
  max-width: 100%;
  overflow-wrap: anywhere;
}

/* console: dark panel, timestamped lines, prompt, quick commands */
.console {
  background: var(--color-term-bg);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  overflow: hidden;
  color: var(--color-term-fg);
}
.console-head {
  display: flex;
  align-items: center;
  gap: var(--sp-10);
  padding: var(--sp-8) var(--sp-12);
  border-bottom: var(--hair-w) solid var(--color-console-rule);
  font: 500 12px var(--font-sans);
  color: var(--color-term-dim);
}
.console-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-ok);
  animation: tf-pulse 2.4s infinite;
}
.console-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.console .console-clear,
.console .console-chip {
  height: 28px;
  padding: 0 var(--sp-10);
  border-radius: var(--r-sm);
  border: var(--hair-w) solid var(--color-console-line);
  background: none;
  color: var(--color-term-fg);
  font: 500 12px var(--font-sans);
  text-box: normal;
}
.console .console-chip {
  border-radius: var(--r-pill);
  font-family: var(--font-mono);
}
.console .console-clear:hover,
.console .console-chip:hover {
  background: var(--color-console-rule);
  border-color: var(--color-console-line);
  color: var(--color-term-fg);
}
.console-lines {
  max-height: min(440px, 52vh);
  overflow: auto;
  padding: var(--sp-12) var(--sp-14);
  font: 400 12.5px/1.65 var(--font-mono);
}
.console-line {
  display: flex;
  gap: var(--sp-12);
}
.console-time {
  color: var(--color-term-dim);
  flex: none;
}
.console-text {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  min-width: 0;
}
.console-line.is-cmd .console-text {
  color: var(--color-term-cmd);
}
.console-line.is-err .console-text {
  color: var(--color-term-err);
}
.console-form {
  display: flex;
  gap: var(--sp-8);
  margin: 0;
  padding: var(--sp-10);
  border-top: var(--hair-w) solid var(--color-console-rule);
}
.console-prompt {
  align-self: center;
  padding-left: 4px;
  color: var(--color-term-cmd);
  font: 500 14px var(--font-mono);
}
.console-form input:not([type="checkbox"]):not([type="radio"]) {
  flex: 1;
  border-color: var(--color-console-line);
  background: var(--color-console-rule);
  color: var(--color-term-fg);
  font: 400 13px var(--font-mono);
}
.console-quick {
  display: flex;
  gap: var(--sp-6);
  flex-wrap: wrap;
  margin: 0;
  padding: 0 var(--sp-10) var(--sp-10);
}

/* backup comparison: side by side or unified */
.diff {
  overflow: hidden;
}
.diff-scroll {
  overflow-x: auto;
}
.diff-grid {
  display: grid;
  grid-template-columns: minmax(150px, 0.7fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--sp-12);
  min-width: 620px;
  padding: var(--sp-8) var(--sp-16);
  border-bottom: var(--hair-w) solid var(--color-border);
}
.diff-headrow {
  background: var(--color-surface-2);
  font: 500 12px var(--font-sans);
  color: var(--color-text-2);
}
.diff-row {
  min-height: 36px;
  font: 400 12.5px var(--font-mono);
  overflow-wrap: anywhere;
  color: var(--color-text-2);
}
.diff-row.is-changed {
  background: var(--color-warn-bg);
  color: var(--color-text);
}
.diff-key {
  color: var(--color-text);
  font-weight: 500;
}
/* phone: the setting on its own line, older and newer side by side under it; nothing to scroll sideways */
@media (max-width: 44.99rem) {
  .diff-grid {
    min-width: 0;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 2px var(--sp-12);
  }
  .diff-headrow > :first-child {
    display: none;
  }
  .diff-key {
    grid-column: 1 / -1;
  }
}
.diff-uni {
  padding: var(--sp-6) 0;
  font: 400 12.5px/1.6 var(--font-mono);
}
.diff-uni-head {
  padding: 2px var(--sp-14) var(--sp-8);
  font: 500 12px var(--font-sans);
  color: var(--color-text-2);
}
.diff-line {
  display: grid;
  grid-template-columns: 16px minmax(0, 1fr);
  gap: var(--sp-8);
  padding: 3px var(--sp-14);
  color: var(--color-text-2);
  overflow-wrap: anywhere;
}
.diff-line b {
  font-weight: 500;
  color: var(--color-text);
}
.diff-line.is-del {
  background: var(--color-danger-bg);
  color: var(--color-danger-fg);
}
.diff-line.is-add {
  background: var(--color-ok-bg);
  color: var(--color-ok-fg);
}

/* release notes: text only, the breaking-change box in the warn tone */
.unotes {
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  padding: var(--sp-20) var(--sp-22);
  display: flex;
  flex-direction: column;
  gap: var(--sp-16);
  font-size: 14px;
  line-height: 1.6;
}
.unotes p,
.unotes ul {
  margin: 0;
}
.unotes ul {
  padding-left: var(--sp-20);
}
.unotes h2 {
  margin: 0;
  font: 600 15px var(--font-sans);
  letter-spacing: normal;
}
.unotes pre {
  margin: 0;
  padding: var(--sp-10) var(--sp-12);
  border-radius: var(--r-code);
  font: 400 12px/1.6 var(--font-mono);
}
.unotes pre code {
  padding: 0;
  background: none;
  font-size: inherit;
}
.unotes-breaking {
  border: var(--hair-w) solid var(--color-warn);
  background: var(--color-warn-bg);
  border-radius: var(--r-banner);
  padding: var(--sp-12) var(--sp-16);
}
.unotes-breaking h2 {
  display: flex;
  align-items: center;
  gap: var(--sp-8);
  margin: 0 0 var(--sp-6);
  font-size: 14px;
  color: var(--color-warn-fg);
}

/* ==== page: Styleguide (/styleguide, WP F4): the prototype's UI states, then the gallery ==== */
.sg-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 460px), 1fr));
  gap: var(--sp-14);
  align-items: start;
}
.sg-live {
  grid-column: 1 / -1;
  display: block;
}
.sg-live > h2 {
  margin: 0 0 4px;
}
.sg-live > .ucard-sub {
  margin: 0 0 6px;
}
.sg-live-row {
  display: flex;
  align-items: center;
  gap: var(--sp-14);
  flex-wrap: wrap;
  padding: var(--sp-10) 0;
  border-top: var(--hair-w) solid var(--color-border);
}
.sg-live-row > span:last-child {
  flex: 1;
  min-width: 200px;
  font-size: 13px;
  color: var(--color-text-2);
}
/* the gallery starts where the prototype's screen ends (the prototype's 120 px bottom padding of
   main), so the UI-states screen above it is the prototype's to the pixel and the gallery only
   adds below */
.sg-gallery {
  display: flex;
  flex-direction: column;
  gap: var(--sp-14);
  margin-top: 120px;
  padding-top: var(--sp-24);
  border-top: var(--hair-w) solid var(--color-border);
}
.sg-gallery-head h2 {
  margin: 0;
  font: 600 17px var(--font-sans);
  letter-spacing: -0.01em;
}
.sg-gallery-head p {
  margin: 4px 0 0;
  font-size: 13px;
  color: var(--color-text-2);
}
.sg-row {
  display: flex;
  gap: var(--sp-8);
  flex-wrap: wrap;
  align-items: center;
}
.sg-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr));
  gap: var(--sp-14);
  align-items: start;
}
.sg-stack {
  display: flex;
  flex-direction: column;
  gap: var(--sp-12);
  align-items: flex-start;
}
.sg-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: var(--sp-14) var(--sp-18);
  max-width: 820px;
}
.sg-label {
  margin: 0 0 var(--sp-6);
  font: 500 12.5px var(--font-sans);
  color: var(--color-text-2);
}
/* open menus and dialogs in the flow (data-static: the scripts leave them alone) */
.sg-flow {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-24);
  align-items: flex-start;
}
.sg-flow details[data-static] > .rowmenu-body {
  position: static;
  margin-top: var(--sp-6);
}
.sg-flow details.usermenu {
  display: inline-block;
}
.sg-flow .um-menu {
  position: static;
  margin-top: var(--sp-8);
}
.sg-drawer .dr {
  width: min(300px, 100%);
  padding: var(--sp-10);
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
}
.sg-flow dialog[data-static] {
  position: static;
  margin: 0;
  max-height: none;
}
.sg-sec .errpage {
  min-height: 0;
}
.sg-sec .uauth {
  padding: var(--sp-24) var(--sp-16);
  border-radius: var(--r-banner);
  background: var(--color-bg);
}
@media (prefers-reduced-motion: reduce) {
  .live-pill-dot,
  .console-dot,
  .skel-line {
    animation: none;
  }
}

/* ==== page: sign-in, account and users (WP P9) ==== */
/* standalone pages (sign-in, forced password change, messages): the auth card centred */
body.page-auth main {
  padding: 0 var(--page-pad);
}
.page-auth .uauth {
  min-height: calc(100vh - 150px);
  justify-content: center;
  gap: var(--sp-20);
  padding: var(--sp-12) 0;
}
.page-auth .brand {
  gap: var(--sp-10);
  font-size: 18px;
}
.page-auth .brand-mark {
  width: 28px;
  height: 28px;
}
.auth-card .banner {
  margin: 0;
  gap: var(--sp-10);
  padding: var(--sp-10) var(--sp-12);
  align-items: flex-start;
  flex-wrap: nowrap;
}
.auth-card .banner-dot {
  margin-top: 5px;
}
.auth-card .ucheck input {
  margin: 3px 3px 3px 4px;
}
.auth-card > form.auth-form {
  display: contents;
}
.auth-card .btn-tall {
  width: 100%;
}
.auth-text {
  margin: 0;
  color: var(--color-text-2);
  font-size: 13.5px;
  text-wrap: pretty;
}
.pw-who {
  display: flex;
  align-items: center;
  gap: var(--sp-10);
  padding: var(--sp-8) var(--sp-10);
  border-radius: var(--r-control);
  background: var(--color-surface-2);
  font-size: 12.5px;
  color: var(--color-text-2);
}
.pw-who .uavatar {
  width: 26px;
  height: 26px;
  font-size: 10.5px;
}
.pw-who b {
  color: var(--color-text);
  font-weight: 600;
}
.pw-checks {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  font-size: 12.5px;
  color: var(--color-text-2);
}
.pw-checks li::before {
  content: "○";
  display: inline-block;
  width: 14px;
  margin-right: var(--sp-8);
  text-align: center;
}
.auth-signout {
  display: flex;
  justify-content: center;
  margin: 0;
}
/* account page: profile and password side by side, one column on a phone */
.acct-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
  gap: var(--sp-14);
  align-items: start;
  max-width: 900px;
}
.acct-grid .ukv {
  display: grid;
  gap: var(--sp-6);
}
.acct-grid .ukv-row {
  grid-template-columns: 110px minmax(0, 1fr);
  padding: 0;
  border-top: 0;
}
.acct-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-12);
  margin: 0;
}
.acct-submit {
  align-self: flex-end;
}
.acct-edit .acct-form {
  margin-top: var(--sp-10);
}
/* user list: one wrapping row per user (who, badges, source, last sign-in, menu) */
.users-list {
  margin-bottom: 0;
}
.users-list .urow:last-child {
  border-bottom: 0;
}
/* #419-S: Users in column stacks. The Roles and Safeguards cards sit in the narrow stack. */
.roles-list,
.safe-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--sp-10);
  font-size: var(--fs-body);
}
.roles-list li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: var(--sp-10);
  align-items: baseline;
}
.roles-n {
  color: var(--color-text-2);
  font-size: var(--fs-meta);
}
.safe-list {
  padding-left: var(--sp-18);
  list-style: disc;
  gap: var(--sp-6);
}
.cstack .ucard.users-roles,
.cstack .ucard.users-safe,
.cstack .ucard.wn-you,
.cstack .ucard.wn-jump {
  padding: var(--sp-14) var(--sp-16);
  gap: var(--sp-10);
}
/* one line per user in the wide stack (who, role, source, last seen, menu) */
@media (min-width: 45rem) {
  .cstack .users-list .urow {
    display: grid;
    grid-template-columns: minmax(0, 1.5fr) 90px minmax(0, 100px) minmax(
        0,
        1.6fr
      ) auto;
    align-items: center;
    column-gap: var(--sp-12);
  }
}
/* two half columns (tablet): the list takes the full row, a half column squeezes the names */
@media (min-width: 45rem) and (max-width: 74.99rem) {
  .cstacks .users-list {
    column-span: all;
  }
}
.urow {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-6) var(--sp-12);
  min-height: 58px;
  padding: var(--sp-6) var(--sp-6) var(--sp-6) var(--sp-16);
  border-bottom: var(--hair-w) solid var(--color-border);
}
.urow > a.btn {
  margin-right: var(
    --sp-10
  ); /* the row's right padding fits the icon button, not a text one */
}
.urow-off {
  background: var(--color-surface-2);
}
.urow-who {
  flex: 2 1 220px;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--sp-10);
  padding: var(--sp-4) 0;
}
.urow-off .urow-who {
  color: var(--color-text-2);
}
.urow-names {
  min-width: 0;
}
.urow-name {
  display: flex;
  align-items: center;
  gap: var(--sp-6);
  flex-wrap: wrap;
  font-weight: 500;
  overflow-wrap: anywhere;
}
.urow-sub {
  display: block;
  font-size: 12px;
  color: var(--color-text-2);
  overflow-wrap: anywhere;
}
.urow-badges {
  flex: 1 1 190px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-6);
}
.urow-meta {
  flex: 1 1 170px;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.urow > .rowmenu {
  flex: none;
}
.urow > .rowmenu > summary {
  width: 40px;
  height: 40px;
}
.urow .uavatar {
  background: var(--color-surface-3);
  color: var(--color-text-2);
}
/* add-user form: two columns of fields, the password choice across, the buttons at the foot */
.useradd-sum {
  max-width: 760px;
  margin-bottom: var(--sp-14);
}
form.useradd {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: var(--sp-14) var(--sp-18);
  max-width: 760px;
  padding: var(--sp-18);
  margin: 0;
}
.useradd-mode {
  grid-column: 1 / -1;
}
.useradd-mode .rcards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: var(--sp-10);
}
.useradd-mode legend {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.useradd-pw {
  display: contents;
}
form.useradd:has(input[name="otp"][value="1"]:checked) .useradd-pw {
  display: none;
}
.useradd-foot {
  grid-column: 1 / -1;
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-8);
  border-top: var(--hair-w) solid var(--color-border);
  padding-top: var(--sp-14);
}

/* ==== page: About (WP F3 pilot) ==== */
.about-head {
  margin-bottom: var(--sp-16);
}
.about-head h1 {
  margin: 0;
  font: 600 22px/1.2 var(--font-sans);
  letter-spacing: -0.015em;
}
.about-card {
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
}
.about-version {
  padding: var(--sp-16);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "name actions" "build actions" "meta actions";
  column-gap: var(--sp-24);
  align-items: center;
}
.about-version .about-name {
  grid-area: name;
}
.about-version .about-build {
  grid-area: build;
}
.about-version .about-meta {
  grid-area: meta;
}
.about-name {
  font: 600 18px var(--font-sans);
}
.about-build {
  font: 400 12.5px var(--font-mono);
  color: var(--color-text-2);
  margin-top: var(--sp-4);
}
.about-meta {
  font-size: 13px;
  color: var(--color-text-2);
  margin-top: var(--sp-12);
}
.about-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-8);
  margin-top: var(--sp-14);
}
.about-version .about-actions {
  grid-area: actions;
  margin-top: 0;
  justify-content: flex-end;
}
.about-actions .button {
  height: var(--ctl);
  padding: 0 var(--sp-12);
  font: 500 13px var(--font-sans);
  text-box: normal;
}
.about-comps {
  overflow: hidden;
}
.about-comps h2 {
  margin: 0;
  padding: var(--sp-12) var(--sp-16);
  border-bottom: var(--hair-w) solid var(--color-border);
  font: 600 14px var(--font-sans);
  letter-spacing: normal;
}
.comp-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.comp-list .comp:nth-child(odd) {
  border-right: var(--hair-w) solid var(--color-border);
}
.comp-list .comp:nth-last-child(-n + 2) {
  border-bottom: 0;
}
.comp {
  display: flex;
  align-items: center;
  gap: var(--sp-10);
  flex-wrap: wrap;
  padding: var(--sp-9) var(--sp-16);
  border-bottom: var(--hair-w) solid var(--color-border);
}
.comp-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--t);
}
.comp-text {
  flex: 1;
  min-width: 140px;
}
.comp-name {
  display: block;
  font-weight: 500;
  font-size: 13px;
}
.comp-detail {
  display: block;
  font: 400 12px var(--font-mono);
  color: var(--color-text-2);
}
.comp-state {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 var(--sp-7);
  border-radius: var(--r-badge);
  font: 500 11.5px var(--font-sans);
  background: var(--t-bg);
  color: var(--t-fg);
}
.t-ok {
  --t: var(--color-ok);
  --t-bg: var(--color-ok-bg);
  --t-fg: var(--color-ok-fg);
}
.t-warn {
  --t: var(--color-warn);
  --t-bg: var(--color-warn-bg);
  --t-fg: var(--color-warn-fg);
}
.t-danger {
  --t: var(--color-danger);
  --t-bg: var(--color-danger-bg);
  --t-fg: var(--color-danger-fg);
}
.t-neutral {
  --t: var(--color-neutral);
  --t-bg: var(--color-neutral-bg);
  --t-fg: var(--color-neutral-fg);
}
/* the effective configuration: a full-width card of two tables (.cset, shared with Advanced); keys stay on one line on a desktop */
.about-cfg .cset table {
  table-layout: fixed;
  width: 100%;
}
.about-cfg .cset th:first-child {
  width: 58%;
}
.about-cfg .cset td.p1 code {
  white-space: nowrap;
}
.about-cfg .cset td.grow {
  overflow-wrap: anywhere;
}
.about-links {
  margin: 0;
}
/* #419-S: the version card sits in the narrow stack, the licence line in a card of its own */
.about-proj {
  padding: var(--sp-14) var(--sp-16);
}
.about-proj h2 {
  margin: 0 0 var(--sp-8);
  font: 600 14px var(--font-sans);
  letter-spacing: normal;
}
.cstack .about-version {
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "name" "build" "meta" "actions";
}
.cstack .about-version .about-actions {
  justify-content: flex-start;
  margin-top: var(--sp-14);
}
/* two half columns (tablet): the components take the full row, so the cards above them leave no gap
   below the shorter column */
@media (min-width: 45rem) and (max-width: 74.99rem) {
  .cstacks .about-comps {
    column-span: all;
    margin-top: var(--sp-16);
  }
}
@media (max-width: 44.99rem) {
  .about-version {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "name" "build" "meta" "actions";
  }
  .about-version .about-actions {
    margin-top: var(--sp-14);
    justify-content: flex-start;
  }
  .comp-list {
    grid-template-columns: minmax(0, 1fr);
  }
  .comp-list .comp:nth-child(odd) {
    border-right: 0;
  }
  .comp-list .comp:nth-last-child(-n + 2) {
    border-bottom: var(--hair-w) solid var(--color-border);
  }
  .comp-list .comp:last-child {
    border-bottom: 0;
  }
  .about-cfg .cset th:first-child {
    width: 50%;
  }
  .about-cfg .cset td.p1 code {
    white-space: normal;
    overflow-wrap: anywhere;
  }
}

/* count pill on a navigation entry: "Add devices 5" */
.nav-badge {
  display: inline-flex;
  align-items: center;
  height: 18px;
  padding: 0 var(--sp-6);
  border-radius: 9px;
  font: 500 11px var(--font-sans);
  white-space: nowrap;
  background: var(--color-accent);
  color: var(--color-on-accent);
}

/* phones: a crumb link is a 40 px touch target without moving the line */
@media (max-width: 44.99rem) {
  nav.crumbs a,
  .crumb.trail a {
    display: inline-block;
    padding: 11px 0;
    margin: -11px 0;
  }
}

/* ==== page: Dashboard (/, WP P1): tiles, health bar, "Needs attention" and the transactions ==== */
.dash-none {
  margin: 0;
  padding: var(--sp-14);
  font-size: 13px;
  color: var(--color-text-2);
}

/* ==========================================================================
   F5 (design v2): data tables, pinned columns, phone device cards, bulk bar.
   Markup: ui.Table / tableprefs.go (table, Columns chooser), ui.DeviceCards,
   devices.html (bulk bar). Behaviour: tables.js, select.js.
   ========================================================================== */

/* The table frame of the prototype: a surface card whose content scrolls sideways when the
   columns need more room than the page has (from 45rem; below it the device list shows cards).
   A table inside a card keeps the card's frame. */
@media (min-width: 45rem) {
  .tablescroll {
    overflow-x: auto;
  }
  /* without tables.js nothing places an open menu outside the scrolling box: it stops clipping */
  .tablescroll:not(.is-js):has(details[open]) {
    overflow: visible;
  }
  .tablewrap:not(.card .tablewrap) > .tablescroll {
    background: var(--color-surface);
    border: var(--hair-w) solid var(--color-border);
    border-radius: 10px;
  }
  .tablescroll > table {
    border-collapse: separate;
    border-spacing: 0;
  }
  .tablescroll > table > thead > tr > th {
    height: 38px;
    padding: 0 14px 0 10px;
    background: var(--color-surface-2);
    font: 500 12px var(--font-sans);
    letter-spacing: 0;
    text-transform: none;
    color: var(--color-text-2);
    vertical-align: middle;
  }
  .tablescroll > table > thead > tr > th.sel {
    padding: 0 0 0 6px; /* as td.sel: the select-all box on the row boxes' line */
  }
  .tablewrap:not(.card .tablewrap)
    > .tablescroll
    > table
    > tbody
    > tr:last-child
    > td {
    border-bottom: 0;
  }
}

/* Pinned columns (D19): the server marks them per table (plN pinned left, prN pinned right,
   N = the column's place), tables.js measures the offsets (--plN, --prN). They stay at their
   edge while the table scrolls sideways; a line separates them from the scrolling columns.
   The background is the row's (:where keeps the hover and selection colours in charge). */
@media (min-width: 45rem) {
  table.pl1 > :is(thead, tbody) > tr > :nth-child(1),
  table.pl2 > :is(thead, tbody) > tr > :nth-child(2),
  table.pl3 > :is(thead, tbody) > tr > :nth-child(3),
  table.pl4 > :is(thead, tbody) > tr > :nth-child(4),
  table.pl5 > :is(thead, tbody) > tr > :nth-child(5),
  table.pl6 > :is(thead, tbody) > tr > :nth-child(6),
  table.pl7 > :is(thead, tbody) > tr > :nth-child(7),
  table.pl8 > :is(thead, tbody) > tr > :nth-child(8),
  table.pl9 > :is(thead, tbody) > tr > :nth-child(9),
  table.pl10 > :is(thead, tbody) > tr > :nth-child(10),
  table.pl11 > :is(thead, tbody) > tr > :nth-child(11),
  table.pl12 > :is(thead, tbody) > tr > :nth-child(12),
  table.pl13 > :is(thead, tbody) > tr > :nth-child(13),
  table.pl14 > :is(thead, tbody) > tr > :nth-child(14),
  table.pl15 > :is(thead, tbody) > tr > :nth-child(15),
  table.pl16 > :is(thead, tbody) > tr > :nth-child(16),
  table.pr1 > :is(thead, tbody) > tr > :nth-child(1),
  table.pr2 > :is(thead, tbody) > tr > :nth-child(2),
  table.pr3 > :is(thead, tbody) > tr > :nth-child(3),
  table.pr4 > :is(thead, tbody) > tr > :nth-child(4),
  table.pr5 > :is(thead, tbody) > tr > :nth-child(5),
  table.pr6 > :is(thead, tbody) > tr > :nth-child(6),
  table.pr7 > :is(thead, tbody) > tr > :nth-child(7),
  table.pr8 > :is(thead, tbody) > tr > :nth-child(8),
  table.pr9 > :is(thead, tbody) > tr > :nth-child(9),
  table.pr10 > :is(thead, tbody) > tr > :nth-child(10),
  table.pr11 > :is(thead, tbody) > tr > :nth-child(11),
  table.pr12 > :is(thead, tbody) > tr > :nth-child(12),
  table.pr13 > :is(thead, tbody) > tr > :nth-child(13),
  table.pr14 > :is(thead, tbody) > tr > :nth-child(14),
  table.pr15 > :is(thead, tbody) > tr > :nth-child(15),
  table.pr16 > :is(thead, tbody) > tr > :nth-child(16) {
    position: sticky;
    z-index: 1;
  }
  :where(
    table.pl1 > :is(thead, tbody) > tr > :nth-child(1),
    table.pl2 > :is(thead, tbody) > tr > :nth-child(2),
    table.pl3 > :is(thead, tbody) > tr > :nth-child(3),
    table.pl4 > :is(thead, tbody) > tr > :nth-child(4),
    table.pl5 > :is(thead, tbody) > tr > :nth-child(5),
    table.pl6 > :is(thead, tbody) > tr > :nth-child(6),
    table.pl7 > :is(thead, tbody) > tr > :nth-child(7),
    table.pl8 > :is(thead, tbody) > tr > :nth-child(8),
    table.pl9 > :is(thead, tbody) > tr > :nth-child(9),
    table.pl10 > :is(thead, tbody) > tr > :nth-child(10),
    table.pl11 > :is(thead, tbody) > tr > :nth-child(11),
    table.pl12 > :is(thead, tbody) > tr > :nth-child(12),
    table.pl13 > :is(thead, tbody) > tr > :nth-child(13),
    table.pl14 > :is(thead, tbody) > tr > :nth-child(14),
    table.pl15 > :is(thead, tbody) > tr > :nth-child(15),
    table.pl16 > :is(thead, tbody) > tr > :nth-child(16),
    table.pr1 > :is(thead, tbody) > tr > :nth-child(1),
    table.pr2 > :is(thead, tbody) > tr > :nth-child(2),
    table.pr3 > :is(thead, tbody) > tr > :nth-child(3),
    table.pr4 > :is(thead, tbody) > tr > :nth-child(4),
    table.pr5 > :is(thead, tbody) > tr > :nth-child(5),
    table.pr6 > :is(thead, tbody) > tr > :nth-child(6),
    table.pr7 > :is(thead, tbody) > tr > :nth-child(7),
    table.pr8 > :is(thead, tbody) > tr > :nth-child(8),
    table.pr9 > :is(thead, tbody) > tr > :nth-child(9),
    table.pr10 > :is(thead, tbody) > tr > :nth-child(10),
    table.pr11 > :is(thead, tbody) > tr > :nth-child(11),
    table.pr12 > :is(thead, tbody) > tr > :nth-child(12),
    table.pr13 > :is(thead, tbody) > tr > :nth-child(13),
    table.pr14 > :is(thead, tbody) > tr > :nth-child(14),
    table.pr15 > :is(thead, tbody) > tr > :nth-child(15),
    table.pr16 > :is(thead, tbody) > tr > :nth-child(16)
  ) {
    background-color: var(--color-surface);
  }
  :where(
    table.pl1 > thead > tr > :nth-child(1),
    table.pl2 > thead > tr > :nth-child(2),
    table.pl3 > thead > tr > :nth-child(3),
    table.pl4 > thead > tr > :nth-child(4),
    table.pl5 > thead > tr > :nth-child(5),
    table.pl6 > thead > tr > :nth-child(6),
    table.pl7 > thead > tr > :nth-child(7),
    table.pl8 > thead > tr > :nth-child(8),
    table.pl9 > thead > tr > :nth-child(9),
    table.pl10 > thead > tr > :nth-child(10),
    table.pl11 > thead > tr > :nth-child(11),
    table.pl12 > thead > tr > :nth-child(12),
    table.pl13 > thead > tr > :nth-child(13),
    table.pl14 > thead > tr > :nth-child(14),
    table.pl15 > thead > tr > :nth-child(15),
    table.pl16 > thead > tr > :nth-child(16),
    table.pr1 > thead > tr > :nth-child(1),
    table.pr2 > thead > tr > :nth-child(2),
    table.pr3 > thead > tr > :nth-child(3),
    table.pr4 > thead > tr > :nth-child(4),
    table.pr5 > thead > tr > :nth-child(5),
    table.pr6 > thead > tr > :nth-child(6),
    table.pr7 > thead > tr > :nth-child(7),
    table.pr8 > thead > tr > :nth-child(8),
    table.pr9 > thead > tr > :nth-child(9),
    table.pr10 > thead > tr > :nth-child(10),
    table.pr11 > thead > tr > :nth-child(11),
    table.pr12 > thead > tr > :nth-child(12),
    table.pr13 > thead > tr > :nth-child(13),
    table.pr14 > thead > tr > :nth-child(14),
    table.pr15 > thead > tr > :nth-child(15),
    table.pr16 > thead > tr > :nth-child(16)
  ) {
    z-index: 3;
    background-color: var(--color-surface-2);
  }
  table.pl1 > :is(thead, tbody) > tr > :nth-child(1) {
    left: var(--pl1, 0px);
  }
  table.pl2 > :is(thead, tbody) > tr > :nth-child(2) {
    left: var(--pl2, 0px);
  }
  table.pl3 > :is(thead, tbody) > tr > :nth-child(3) {
    left: var(--pl3, 0px);
  }
  table.pl4 > :is(thead, tbody) > tr > :nth-child(4) {
    left: var(--pl4, 0px);
  }
  table.pl5 > :is(thead, tbody) > tr > :nth-child(5) {
    left: var(--pl5, 0px);
  }
  table.pl6 > :is(thead, tbody) > tr > :nth-child(6) {
    left: var(--pl6, 0px);
  }
  table.pl7 > :is(thead, tbody) > tr > :nth-child(7) {
    left: var(--pl7, 0px);
  }
  table.pl8 > :is(thead, tbody) > tr > :nth-child(8) {
    left: var(--pl8, 0px);
  }
  table.pl9 > :is(thead, tbody) > tr > :nth-child(9) {
    left: var(--pl9, 0px);
  }
  table.pl10 > :is(thead, tbody) > tr > :nth-child(10) {
    left: var(--pl10, 0px);
  }
  table.pl11 > :is(thead, tbody) > tr > :nth-child(11) {
    left: var(--pl11, 0px);
  }
  table.pl12 > :is(thead, tbody) > tr > :nth-child(12) {
    left: var(--pl12, 0px);
  }
  table.pl13 > :is(thead, tbody) > tr > :nth-child(13) {
    left: var(--pl13, 0px);
  }
  table.pl14 > :is(thead, tbody) > tr > :nth-child(14) {
    left: var(--pl14, 0px);
  }
  table.pl15 > :is(thead, tbody) > tr > :nth-child(15) {
    left: var(--pl15, 0px);
  }
  table.pl16 > :is(thead, tbody) > tr > :nth-child(16) {
    left: var(--pl16, 0px);
  }
  table.pr1 > :is(thead, tbody) > tr > :nth-child(1) {
    right: var(--pr1, 0px);
  }
  table.pr2 > :is(thead, tbody) > tr > :nth-child(2) {
    right: var(--pr2, 0px);
  }
  table.pr3 > :is(thead, tbody) > tr > :nth-child(3) {
    right: var(--pr3, 0px);
  }
  table.pr4 > :is(thead, tbody) > tr > :nth-child(4) {
    right: var(--pr4, 0px);
  }
  table.pr5 > :is(thead, tbody) > tr > :nth-child(5) {
    right: var(--pr5, 0px);
  }
  table.pr6 > :is(thead, tbody) > tr > :nth-child(6) {
    right: var(--pr6, 0px);
  }
  table.pr7 > :is(thead, tbody) > tr > :nth-child(7) {
    right: var(--pr7, 0px);
  }
  table.pr8 > :is(thead, tbody) > tr > :nth-child(8) {
    right: var(--pr8, 0px);
  }
  table.pr9 > :is(thead, tbody) > tr > :nth-child(9) {
    right: var(--pr9, 0px);
  }
  table.pr10 > :is(thead, tbody) > tr > :nth-child(10) {
    right: var(--pr10, 0px);
  }
  table.pr11 > :is(thead, tbody) > tr > :nth-child(11) {
    right: var(--pr11, 0px);
  }
  table.pr12 > :is(thead, tbody) > tr > :nth-child(12) {
    right: var(--pr12, 0px);
  }
  table.pr13 > :is(thead, tbody) > tr > :nth-child(13) {
    right: var(--pr13, 0px);
  }
  table.pr14 > :is(thead, tbody) > tr > :nth-child(14) {
    right: var(--pr14, 0px);
  }
  table.pr15 > :is(thead, tbody) > tr > :nth-child(15) {
    right: var(--pr15, 0px);
  }
  table.pr16 > :is(thead, tbody) > tr > :nth-child(16) {
    right: var(--pr16, 0px);
  }
  :where(
    table.pl2 > :is(thead, tbody) > tr > :nth-child(2),
    table.pl3 > :is(thead, tbody) > tr > :nth-child(3),
    table.pl4 > :is(thead, tbody) > tr > :nth-child(4),
    table.pl5 > :is(thead, tbody) > tr > :nth-child(5),
    table.pl6 > :is(thead, tbody) > tr > :nth-child(6),
    table.pl7 > :is(thead, tbody) > tr > :nth-child(7),
    table.pl8 > :is(thead, tbody) > tr > :nth-child(8),
    table.pl9 > :is(thead, tbody) > tr > :nth-child(9),
    table.pl10 > :is(thead, tbody) > tr > :nth-child(10),
    table.pl11 > :is(thead, tbody) > tr > :nth-child(11),
    table.pl12 > :is(thead, tbody) > tr > :nth-child(12),
    table.pl13 > :is(thead, tbody) > tr > :nth-child(13),
    table.pl14 > :is(thead, tbody) > tr > :nth-child(14),
    table.pl15 > :is(thead, tbody) > tr > :nth-child(15),
    table.pl16 > :is(thead, tbody) > tr > :nth-child(16)
  ) {
    box-shadow: inset -1px 0 0 var(--color-border);
  }
  :where(
    table.pr1 > :is(thead, tbody) > tr > :nth-child(1),
    table.pr2 > :is(thead, tbody) > tr > :nth-child(2),
    table.pr3 > :is(thead, tbody) > tr > :nth-child(3),
    table.pr4 > :is(thead, tbody) > tr > :nth-child(4),
    table.pr5 > :is(thead, tbody) > tr > :nth-child(5),
    table.pr6 > :is(thead, tbody) > tr > :nth-child(6),
    table.pr7 > :is(thead, tbody) > tr > :nth-child(7),
    table.pr8 > :is(thead, tbody) > tr > :nth-child(8),
    table.pr9 > :is(thead, tbody) > tr > :nth-child(9),
    table.pr10 > :is(thead, tbody) > tr > :nth-child(10),
    table.pr11 > :is(thead, tbody) > tr > :nth-child(11),
    table.pr12 > :is(thead, tbody) > tr > :nth-child(12),
    table.pr13 > :is(thead, tbody) > tr > :nth-child(13),
    table.pr14 > :is(thead, tbody) > tr > :nth-child(14),
    table.pr15 > :is(thead, tbody) > tr > :nth-child(15),
    table.pr16 > :is(thead, tbody) > tr > :nth-child(16)
  ) {
    box-shadow: inset 1px 0 0 var(--color-border);
  }
  /* an open row menu lies over the cells of the rows below it, pinned ones included */
  .tablescroll td:has(> details.rowmenu[open]) {
    z-index: 4;
  }
}

/* Phone device cards (ui.DeviceCards): below 45rem the device list shows cards instead of the
   table; the server renders both. */
.dcards {
  display: none;
}
@media (max-width: 44.99rem) {
  .tablewrap[data-key="devices"],
  #devtable > .toolbar {
    display: none;
  }
  .dcards {
    display: flex;
    flex-direction: column;
    gap: 8px;
  }
  .dcards-all {
    display: flex;
    align-items: center;
    gap: 8px;
    height: 40px;
    padding: 0 4px;
    font-size: 13px;
    color: var(--color-text-2);
  }
  .dcards-all[hidden] {
    display: none;
  }
  .dcards-all input,
  .dcard-sel input {
    width: 18px;
    height: 18px;
    margin: 0;
    accent-color: var(--color-accent);
  }
  .dcard-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 0;
    padding: 0;
    list-style: none;
  }
  .dcard {
    display: grid;
    grid-template-columns: 40px minmax(0, 1fr) auto;
    align-items: start;
    gap: 0 4px;
    padding: 6px 6px 10px 2px;
    background: var(--color-surface);
    border: var(--hair-w) solid var(--color-border);
    border-radius: 10px;
  }
  .dcard:not(:has(.dcard-sel)) {
    grid-template-columns: minmax(0, 1fr) auto;
    padding-left: 12px;
  }
  .dcard:has(.dcard-sel input:checked) {
    background: var(--color-accent-bg);
    border-color: var(--color-accent);
  }
  .dcard-sel {
    display: grid;
    place-items: center;
    width: 40px;
    height: 44px;
  }
  .dcard-main {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
    padding: 10px 0 0;
  }
  .dcard-main a.dcard-title::after {
    content: "";
    position: absolute;
    inset: 0;
  }
  .dcard-name {
    display: flex;
    align-items: flex-start;
    gap: 7px;
  }
  .dcard-dot {
    flex: none;
    width: 8px;
    height: 8px;
    margin-top: 6px;
    border-radius: 50%;
    background: var(--color-neutral);
  }
  .dcard:has(.dcard-badges .tone-ok) .dcard-dot {
    background: var(--color-ok);
  }
  .dcard:has(.dcard-badges .tone-warn) .dcard-dot {
    background: var(--color-warn);
  }
  .dcard:has(.dcard-badges .tone-bad) .dcard-dot {
    background: var(--color-danger);
  }
  .dcard-main a.dcard-title {
    color: var(--color-text);
    font-weight: 600;
    line-height: 1.3;
    text-decoration: none;
    overflow-wrap: anywhere;
  }
  .dcard-main:hover a.dcard-title {
    color: var(--color-accent-fg);
    text-decoration: underline;
  }
  .dcard-meta {
    font: 400 12px var(--font-mono);
    color: var(--color-text-2);
    overflow-wrap: anywhere;
    text-wrap: balance; /* "addr · chip ·" / "16.0.0" becomes two even lines */
  }
  /* address, chip and version break between each other, not inside */
  .dcard-meta-part {
    display: inline-block; /* one unit for the line breaker; max-width lets a very long host still wrap */
    max-width: 100%;
  }
  .dcard-badges {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 5px;
  }
  /* the card shows the status badge and a failing channel only (the prototype's "MQTT down");
     the last-report age and the working channels are the table's detail */
  .dcard-badges > small.muted,
  .dcard-badges .chan:not(.chan-bad) {
    display: none;
  }
  .dcard-last {
    font-size: 12px;
    color: var(--color-text-3);
  }
  /* the prototype's side column: the 48 x 44 switch over the 44 x 44 "⋯", centred, no gap */
  .dcard-side {
    display: flex;
    flex-direction: column;
    align-items: center;
  }
}

/* Bulk bar (prototype): on a wide screen a dark bar centred at the bottom of the window with
   the count, Update firmware, Send command, Back up, Restart, Power on, Power off and Clear;
   on a phone a light panel across the bottom: the count and Clear, then the actions in a grid
   of three. It is fixed to the window, so it is never clipped (layout guard a). The bars of the
   pages not ported yet (no .bulk-acts) keep their old look until their package moves them. */
.bulkbar:has(> .bulk-acts) {
  position: fixed;
  z-index: var(--z-sticky);
  left: 0;
  right: 0;
  bottom: calc(var(--sb-h) + 16px);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  width: fit-content;
  max-width: calc(100% - 32px);
  min-height: 0;
  margin: 0 auto;
  padding: 7px 7px 7px 16px;
  background: var(--color-text);
  color: var(--color-surface);
  border: 0;
  border-radius: 11px;
  box-shadow: var(--shadow-lg);
}
.bulkbar:has(> .bulk-acts)[hidden] {
  display: none;
}
.bulkbar:has(> .bulk-acts) .bulk-count {
  flex: none;
  margin-right: 8px;
  font: 600 13px var(--font-sans);
  white-space: nowrap;
}
.bulkbar:has(> .bulk-acts) .bulk-acts {
  display: contents;
}
.bulkbar:has(> .bulk-acts) button {
  flex: none;
  min-height: 0;
  height: 34px;
  padding: 0 12px;
  border-radius: 7px;
  border: var(--hair-w) solid
    color-mix(in oklab, var(--color-surface) 30%, transparent);
  background: transparent;
  box-shadow: none;
  color: var(--color-surface);
  font: 500 13px var(--font-sans);
  white-space: nowrap;
}
.bulkbar:has(> .bulk-acts) button.primary {
  border: 0;
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-weight: 600;
}
.bulkbar:has(> .bulk-acts) .bulk-clear {
  padding: 0 10px;
  border: 0;
}
.bulkbar:has(> .bulk-acts) .bulk-sep {
  width: 1px;
  height: 20px;
  margin: 0 4px;
  background: color-mix(in oklab, var(--color-surface) 25%, transparent);
}
.bulkbar:has(> .bulk-acts) .bulk-sm {
  display: none;
}
body:has(.bulkbar:not([hidden]) > .bulk-acts) {
  padding-bottom: calc(80px + var(--sb-h));
}
/* how many selected devices are not online and will be skipped (select.js) */
.bulkbar .bulk-note {
  margin-left: 8px;
  font-weight: 500;
  opacity: 0.75;
}
.bulkbar .bulk-note::before {
  content: "\00b7  ";
}
.bulkbar .bulk-note[hidden] {
  display: none;
}
@media (max-width: 44.99rem) {
  .bulkbar .bulk-note::before {
    content: none;
  }
  .bulkbar .bulk-note {
    display: block;
    margin-left: 0;
    white-space: normal;
  }
  .bulkbar .bulk-note[hidden] {
    display: none;
  }
}
@media (max-width: 44.99rem) {
  .bulkbar:has(> .bulk-acts) {
    bottom: 0;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 8px 6px;
    width: auto;
    max-width: none;
    padding: 10px 12px calc(14px + env(safe-area-inset-bottom, 0px));
    background: var(--color-surface);
    color: var(--color-text);
    border-top: var(--hair-w) solid var(--color-border);
    border-radius: 0;
    box-shadow: 0 -6px 20px rgba(0, 0, 0, 0.08);
  }
  .bulkbar:has(> .bulk-acts) .bulk-count {
    grid-row: 1;
    grid-column: 1;
    margin: 0;
    font-size: 13.5px;
  }
  .bulkbar:has(> .bulk-acts) .bulk-clear {
    grid-row: 1;
    grid-column: 2;
    height: 40px;
    background: none;
    color: var(--color-accent-fg);
    font-size: 13.5px;
  }
  .bulkbar:has(> .bulk-acts) .bulk-acts {
    display: grid;
    grid-column: 1 / -1;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 6px;
  }
  .bulkbar:has(> .bulk-acts) .bulk-acts button {
    height: 44px;
    padding: 0 6px;
    border-radius: 8px;
    border: var(--hair-w) solid var(--color-border-strong);
    background: var(--color-surface);
    color: var(--color-text);
    font-size: 12.5px;
  }
  .bulkbar:has(> .bulk-acts) .bulk-acts button.primary {
    border: 0;
    background: var(--color-accent);
    color: var(--color-on-accent);
  }
  .bulkbar:has(> .bulk-acts) .bulk-sep,
  .bulkbar:has(> .bulk-acts) .bulk-lg {
    display: none;
  }
  .bulkbar:has(> .bulk-acts) .bulk-sm {
    display: inline;
  }
  body:has(.bulkbar:not([hidden]) > .bulk-acts) {
    padding-bottom: calc(180px + var(--sb-h));
  }
}

/* ==== page: Devices (/devices, WP P2): toolbar, the device table's cells, row menu ====
   Markup: devices.templ, the row partials in partials.html. Values are the prototype's. */
.dv-list {
  position: relative;
}
.pghead:has(+ #list-notice) {
  margin-bottom: 14px;
}
.dv-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0 0 12px;
}
.dv-q {
  flex: 1 1 180px;
  min-width: 0;
  height: var(--ctl);
  padding: 0 10px;
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: 7px;
  background: var(--color-surface);
  color: var(--color-text);
  font: 400 13.5px var(--font-sans);
}
.dv-chips {
  display: flex;
  gap: 6px;
  max-width: 100%;
  padding-bottom: 2px;
  overflow-x: auto;
}
/* The Devices list shows every filter at once: the chips wrap onto further lines instead of scrolling
   sideways, so a phone never hides "Not compliant" or the Chip and Tag pickers off its edge (#678).
   The other chip rows (Backups, Policy) keep the scroll strip above. */
.dv-list .dv-chips {
  flex-wrap: wrap;
  padding-bottom: 0;
  overflow-x: visible;
}
.dv-chip {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 6px;
  height: var(--ctl);
  padding: 0 11px;
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: 999px;
  background: var(--color-surface);
  color: var(--color-text);
  font: 500 12.5px var(--font-sans);
  white-space: nowrap;
  text-decoration: none;
}
.dv-chip[aria-current] {
  border-color: var(--color-text);
  background: var(--color-text);
  color: var(--color-surface);
}
.dv-n {
  font-variant-numeric: tabular-nums;
  opacity: 0.75;
}
.dv-vsep {
  flex: none;
  align-self: stretch;
  width: 1px;
  margin: 6px 2px;
  background: var(--color-border);
}
/* The separator between the chips and the pickers only belongs to a single line: when the Devices list's
   row wraps it would be left hanging at the end of a line, so it shows only where the row fits (#678). */
.dv-list .dv-vsep {
  display: none;
}
@media (min-width: 70rem) {
  .dv-list .dv-vsep {
    display: block;
  }
}
.dv-pick {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 4px;
  height: var(--ctl);
  margin: 0;
  padding: 0 2px 0 11px;
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: 999px;
  background: var(--color-surface);
  color: var(--color-text-2);
  font: 500 12.5px var(--font-sans);
  white-space: nowrap;
}
.dv-pick.on {
  border-color: var(--color-text);
}
.dv-pick select {
  appearance: auto;
  width: auto;
  min-width: 0;
  height: 100%;
  min-height: 0;
  margin: 0;
  padding: 0 6px;
  border: 0;
  border-radius: 999px;
  background-color: transparent;
  background-image: none;
  box-shadow: none;
  color: var(--color-text);
  font: 500 12.5px var(--font-sans);
  cursor: pointer;
}
.dv-grow {
  flex: 1;
}
.dv-sort {
  display: none;
  align-items: center;
  gap: 6px;
  margin: 0;
  color: var(--color-text-2);
  font: 400 12.5px var(--font-sans);
}
.dv-sort select {
  appearance: auto;
  width: auto;
  height: var(--ctl);
  min-height: 0;
  margin: 0;
  padding: 0 8px;
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: 7px;
  background-color: var(--color-surface);
  background-image: none;
  color: var(--color-text);
  font: 400 13px var(--font-sans);
}
/* the Columns chooser of the reloaded table part sits at the end of the toolbar's first row */
.dv-cols {
  display: none;
}
@media (min-width: 45rem) {
  .dv-tools {
    padding-right: 98px;
  }
  /* on a wide screen the field stays a field; a phone gives it its whole line */
  .dv-q {
    max-width: 380px;
  }
  .dv-cols {
    display: block;
    position: absolute;
    top: 0;
    right: 0;
  }
  .dv-cols .colbtn {
    gap: 6px;
    height: var(--ctl);
    padding: 0 12px;
    border-radius: 7px;
    font: 500 13px var(--font-sans);
  }
  .dv-cols .colbtn::after {
    content: "▾";
    width: auto;
    height: auto;
    border: 0;
    transform: none;
    font-size: 10px;
  }
}
@media (max-width: 44.99rem) {
  .dv-sort {
    display: flex;
  }
  /* a 40 px touch target: the select covers its pill's border too */
  .dv-pick select {
    height: calc(100% + 2px);
    margin: -1px 0;
  }
}
.dv-nomatch {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 32px 16px;
  border: var(--hair-w) solid var(--color-border);
  border-radius: 10px;
  background: var(--color-surface);
  text-align: center;
}
.dv-nomatch b {
  font-weight: 600;
}
.dv-nomatch span {
  color: var(--color-text-2);
  font-size: 13px;
}

/* the device table: the prototype's fixed column widths; it scrolls sideways below their sum */
@media (min-width: 45rem) {
  /* a definite width keeps the fixed layout (max-content would fall back to auto and let a long
     address widen its column); the table is then the sum of its columns, at least the frame */
  table.devtable:not(.tfix) {
    table-layout: fixed;
    width: 1px;
    min-width: 100%;
  }
  table.devtable col.c-sel {
    width: 44px;
  }
  table.devtable col.c-name {
    width: 280px;
  }
  table.devtable col.c-status {
    width: 170px;
  }
  table.devtable col.c-reach {
    width: 150px;
  }
  table.devtable col.c-fw {
    width: 140px;
  }
  table.devtable col.c-fam {
    width: 100px;
  }
  table.devtable col.c-addr {
    width: 132px;
  }
  table.devtable col.c-up {
    width: 120px; /* "seen 30s ago" is 12 characters of 13px mono plus padding */
  }
  table.devtable col.c-wifi {
    width: 124px;
  }
  table.devtable col.c-restart {
    width: 180px;
  }
  table.devtable col.c-ota {
    width: 260px;
  }
  table.devtable col.c-gt {
    width: 130px;
  }
  table.devtable col.c-pow {
    width: 72px;
  }
  table.devtable col.c-act {
    width: 52px;
  }
  table.devtable {
    font-size: 13px;
  }
  .tablescroll > table.devtable > thead > tr > th.sel,
  .tablescroll > table.devtable > tbody > tr > td.sel {
    padding: 0 0 0 14px;
    text-align: left;
  }
  /* relative: a cell is the containing block of its screen-reader texts (absolute), which
     otherwise escape the scrolling box and widen the page */
  table.devtable > tbody > tr > td {
    position: relative;
    height: 44px;
    padding: 0 10px;
    border-bottom: var(--hair-w) solid var(--color-border);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: middle;
  }
  table.devtable > tbody > tr > td.dv-name {
    padding: 3px 10px;
    white-space: normal;
  }
  table.devtable td.dv-name > a {
    display: -webkit-box;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    color: var(--color-text);
    font: 500 13px/1.3 var(--font-sans);
    text-decoration: none;
    overflow-wrap: anywhere;
  }
  table.devtable td.dv-name > a:hover {
    color: var(--color-accent-fg);
    text-decoration: underline;
  }
  /* the row menu opens inside its cell (without JavaScript; tables.js places it fixed) */
  table.devtable > tbody > tr > td.rowacts {
    padding: 0 6px;
    overflow: visible;
  }
  /* the selection box sits 14 px in, with the browser's own checkbox margin, like the prototype's */
  table.devtable input[type="checkbox"] {
    width: 15px;
    height: 15px;
    margin: 3px 3px 3px 4px;
    vertical-align: middle;
    accent-color: var(--color-accent);
  }
  .tablescroll > table.devtable > thead > tr > th.sel > input:not([hidden]) {
    display: inline-block;
    margin: 3px 3px 3px 4px;
  }
  table.devtable td.sel > label.hit {
    display: inline;
    min-width: 0;
    min-height: 0;
    margin: 0;
    padding: 0;
  }
  table.devtable .sortlink .sortmark {
    display: none;
  }
  table.devtable .sortlink.is-asc::after {
    content: " ↑";
    white-space: pre;
  }
  table.devtable .sortlink.is-desc::after {
    content: " ↓";
    white-space: pre;
  }
}
.dv-st {
  gap: 5px;
}
.dv-dot {
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--t);
}
.dv-mdown {
  display: none;
}
/* OTA URL and group topic: one line cut off by the cell (the title holds the whole value), the
   time it was read under it. */
.dv-trunc {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dv-readat {
  font-size: 0.85em;
}
.dv-mono,
.dv-mono a {
  font: 400 12.5px var(--font-mono);
  color: var(--color-text-2);
}
td[data-label="Firmware"] .dv-mono {
  color: var(--color-text);
}
.dv-upd {
  height: 18px;
  font-size: 11px;
}
.dv-reach {
  font: 500 12px var(--font-mono);
}
.dv-reach .chan {
  display: inline;
  height: auto;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  font: inherit;
  color: var(--color-text-3);
}
.dv-reach .chan-ok {
  color: var(--color-ok-fg);
}
.dv-reach .chan-bad {
  color: var(--color-danger-fg);
}
.dv-reach-sep {
  color: var(--color-text-3);
}
.dv-wifi {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  vertical-align: middle;
}
.dv-wbar {
  flex: none;
}
.dv-wbg {
  fill: var(--color-surface-3);
}
.dv-wfg {
  fill: var(--color-ok);
}
.dv-wfg.weak {
  fill: var(--color-warn);
}
.dv-dbm {
  font: 400 12px var(--font-mono);
  color: var(--color-text-2);
}
/* the power switch of a row: 32 x 18 inside a 44 x 32 target; a card's is 34 x 20 in 48 x 44 */
td.pcell .pwr,
td.pcell .pwr[aria-pressed="true"] {
  width: 44px;
  height: 32px;
  border: 0;
  background: none;
  vertical-align: middle;
}
td.pcell .pwr .sw {
  width: 32px;
  height: 18px;
  border-radius: 9px;
}
.pcell .pwr[aria-pressed="true"] .sw {
  background: var(--color-accent);
}
td.pcell .pwr[aria-pressed="true"] .sw::after {
  left: 16px;
}
.dcard-side .pwr[aria-pressed="true"] {
  border: 0;
  background: none;
}
.pcell:has(.pwr + .pwr) .pwr {
  width: 36px;
}
.dcard-side .pwr {
  width: 48px;
  height: 44px;
}
.dcard-side .pwr .sw {
  width: 34px;
  height: 20px;
  border-radius: 10px;
}
.dcard-side .pwr .sw::after {
  top: 3px;
}
.dcard-side .pwr[aria-pressed="true"] .sw::after {
  left: 18px;
}
/* the row menu trigger "⋯" and its panel */
.rowacts .umenu > summary,
.dcard-side .umenu > summary {
  width: 36px;
  height: 32px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--color-text-2);
  font: 600 16px var(--font-sans);
}
.dcard-side .umenu > summary {
  width: 44px;
  height: 44px;
  font-size: 18px;
}
.rowacts .umenu > summary:hover {
  background: var(--color-surface-3);
}
.rowacts .umenu .menu-panel,
.dcard-side .umenu .menu-panel {
  width: 200px;
}
.rowacts .umenu .menu-panel .menu-item,
.dcard-side .umenu .menu-panel .menu-item {
  height: var(--ctl);
  min-height: 0;
  padding: 0 10px;
  border-radius: 6px;
}
/* phone cards: the status badge without its dot (the name carries the dot), MQTT down last */
.dcards-all {
  font-weight: 400;
}
/* the device list's bulk bar is in the flow below the list and sticks to the window's bottom
   edge while the list is in view, as in the prototype (the page grows by the bar); the other
   bars stay fixed (F5) */
.dv-list .bulkbar:has(> .bulk-acts) {
  position: sticky;
  bottom: calc(var(--sb-h) + 16px);
  margin: 14px auto 0;
}
/* at the end of the scroll the bar rests where it sticks mid-scroll, one gap above the status bar */
body:has(.dv-list .bulkbar:not([hidden])) {
  padding-bottom: calc(var(--sb-h) + var(--sp-16));
}
@media (max-width: 44.99rem) {
  .dv-list .bulkbar:has(> .bulk-acts) {
    bottom: var(--sb-h);
    margin: 12px calc(-1 * var(--page-pad, 12px)) 0;
  }
  body:has(.dv-list .bulkbar:not([hidden])) {
    padding-bottom: var(--sb-h);
  }
}
.dcard-st {
  display: contents;
}
.dcard .dv-dot {
  display: none;
}
.dcard .dv-mdown {
  display: inline-flex;
  order: 9;
}
.dcard-last .dv-wbar,
.dcard-last .dl-last,
.dcard-last .dl-never,
.dcard-last:has(.dv-seen) :is(.dl-up, .dl-sep, .dl-wifi),
.dcard:has(.dv-st.t-neutral) .dcard-last > :not(.dl-never) {
  display: none;
}
.dcard-last:has(.dv-seen) .dl-last,
.dcard:has(.dv-st.t-neutral) .dcard-last .dl-never {
  display: inline;
}
.dcard-last .dv-dbm {
  font: inherit;
  color: inherit;
}
.dcard:has(.dv-st.t-ok) .dcard-dot {
  background: var(--color-ok);
}
.dcard:has(.dv-st.t-warn) .dcard-dot {
  background: var(--color-warn);
}
.dcard:has(.dv-st.t-danger) .dcard-dot {
  background: var(--color-danger);
}

/* ==== page: device (WP P3): the header of every device tab, the overview's cards, the
   console, settings and drift tabs and the edit form (prototype screens "device" and "edit") ==== */
.devhd {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: var(--sp-12);
}
.devhd-t {
  min-width: 0;
  flex: 1 1 360px;
}
.devhd-name {
  display: flex;
  align-items: center;
  gap: var(--sp-10);
  flex-wrap: wrap;
}
.devhd-name h1 {
  margin: 0;
  min-width: 0;
  font: 600 22px/1.25 var(--font-sans);
  letter-spacing: -0.015em;
  overflow-wrap: anywhere;
}
.devhd-state {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 22px;
  padding: 0 var(--sp-8);
  border-radius: 5px;
  font: 500 12px var(--font-sans);
  background: var(--t-bg);
  color: var(--t-fg);
}
.devhd-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--t);
}
.devhd-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-6) var(--sp-12);
  margin-top: var(--sp-6);
  font: 400 12.5px var(--font-mono);
  color: var(--color-text-2);
}
.devhd-meta form,
.devhd-acts form,
.devdrift-bar form {
  display: flex;
  margin: 0;
}
.devhd-upd button {
  height: 22px;
  padding: 0 var(--sp-8);
  border: 0;
  border-radius: 5px;
  background: var(--color-accent-bg);
  color: var(--color-accent-fg);
  font: 500 12px var(--font-sans);
  text-box: normal;
  cursor: pointer;
}
.devhd-acts {
  display: flex;
  gap: var(--sp-6);
  flex-wrap: wrap;
}
/* the switch of the header and of a relay: a track with a white knob, the accent when on */
.devsw {
  position: relative;
  flex: none;
  width: 30px;
  height: 18px;
  border-radius: 9px;
  background: var(--color-border-strong);
}
.devsw::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-knob);
}
[aria-pressed="true"] > .devsw,
[aria-checked="true"] > .devsw {
  background: var(--color-accent);
}
[aria-pressed="true"] > .devsw::after {
  left: 14px;
}
.devhd-off {
  display: flex;
  gap: var(--sp-10);
  align-items: center;
  flex-wrap: wrap;
  padding: var(--sp-10) 14px;
  margin-bottom: var(--sp-12);
  border-radius: 9px;
  border: var(--hair-w) solid var(--color-warn);
  background: var(--color-warn-bg);
  color: var(--color-warn-fg);
  font-size: 13px;
}
.devhd-off > div {
  flex: 1;
  min-width: 220px;
}
.devhd-off b {
  font-weight: 600;
}
.devhd-offdot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-warn);
}
.devhd ~ .banner {
  margin: 0 0 var(--sp-12);
}

.devcard {
  break-inside: avoid;
  margin-bottom: var(--card-gap);
}
.devcard {
  min-width: 0;
  padding: 14px var(--sp-16);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  background: var(--color-surface);
}
.devcard h2 {
  margin: 0 0 var(--sp-10);
  font: 600 13px var(--font-sans);
  letter-spacing: normal;
}
.devcard-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-10);
  margin-bottom: var(--sp-10);
}
.devcard-head h2 {
  margin: 0;
}
/* the links and the Columns chooser at the end of a card's title line */
.devcard-tools {
  display: flex;
  align-items: center;
  gap: var(--sp-8);
}
/* Identity: the Refresh link sits in the title's line without making it taller */
#identity .devcard-head {
  margin: -8px 0 2px;
}
.devcard .banner {
  margin: var(--sp-10) 0 0;
}
.devcard table {
  margin: 0;
}
.devcard-note {
  margin: 0;
  font-size: 13px;
  color: var(--color-text-2);
}
.devcard-h3 {
  margin: var(--sp-12) 0 4px;
  font: 600 12px var(--font-sans);
  color: var(--color-text-2);
}
.devrelay-sw {
  display: grid;
  place-items: center;
  width: 44px;
  height: 36px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.devrelay-sw:disabled {
  cursor: default;
  opacity: 0.5;
}
.devrelay-sw .devsw {
  width: 32px;
}
.devrelay-sw[aria-checked="true"] .devsw::after {
  left: 16px;
}
/* ==== device overview (#145): live tiles, relay box, Identity | Connection, Activity ==== */
.devlive {
  display: flex;
  flex-direction: column;
  gap: var(--card-gap);
  margin-bottom: var(--card-gap);
}
.devlive > .devcard {
  margin-bottom: 0;
}
/* one row, equal width; a tile that does not apply is not built, so the row has no holes. Three
   across between phone and desktop, two on a phone */
.dev-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sp-10);
}
@media (max-width: 44.99rem) {
  .dev-tiles {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  /* "MQTT · HTTP" must fit a half-width tile at 320 px (#420): the smaller size keeps it on one line,
     and it may wrap at a space (no nowrap) rather than run past the tile edge when text is larger.
     A lone last tile takes the whole row */
  .dev-tile-v {
    font-size: 20px;
  }
  .dev-tile[data-tile="reach"] .dev-tile-v {
    font-size: 17px;
  }
  .dev-tile:last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }
}
.dev-tile {
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
  min-width: 0;
  padding: var(--sp-14);
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
}
.dev-tile-l {
  font: 500 12.5px var(--font-sans);
  color: var(--color-text-2);
}
.dev-tile-v {
  font: 600 var(--fs-stat) / 1 var(--font-sans);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  /* the text variant of the tone, not the fill: the fill misses WCAG AA on the surface (#513) */
  color: var(--t-fg, var(--color-text));
  overflow-wrap: anywhere;
}
.dev-tile-s {
  font-size: 12px;
  color: var(--color-text-3);
  overflow-wrap: anywhere;
}
.dev-tile-fix {
  align-self: flex-start;
  margin-top: var(--sp-4);
}
.dev-bar {
  display: block;
  width: 100%;
  height: 6px;
  border-radius: 3px;
}
.dev-bar-track {
  fill: var(--color-surface-3);
}
.dev-bar-fill {
  fill: var(--t, var(--color-warn));
}
/* offline: the last reported state, dimmed. The controls inside are disabled by the page */
.is-dim {
  opacity: 0.55;
}
/* the policy rule the editor just saved */
tr.is-saved > td {
  background: var(--color-accent-bg);
}
.dev-box-meta {
  font-size: 12px;
  color: var(--color-text-2);
}
/* always the same four-column box, one column at 375 px, two in between */
.dev-relays {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-10);
}
@media (max-width: 70rem) {
  .dev-relays {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 30rem) {
  .dev-relays {
    grid-template-columns: minmax(0, 1fr);
  }
}
.dev-relay {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-10);
  min-width: 0;
  margin: 0;
  padding: var(--sp-6) var(--sp-10);
  border: var(--hair-w) solid var(--color-border);
  border-radius: 8px;
  background: var(--color-surface-2);
}
.dev-relay-t {
  display: flex;
  flex-direction: column;
  min-width: 0;
  font-size: 13px;
}
.dev-relay-n {
  overflow-wrap: anywhere;
}
.dev-relay-n .mono {
  font: 400 11px var(--font-mono);
  color: var(--color-text-3);
}
.dev-relay-s {
  font-size: 12px;
  color: var(--color-text-2);
}
.dev-sensor {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  min-width: 0;
  padding: var(--sp-8) var(--sp-10);
  border: var(--hair-w) solid var(--color-border);
  border-radius: 8px;
  background: var(--color-surface-2);
}
.dev-sensor-l {
  font-size: 12px;
  color: var(--color-text-2);
}
.dev-sensor-v {
  font: 600 18px var(--font-sans);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.dev-box .error {
  margin: var(--sp-8) 0 0;
}
/* Identity | Connection: two equal columns, stacked on a phone */
.dev-pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--card-gap);
  align-items: stretch;
  margin-bottom: var(--card-gap);
}
@media (max-width: 44.99rem) {
  .dev-pair {
    grid-template-columns: minmax(0, 1fr);
  }
}
.dev-act {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
}
.dev-act li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-4) var(--sp-12);
  padding: var(--sp-6) 0;
  border-top: var(--hair-w) solid var(--color-border);
  font-size: 13px;
}
.dev-act li:first-child {
  border-top: 0;
}
/* Activity page (#419-D): the entries flow through as many columns as fit (2 at 1280, 3 at 1920),
   newest first down each column; the overview's box stays one column */
.devcard.cw-act .dev-act {
  display: block;
  column-width: 24rem;
  column-gap: var(--sp-24, 24px);
}
.devcard.cw-act .dev-act li {
  break-inside: avoid;
}
.devcard.cw-act .dev-act li:first-child {
  border-top: var(--hair-w) solid var(--color-border);
}
.dev-act-at {
  flex: none;
  min-width: 9.5em;
  font: 400 12px var(--font-mono);
  color: var(--color-text-3);
}
.dev-act-more {
  color: var(--color-text-2);
  overflow-wrap: anywhere;
}
.devnote {
  margin: var(--sp-10) 0 0;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.devconfirm {
  margin-bottom: var(--sp-12);
}
.devconfirm p {
  margin: 0;
  font-size: 13px;
}

/* console tab: the panel's fixed height, the prompt's look and the quick commands below it */
.console-lines {
  height: min(440px, 52vh);
  max-height: none;
}
.console-line.is-dim .console-text {
  color: var(--color-term-dim);
}
.console a.console-clear {
  display: inline-flex;
  align-items: center;
  border-radius: 6px;
  border-color: var(--color-console-line);
  text-decoration: none;
}
.console-form input:not([type="checkbox"]):not([type="radio"]) {
  min-width: 0;
  padding: 0 var(--sp-10);
  border-radius: 7px;
  background: color-mix(in srgb, var(--color-console-rule) 50%, transparent);
}
/* the terminal keeps its own colours in both themes: the global placeholder grey (text-2) is
   nearly invisible on it in light mode */
.console-form input::placeholder {
  color: color-mix(in srgb, var(--color-term-fg) 62%, transparent);
  opacity: 1;
}
.console-form .console-send {
  height: var(--ctl);
  padding: 0 14px;
  border: 0;
  border-radius: 7px;
  background: var(--color-term-cmd);
  color: var(--color-term-bg);
  font: 600 13px var(--font-sans);
  text-box: normal;
  cursor: pointer;
}
.console-quick {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-6);
  margin: var(--sp-10) 0 0;
  padding: 0;
}
.console-quick-l {
  margin-right: 4px;
  font-size: 12px;
  color: var(--color-text-3);
}
.btn.console-chip {
  padding: 0 var(--sp-10);
  font: 400 12.5px var(--font-mono);
}

/* command history: the "Clear history" link under the console prompt (history.js shows it) */
.console .console-histbar {
  display: block;
  padding: 0 var(--sp-14) var(--sp-8);
  text-align: right;
}
.bk-histlink {
  align-self: flex-start;
  font-size: 12px;
}
.bk-histlink[hidden] {
  display: none;
}
.console .console-histlink {
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-term-dim);
  font: 500 12px var(--font-sans);
  cursor: pointer;
}
.console .console-histlink:hover {
  color: var(--color-term-fg);
  text-decoration: underline;
}
.console .console-histlink[hidden] {
  display: none;
}

/* settings tab and edit form: one card, label-above fields in columns of at least 260 px */
.devform {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: 14px 18px;
  max-width: 760px;
  margin: 0;
  padding: 18px;
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  background: var(--color-surface);
}
.devform-edit {
  max-width: 720px; /* the width of the Add devices form */
}
.devform-msg {
  display: flex;
  flex-direction: column;
  gap: var(--sp-12);
}
.devform-msg h2,
.devform-h {
  margin: 0;
  font: 600 13px var(--font-sans);
  letter-spacing: normal;
}
.devform-msg p {
  margin: 0;
  font-size: 13px;
  color: var(--color-text-2);
}
.devform-h {
  grid-column: 1 / -1;
  padding-top: 14px;
  border-top: var(--hair-w) solid var(--color-border);
}
.devform-foot {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-8);
  padding-top: 14px;
  border-top: var(--hair-w) solid var(--color-border);
}
.devform-count {
  flex: 1 1 16rem; /* own line on a phone; a zero basis squeezed the text between the buttons */
  font-size: 12.5px;
  color: var(--color-text-2);
}
.devform-sp {
  flex: 1;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.devform-err {
  color: var(--color-danger-fg);
}
.ufield-warn {
  font: 400 12px var(--font-sans);
  color: var(--color-warn-fg);
}
.devform select[aria-invalid="true"] {
  border-color: var(--color-danger);
}
.devform-checks {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4) var(--sp-18);
}
.pghead + .err-summary,
.pghead + .banner {
  max-width: 820px;
  margin: 0 0 14px;
}

/* drift tab: the settings of the newest backup against the device, changed rows tinted */
.devdrift {
  display: flex;
  flex-direction: column;
  gap: var(--sp-12);
}
.devdrift > .banner,
.devdrift > .devnote {
  margin: 0;
}
.devdrift-lead {
  margin: 0;
  font-size: 13px;
  color: var(--color-text-2);
  text-wrap: pretty;
}
.devdrift-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-10);
}
.devdrift-bar b {
  font-weight: 600;
  font-size: 14px;
}
.devdrift-sp {
  flex: 1;
}
.devdrift-all {
  display: flex;
  align-items: center;
  gap: var(--sp-8);
  min-height: var(--ctl);
  font-size: 13px;
  color: var(--color-text);
  text-decoration: none;
}
.devdrift-box {
  width: 16px;
  height: 16px;
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: 3px;
  background: var(--color-surface);
}
.devdrift-all[aria-checked="true"] .devdrift-box {
  border-color: var(--color-accent);
  background: var(--color-accent);
  box-shadow: inset 0 0 0 3px var(--color-surface);
}
.devdrift-tbl {
  overflow: hidden;
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  background: var(--color-surface);
}
.devdrift-scroll {
  overflow-x: auto;
}
.devdrift-in {
  min-width: 560px;
}
.devdrift-row {
  display: grid;
  grid-template-columns: minmax(140px, 0.7fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--sp-12);
  min-height: 36px;
  padding: var(--sp-8) var(--sp-16);
  border-bottom: var(--hair-w) solid var(--color-border);
  font: 400 12.5px var(--font-mono);
  overflow-wrap: anywhere;
}
.devdrift-row:last-child {
  border-bottom: 0;
}
.devdrift-head {
  min-height: 0;
  background: var(--color-surface-2);
  font: 500 12px var(--font-sans);
  color: var(--color-text-2);
}
.devdrift-row.is-changed {
  background: var(--color-warn-bg);
}
.devdrift-k {
  font-weight: 500;
  color: var(--color-text);
}
.devdrift-a {
  color: var(--color-text-2);
}
.devdrift-row.is-changed .devdrift-b {
  color: var(--color-text);
}
.console-who {
  margin-left: var(--sp-10);
  color: var(--color-term-dim);
}
/* a device's reply is data, mostly one long JSON line without spaces: it keeps its line breaks and
   spacing but wraps inside the panel, breaking a token only where nothing else fits */
.console-line.is-reply .console-text {
  white-space: pre-wrap;
  word-break: normal;
  overflow-wrap: anywhere;
}
/* a device's form is long: its Save bar stays in view at the bottom of the window while the
   form scrolls (where the form ends, it sits at its place) */
.devform-foot {
  position: sticky;
  bottom: var(--sb-h);
  margin: 0 -18px -18px;
  padding: 14px 18px 18px;
  border-radius: 0 0 var(--r-card) var(--r-card);
  background: var(--color-surface);
}
/* a long device name in the trail (Devices / name / Edit) wraps inside the line instead of
   widening the page */
nav.crumbs a {
  min-width: 0;
  overflow-wrap: anywhere;
}
/* phones: the card links and the relay switches are 40 px touch targets */
@media (max-width: 44.99rem) {
  .devcard-head .btn.btn-link {
    min-height: 40px;
  }
  #identity .devcard-head {
    margin: -12px 0 -2px;
  }
  .devrelay-sw {
    height: 40px;
  }
}
/* the settings form's labels are <label for> elements beside the control: the field label's
   look, not the legacy form label's */
.devform label.ufield-label {
  font: 500 12.5px var(--font-sans);
  color: var(--color-text-2);
}

/* Card stacks (#391, #367): in every card grid the cards stack directly under each other per column,
   so a short card beside a tall one leaves no gap below it. ui.CardStacks holds ui.CardStack columns
   (1, 2 or all columns wide); from 75rem the stacks sit side by side, from 45rem two balanced
   columns flow through the cards in document order, below that one column. The document order is
   the phone reading order; .r1 to .r8 (ui.StackRank) place a card elsewhere on a phone. */
.devform.devform-cards {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--sp-16);
  max-width: none;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
}
.cstacks {
  display: grid;
  gap: var(--sp-16);
  align-items: start;
}
.cstacks-1 {
  grid-template-columns: minmax(0, 1fr);
}
.cstacks-2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.cstacks-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.cstack {
  display: flex;
  flex-direction: column;
  gap: var(--sp-16);
  min-width: 0;
}
.cstack > * {
  margin: 0;
}
.cstack-w2 {
  grid-column: span 2;
}
.cstack-all {
  grid-column: 1 / -1;
}
.gcard .gbody {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-14);
}
.gcard .ufield {
  min-width: 0;
}
.devform-cards .devform-foot {
  margin: 0;
  padding: 14px 18px;
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  background: var(--color-surface);
}
@media (max-width: 74.99rem) {
  .cstacks {
    display: block;
    columns: 2;
    column-gap: var(--sp-16);
  }
  .cstacks-1 {
    columns: auto;
  }
  .cstack {
    display: contents;
  }
  .cstack > * {
    break-inside: avoid;
    margin: 0 0 var(--sp-16);
  }
  .cstack-all > * {
    column-span: all;
  }
  /* the margin below the last card of a column run is dropped before a full-width card */
  .cstack-all:not(:first-child) > * {
    margin-top: var(--sp-16);
  }
}
/* The GPIO templates page refreshes itself and a read changes how tall a card is (pin table, Why lines,
   diffs). Between phone and desktop width the other pages let the browser flow the stacks into two
   columns by real height; here the two stacks stay two real columns, so no card changes sides when a
   verdict does (#549). Stacks per column, no row gaps. */
@media (min-width: 45rem) and (max-width: 74.99rem) {
  #gt-page .cstacks-2 {
    display: grid;
    columns: auto;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  #gt-page .cstack {
    display: flex;
    flex-direction: column;
    gap: var(--sp-16);
  }
  #gt-page .cstack > * {
    margin: 0;
  }
}
@media (max-width: 44.99rem) {
  .cstacks,
  .cstacks-1 {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    columns: auto;
    gap: var(--sp-16);
  }
  .cstack > *,
  .cstack-all:not(:first-child) > * {
    margin: 0;
  }
  .cstacks .r1 {
    order: 1;
  }
  .cstacks .r2 {
    order: 2;
  }
  .cstacks .r3 {
    order: 3;
  }
  .cstacks .r4 {
    order: 4;
  }
  .cstacks .r5 {
    order: 5;
  }
  .cstacks .r6 {
    order: 6;
  }
  .cstacks .r7 {
    order: 7;
  }
  .cstacks .r8 {
    order: 8;
  }
}

/* ==== page: device delete (/devices/{id}/delete, N2, WP W4): the prototype's "devdelete" ==== */
.dd-page {
  max-width: 760px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.dd-head h1 {
  margin: 0;
  font: 600 22px/1.2 var(--font-sans);
  letter-spacing: -0.015em;
  overflow-wrap: anywhere;
}
.dd-sub {
  margin: 4px 0 0;
  color: var(--color-text-2);
  font-size: 13px;
  font-family: var(--font-mono);
}
.dd-page .banner {
  margin: 0;
  padding: 10px 12px;
  gap: 10px;
  align-items: flex-start;
  border-radius: 8px;
}
.dd-page .banner-dot {
  margin-top: 5px;
}
.dd-list {
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  overflow: hidden;
}
.dd-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 14px;
  border-bottom: var(--hair-w) solid var(--color-border);
  font-size: 13px;
}
.dd-when {
  font-weight: 500;
}
.dd-what {
  color: var(--color-text-2);
}
.dd-form {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.dd-choice {
  margin: 0;
  padding: 0;
  border: 0;
  min-width: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
  gap: 10px;
}
.dd-choice .rcard {
  padding: 12px;
  border-radius: 9px;
}
.dd-choice .rcard input {
  flex: none;
  margin: 2px 0 0;
}
.dd-choice .rcard-body span {
  margin-top: 2px;
}
.dd-note {
  margin: 0;
  font-size: 13px;
  color: var(--color-text-2);
}
.dd-ack {
  min-height: 40px;
  cursor: pointer;
}
.dd-ack input {
  margin: 3px 3px 3px 4px;
}
.dd-acts {
  display: flex;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 8px;
}
/* like the prototype, the button looks unavailable until the box is ticked; it still posts, and
   the server answers with the field error (works without JavaScript) */
.dd-form:has(input[name="ack"]:not(:checked)) .dd-go {
  opacity: 0.45;
}

/* ==== page: Updates (WP P4): /transactions, /transactions/{batch}, /updates/new and its preview ====
   Markup: transactions.templ. The prototype's "updates", "txn", "compliant", "newupdate" screens. */

/* transactions list: the running transactions as cards, then the history */
.upd-run .txn-card {
  gap: 10px;
  margin-bottom: 14px;
  padding: 16px;
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  background: var(--color-surface);
}
.upd-run .txn-card:hover {
  border-color: var(--color-border-strong);
  background: var(--color-surface);
}
.upd-run .txn-title {
  font-size: 15px;
}
.upd-run .seg-bar {
  height: 8px;
  border-radius: 4px;
}
.upd-run .txn-sum {
  font-size: 13px;
}
.upd .ulist {
  overflow: hidden;
}
.upd .ulist-head,
.upd-pick .ulist-head,
.upd-prev .ulist-head {
  padding: 12px 16px;
}
.upd-hrow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 4px 12px;
  align-items: center;
  min-height: 48px;
  padding: 10px 16px;
  border-bottom: var(--hair-w) solid var(--color-border);
  color: var(--color-text);
  text-decoration: none;
}
.upd-hrow:hover {
  background: var(--color-surface-2);
  color: var(--color-text);
}
.upd-hhead {
  display: none;
  gap: 4px 12px;
  align-items: center;
  min-height: 36px;
  padding: 0 16px;
  border-bottom: var(--hair-w) solid var(--color-border);
  background: var(--color-surface-2);
  font: 500 12px var(--font-sans);
  color: var(--color-text-2);
}
@media (min-width: 45rem) {
  /* fixed columns with a header row (on a phone each row is its own card) */
  .upd-hrow,
  .upd-hhead {
    grid-template-columns: 96px minmax(0, 2fr) 110px 130px minmax(0, 1.5fr);
  }
  .upd-hhead {
    display: grid;
  }
}
.upd-hid {
  font: 500 12.5px var(--font-mono);
}
.upd-htitle {
  font-weight: 500;
}
.upd-hn {
  font-size: 12.5px;
  color: var(--color-text-2);
}
.upd-hwho {
  font-size: 12.5px;
  color: var(--color-text-3);
}
/* phone: id and result on the first line, the title under them, then devices and who: one reading
   order instead of two columns that zigzag */
@media (max-width: 44.99rem) {
  .upd-hrow {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "id st"
      "title title"
      "n who";
  }
  .upd-hid {
    grid-area: id;
  }
  .upd-hst {
    grid-area: st;
  }
  .upd-htitle {
    grid-area: title;
  }
  .upd-hn {
    grid-area: n;
  }
  .upd-hwho {
    grid-area: who;
    text-align: right;
  }
}

/* one transaction: header with the state, progress, device rows and the log of one device */
.upd-head {
  align-items: flex-start;
  margin-bottom: 14px;
}
.upd-h1 {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.bdg.bdg-lg {
  height: 22px;
  padding: 0 8px;
  font-size: 12px;
}
.upd-head .mono {
  font-family: var(--font-mono);
}
.upd-links a {
  color: var(--color-accent-fg);
}
.ucard.upd-prog {
  display: block;
  margin-bottom: 14px;
  padding: 14px 16px;
}
.upd-prog .seg-bar {
  height: 10px;
  border-radius: 5px;
}
.upd-counts {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
  margin-top: 10px;
  font-size: 13px;
  color: var(--color-text-2);
}
.upd-counts b {
  font-weight: 600;
  color: var(--color-text);
}
.upd-counts b.upd-bad,
.upd-bad {
  color: var(--color-danger-fg);
}
.upd-warn {
  color: var(--color-warn-fg);
}
.upd-of {
  color: var(--color-text-3);
}
.upd-cols {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: flex-start;
}
.upd-rows {
  flex: 2 1 460px;
  max-width: 100%;
  overflow: hidden;
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  background: var(--color-surface);
}
.upd-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 12px;
  min-height: 48px;
  padding: 10px 14px;
  border-bottom: var(--hair-w) solid var(--color-border);
  box-shadow: inset 2px 0 0 transparent;
  background: var(--color-surface);
  color: var(--color-text);
  text-decoration: none;
}
.upd-row:hover {
  color: var(--color-text);
}
.upd-row[aria-current="true"] {
  box-shadow: inset 2px 0 0 var(--color-accent);
  background: var(--color-surface-2);
}
.upd-row-name {
  flex: 1 1 200px;
  min-width: 0;
  font-size: 13px;
  font-weight: 500;
  overflow-wrap: anywhere;
}
.upd-row-ver {
  font: 400 12px var(--font-mono);
  color: var(--color-text-2);
}
.upd-row-state {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 170px;
}
.upd-row-state .mini-bar {
  flex: 1;
  min-width: 0;
}
.upd-row-state .mini-bar svg {
  flex: 1;
  width: auto;
  min-width: 0;
  height: 5px;
  border-radius: 3px;
}
.upd-row-state .mini-pct {
  font: 400 11.5px var(--font-mono);
  color: var(--color-text-3);
}
.upd-log {
  position: sticky;
  top: 110px;
  flex: 1 1 320px;
  max-width: 100%;
  overflow: hidden;
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  background: var(--color-term-bg);
  color: var(--color-term-fg);
}
.upd-log-head {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 9px 12px;
  border-bottom: var(--hair-w) solid var(--color-console-rule);
  font: 500 12px var(--font-sans);
  color: var(--color-term-dim);
  overflow-wrap: anywhere;
}
.upd-log-acts {
  display: flex;
  gap: 10px;
  flex: none;
}
.upd-log-acts a,
.upd-log-acts button {
  display: inline-flex;
  align-items: center;
  height: auto;
  min-height: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--color-term-cmd);
  cursor: pointer;
  text-decoration: none;
}
.upd-log-body {
  max-height: 420px;
  overflow: auto;
  padding: 10px 12px;
  font: 400 12px/1.65 var(--font-mono);
}
.upd-log-line {
  display: flex;
  gap: 10px;
}
.upd-log-at {
  flex: none;
  color: var(--color-term-dim);
}
.upd-log-t {
  min-width: 0;
  overflow-wrap: anywhere;
}
.upd-log-err .upd-log-t {
  color: var(--color-term-err);
}

/* "Make compliant": one card per device with its changes */
.upd-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 380px), 1fr));
  gap: 10px;
  align-items: start;
}
.upd-dev {
  padding: 12px 14px;
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  background: var(--color-surface);
}
.upd-dev-head {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-bottom: 6px;
  font-size: 13.5px;
  font-weight: 600;
  overflow-wrap: anywhere;
}
.upd-dev-head a {
  color: var(--color-text);
  text-decoration: none;
}
.upd-dev-head a:hover {
  text-decoration: underline;
}
.upd-dot {
  flex: none;
  width: 8px;
  height: 8px;
  margin-top: 6px;
  border-radius: 50%;
  background: var(--t);
}
.upd-change {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 10px;
  padding: 7px 0;
  border-top: var(--hair-w) solid var(--color-border);
}
.upd-change-t {
  flex: 1 1 180px;
  font: 400 12.5px var(--font-mono);
  overflow-wrap: anywhere;
}
.upd-why {
  margin: 0;
  font-size: 12.5px;
  color: var(--color-danger-fg);
}

/* update preview (step 4): the devices beside the sticky "What happens" */
.upd-split {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: flex-start;
}
.upd-pick,
.upd-prev {
  overflow: hidden;
}
.upd-pick .ulist-head,
.upd-prev .ulist-head {
  flex-wrap: wrap;
  gap: 8px;
}
.upd-head-r {
  display: flex;
  align-items: center;
  gap: 10px;
}
.upd-n {
  font-size: 12.5px;
  color: var(--color-text-2);
}
.upd-quick {
  position: relative;
}
.upd-quick > summary {
  height: 22px;
  list-style: none;
}
.upd-quick > summary::-webkit-details-marker {
  display: none;
}
/* the quick selection floats over the list like a row menu: it is not a .rowmenu (its button is a
   text link, not the "⋯" icon), so it positions its panel itself; in the flow it grew the head to
   the menu's height  */
.upd-quick .menu-panel {
  position: absolute;
  z-index: var(--z-menu);
  top: calc(100% + 4px);
  right: 0;
  left: auto;
}
/* the card clips its rounded corners, which would cut off a menu taller than a short list */
.upd-pick:has(.upd-quick[open]) {
  overflow: visible;
}
.upd-pick-list {
  max-height: 340px;
  overflow: auto;
}
.upd-pick-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 6px 16px;
  border-bottom: var(--hair-w) solid var(--color-border);
  cursor: pointer;
}
.upd-pick-row input {
  flex: none;
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--color-accent);
}
.upd-pick-row .upd-dot {
  width: 7px;
  height: 7px;
  margin-top: 0;
}
.upd-pick-name {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  overflow-wrap: anywhere;
}
/* below 75rem (phone, and the half-width card of two balanced stacks): the name gets a line of its
   own width and the chip and version drop under it when they do not fit beside it, instead of
   squeezing a normal name into a 4 rem column */
@media (max-width: 74.99rem) {
  .upd-pick-row {
    flex-wrap: wrap;
  }
  .upd-pick-name {
    flex: 1 1 12rem;
  }
  .upd-pick-fw {
    margin-left: auto;
  }
}
.upd-pick-why {
  font-size: 12px;
  color: var(--color-text-3);
}
.upd-pick-fw {
  font: 400 12px var(--font-mono);
  color: var(--color-text-2);
  white-space: nowrap;
}
.upd-none {
  margin: 0;
  padding: 12px 16px;
  color: var(--color-text-2);
}
.ucard.upd-rel {
  gap: 8px;
  padding: 14px 16px;
}
.ucard.upd-rel > h2 {
  margin: 0 0 4px;
}
.upd-relopt {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 12px;
  border: var(--hair-w) solid var(--color-border);
  border-radius: 8px;
  background: var(--color-surface);
  cursor: pointer;
}
.upd-relopt:has(input:checked) {
  border-color: var(--color-accent);
  background: var(--color-accent-bg);
}
.upd-relopt:has(input:focus-visible) {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
.upd-relopt input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.upd-relopt-t {
  font-weight: 600;
}
.upd-relopt-d {
  font-size: 12px;
  color: var(--color-text-2);
}
.upd-older > .upd-relopt {
  margin-top: 8px;
}
.ucard.upd-opts {
  gap: 0;
  padding: 14px 16px;
}
.ucard.upd-opts > h2 {
  margin: 0 0 6px;
}
.upd-opt {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  min-height: 40px;
  font-size: 13px;
  cursor: pointer;
}
.upd-opt input[type="checkbox"] {
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--color-accent);
}
.upd-opt select,
.upd-opt input:not([type="checkbox"]) {
  height: var(--ctl);
  padding: 0 8px;
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: var(--r-control);
  background: var(--color-surface);
  color: var(--color-text);
  font: 400 13px var(--font-sans);
}
.ucard.upd-aside {
  gap: 10px;
}
.upd-split .ucard.upd-aside {
  position: sticky;
  top: 110px;
  max-width: 100%;
}
.ucard.upd-aside > h2 {
  margin: 0;
}
.upd-facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 6px 14px;
  margin: 0;
  font-size: 13px;
}
.upd-facts dt {
  color: var(--color-text-2);
}
.upd-facts dd {
  margin: 0;
  font-weight: 600;
}
.upd-facts.upd-facts-wide {
  grid-template-columns: auto minmax(0, 1fr);
  gap: 7px 14px;
}
.upd-facts-wide dd {
  font-weight: 400;
  overflow-wrap: anywhere;
}
.upd-facts-wide dd b {
  font-weight: 600;
}
.upd-facts-wide dd.mono {
  font: 400 12px var(--font-mono);
}
/* the preview's head keeps its title at the top beside "Change" (the prototype's stretch) */
.upd-prev > .ulist-head {
  align-items: flex-start;
}
/* the address the devices fetch from, as the prototype writes it */
.upd-url {
  font: 400 12px var(--font-mono);
  overflow-wrap: anywhere;
}
.upd-steps {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin: 4px 0 0;
  padding-left: 18px;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.upd-hint {
  margin: 0;
  font-size: 12.5px;
  color: var(--color-warn-fg);
}
.upd-aside .btn-tall {
  height: calc(var(--ctl) + 4px);
}
.upd-prev {
  flex: 3 1 520px;
  max-width: 100%;
}
.upd-prev .ulist-head .btn-link {
  height: 28px;
  padding: 0 10px;
}
.upd-prev-list {
  max-height: 420px;
  overflow: auto;
}
.upd-prow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 14px;
  min-height: 48px;
  padding: 8px 16px;
  border-bottom: var(--hair-w) solid var(--color-border);
}
.upd-prow-name {
  flex: 1 1 220px;
  min-width: 0;
  font-size: 13px;
  font-weight: 500;
  overflow-wrap: anywhere;
}
.upd-prow-ver {
  font: 400 12.5px var(--font-mono);
  white-space: nowrap;
}
.upd-prow-ver b {
  font-weight: 600;
}
.upd-from {
  color: var(--color-text-2);
}
.upd-prow-file {
  width: 190px;
  overflow: hidden;
  font: 400 12px var(--font-mono);
  color: var(--color-text-2);
  white-space: nowrap;
  text-overflow: ellipsis;
}
.upd-prow-why,
.upd-prow-note {
  flex: 1 1 100%;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.upd-prow-mem {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 12px;
}
.upd-prow-mem .btn {
  padding: 0;
}
.upd-prow-build {
  flex: 1 1 100%;
  margin-top: 4px;
}
.upd-prow-build .rcard input {
  flex: none;
}
.upd-prev-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  padding: 12px 16px;
}
.upd-prev-foot .upd-hint {
  flex: 1 1 260px;
  margin: 0;
}
.ucard.upd-what {
  flex: 2 1 300px;
  gap: 12px;
}
.upd-go {
  display: flex;
  gap: 8px;
  margin-top: 4px;
}
.upd-go .btn-primary {
  flex: 1;
}
/* update preview: the devices and the "5 · When" card in one column, "What happens" beside it (#604) */
.upd-main {
  flex: 3 1 520px;
  min-width: 0;
  max-width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--sp-14);
}
.upd-main .upd-prev {
  flex: none;
  width: 100%;
}
.upd-when .rcard-body span {
  margin-top: var(--sp-2);
}
.upd-when-at {
  display: flex;
  flex-direction: column;
  gap: var(--sp-12);
  padding: var(--sp-12) var(--sp-14);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-banner);
  background: var(--color-surface-2);
}
/* "Start now" hides the choices; without JavaScript this is all that reacts to the radio */
.upd-when:not(:has(#when-at:checked)) .upd-when-at {
  display: none;
}
.upd-picks {
  margin: 0;
  padding: 0;
  border: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-8);
}
.upd-picks legend {
  margin: 0 0 var(--sp-6);
  padding: 0;
  font: 500 12.5px var(--font-sans);
  color: var(--color-text-2);
}
.upd-picks label.chip {
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  padding: var(--sp-6) var(--sp-12);
  min-height: 44px;
  justify-content: center;
}
.upd-picks label.chip > input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.upd-picks .chip-main {
  font-size: 13.5px;
  font-weight: 500;
}
.upd-picks .chip-sub {
  font-size: 12px;
  font-weight: 400;
  color: var(--color-text-2);
}
.upd-picks label.chip:has(input:checked) .chip-sub {
  color: var(--color-accent-fg);
}
.upd-custom {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-10) var(--sp-14);
  align-items: flex-end;
}
.upd-custom label {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  font-size: 12.5px;
  color: var(--color-text-2);
}
.upd-custom input {
  min-width: 150px;
}
.upd-custom input[aria-invalid="true"] {
  border-color: var(--color-danger);
}
.upd-tz {
  margin: 0;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.upd-err {
  margin: 0;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--color-danger-fg);
}
.upd-warn-text {
  color: var(--color-warn-fg);
}
/* what happens when TasmoFleet is down at the start time, in the plan */
.upd-rule {
  margin: 0;
  padding: var(--sp-10) var(--sp-12);
  border-radius: var(--r-banner);
  background: var(--color-surface-2);
  border: var(--hair-w) solid var(--color-border);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--color-text);
}
.upd-rule b {
  font-weight: 600;
}
/* the cancel dialog's scrim covers the scrollbar gutter too, like the drawer's (F3) */
html:has(dialog#dlg-txn:modal) {
  background: color-mix(in srgb, var(--color-bg) 50%, rgb(10, 14, 18));
}
html:has(dialog#dlg-txn:modal) body {
  min-height: 100vh;
}
/* the page's labels are rows, not the form's stacked, bold labels */
.upd-pick-row,
.upd-opt,
.upd-relopt {
  font-weight: 400;
}
.upd-opt select {
  width: auto;
  padding: 0 30px 0 8px;
}
.upd-opts
  .upd-opt
  input:not([type="checkbox"]):not([type="radio"]):not([type="file"]) {
  width: auto;
  min-width: 220px;
  padding: 0 8px;
}
.upd-opts .upd-opt select,
.upd-opts
  .upd-opt
  input:not([type="checkbox"]):not([type="radio"]):not([type="file"]) {
  width: 100%;
  min-width: 0;
}
/* Options in the wide stack (#419): the three fields in two columns, the checkbox row across both,
   each label above its control */
.cstack .upd-opts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--sp-24);
  row-gap: var(--sp-10);
  align-content: start;
}
.cstack .upd-opts > h2,
.cstack .upd-opts > .upd-opt:first-of-type {
  grid-column: 1 / -1;
}
.cstack .upd-opts .upd-opt:has(select, input:not([type="checkbox"])) {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-4);
}
/* below 75rem the stacks are one or two balanced columns, so the card is half the page or less */
@media (max-width: 74.99rem) {
  .cstack .upd-opts {
    grid-template-columns: minmax(0, 1fr);
  }
}
/* Devices in the wide stack: two columns of rows, the names are short; only from 75rem, where the
   wide stack spans two columns */
@media (min-width: 75rem) {
  .cstack .upd-pick-list {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .cstack .upd-pick-list .upd-pick-row:nth-child(odd) {
    border-right: var(--hair-w) solid var(--color-border);
  }
}
@media (max-width: 44.99rem) {
  .upd-opts .upd-opt:has(select, input:not([type="checkbox"])) {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-4, 4px);
    padding-block: var(--sp-6, 6px);
  }
}
.upd-quick > summary.btn {
  height: 18px;
  padding: 0;
}
.upd-older > summary.btn {
  justify-content: flex-start;
  width: max-content;
  height: 24px;
  padding: 0;
}
.upd-prev .ulist-head .btn.btn-link,
.upd-quick > summary.btn,
.upd-older > summary.btn {
  min-height: 0;
}
@media (max-width: 44.99rem) {
  /* a 40 px touch target without moving the layout */
  .upd-quick > summary.btn,
  .upd-older > summary.btn {
    min-height: 40px;
    margin: -11px 0;
  }
  .upd-quick > summary.btn {
    padding: 0 4px;
    margin: -11px -4px;
  }
  .upd-older > summary.btn {
    margin: -8px 0;
  }
  .upd-log-acts a,
  .upd-log-acts button {
    min-height: 40px;
    margin: -12px 0;
  }
  /* an option with a control: its name above the control, the control as wide as the card */
  .upd-opt:not(:has(> input[type="checkbox"])) {
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    padding: 4px 0;
  }
  .upd-opt select,
  .upd-opts
    .upd-opt
    input:not([type="checkbox"]):not([type="radio"]):not([type="file"]) {
    width: 100%;
    min-width: 0;
  }
}

/* ==== page: Firmware (WP P5), values from the prototype's firmware screen ==== */
.fw-check {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12.5px;
  color: var(--color-text-2);
  white-space: nowrap;
  align-self: center;
}
.fw-check.t-danger {
  color: var(--color-danger-fg);
}
.fw-check-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--t);
  flex: none;
}
.fw-check-dot.pulse {
  animation: tf-pulse 0.9s ease-in-out infinite;
}
.fw-check-form {
  display: contents;
}
.fw-check-form .btn:disabled {
  opacity: 0.6;
}
.fw-down {
  margin: 0;
}
.fw-rels {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: var(--sp-10) var(--sp-12) var(--sp-12);
}
.fw-rel {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border-radius: var(--r-card);
  border: var(--hair-w) solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text);
  text-decoration: none;
}
.fw-rel:hover {
  color: var(--color-text);
  border-color: var(--color-border-strong);
}
.fw-rel[aria-current="true"] {
  border-color: var(--color-accent);
  background: var(--color-accent-bg);
}
.fw-rel-t {
  flex: 1;
  min-width: 0;
}
.fw-rel-h {
  display: flex;
  align-items: center;
  gap: 8px;
}
.fw-rel-v {
  font: 600 14px var(--font-mono);
}
.fw-rel-d {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  color: var(--color-text-2);
}
.fw-rel-n {
  font-size: 12px;
  color: var(--color-text-3);
  white-space: nowrap;
}
.fw-older-tg {
  display: flex;
  align-items: center;
  height: var(--ctl);
  padding: 0 4px;
  color: var(--color-accent-fg);
  font: 500 13px var(--font-sans);
  text-decoration: none;
}
.fw-older {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.fw-chip {
  padding: 5px 8px;
  border-radius: 6px;
  background: var(--color-surface-2);
  color: var(--color-text-2);
  font: 400 12px var(--font-mono);
  text-decoration: none;
}
.fw-chip:hover {
  color: var(--color-text);
}
.fw-chip[aria-current="true"] {
  background: var(--color-accent-bg);
  color: var(--color-accent-fg);
}
.fw-norel {
  padding: 12px 14px;
  border-radius: var(--r-card);
  border: var(--hair-w) dashed var(--color-border-strong);
  color: var(--color-text-2);
  font-size: 13px;
}
.fw-norel p {
  margin: 0;
}
.fw-sel {
  display: block;
}
.fw-sel-head {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  align-items: flex-start;
}
.fw-sel h2 {
  margin: 0;
  font: 600 16px var(--font-sans);
  letter-spacing: normal;
}
.fw-sel-sub {
  margin-top: 2px;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.fw-excerpt {
  margin: 12px 0 0;
  padding-left: 18px;
  font-size: 13px;
  color: var(--color-text);
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.fw-excerpt li {
  margin: 0;
}
.fw-files-h {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin: 16px 0 6px;
}
.fw-files-h h3 {
  margin: 0;
  font: 600 13px var(--font-sans);
  letter-spacing: normal;
}
.fw-files-h form {
  margin: 0;
}
.fw-files-h .btn-link {
  min-height: 0;
  font-size: 12.5px;
}
.fw-file {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 8px 0;
  border-top: var(--hair-w) solid var(--color-border);
}
.fw-file-n {
  flex: 1 1 160px;
  font: 500 12.5px var(--font-mono);
  overflow-wrap: anywhere;
}
.fw-file-c,
.fw-file-d {
  width: 80px;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.fw-file-s {
  width: 60px;
  font: 400 12px var(--font-mono);
  color: var(--color-text-3);
  white-space: nowrap;
}
.fw-file .bdg {
  height: 22px;
  padding: 0 8px;
}
.fw-file-fail {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.fw-file-fail .bdg {
  height: 20px;
  padding: 0 7px;
}
.fw-file-nc {
  font-size: 12.5px;
  color: var(--color-text-3);
}
.fw-fetch {
  margin: 0;
}
.fw-fetch .btn-sm {
  height: 30px;
  padding: 0 10px;
  font-size: 12.5px;
}
.fw-file-fail .fw-fetch .btn-sm {
  padding: 0 12px;
  font-size: 13px;
}
.fw-fetching {
  text-decoration: none;
}
.fw-note {
  margin: 8px 0 0;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.fw-fullnotes,
.fw-dlg-body {
  font-size: 13px;
}
.fw-fullnotes h3,
.fw-dlg-body h3 {
  margin: 14px 0 6px;
  font: 600 13px var(--font-sans);
}
.fw-fleet {
  margin: 0;
  padding-left: 18px;
}
.fw-dlg-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
dialog.fw-notes-dlg {
  width: min(720px, calc(100% - 32px));
}
/* the title and Close stay put and only the notes scroll; the shadows at the top and bottom edge
   of the notes show that there is more */
dialog.fw-notes-dlg[open] {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
dialog.fw-notes-dlg .udlg-in {
  flex: 1;
  min-height: 0;
}
dialog.fw-notes-dlg .fw-dlg-body {
  flex: 1;
  min-height: 0;
  overflow: auto;
  padding-bottom: var(--sp-8);
  background:
    linear-gradient(var(--color-surface) 30%, transparent) top / 100% 24px
      no-repeat local,
    linear-gradient(transparent, var(--color-surface) 70%) bottom / 100% 24px
      no-repeat local,
    linear-gradient(var(--color-border), transparent) top / 100% 8px no-repeat
      scroll,
    linear-gradient(transparent, var(--color-border)) bottom / 100% 8px
      no-repeat scroll;
}
.fw-cache {
  overflow: hidden;
}
.fw-cache .ulist-head {
  padding: 12px 16px;
}
.fw-cache-total {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-size: 12px;
  color: var(--color-text-2);
}
.fw-unused {
  margin: 0;
}
.fw-unused .btn-link {
  min-height: 0;
  font-size: 12px;
  color: var(--color-danger-fg);
}
.fw-crow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 2px var(--sp-12);
  align-items: baseline;
  padding: var(--sp-10) 16px;
  border-bottom: var(--hair-w) solid var(--color-border);
  font-size: 12.5px;
}
.fw-c-f {
  grid-column: 1;
  font: 500 12.5px var(--font-mono);
  overflow-wrap: anywhere;
}
.fw-c-v time {
  white-space: nowrap;
}
.fw-c-v {
  grid-column: 1;
  font-family: var(--font-mono);
  color: var(--color-text-2);
}
.fw-c-h {
  grid-column: 2;
  grid-row: 2;
  justify-self: end;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--color-text-3);
}
.fw-c-a {
  grid-column: 2;
  grid-row: 1;
  justify-self: end;
  display: flex;
  gap: 6px;
  align-items: center;
  color: var(--color-text-3);
}
.fw-del,
.fw-needed {
  display: inline-flex;
  align-items: center;
  height: 32px;
  padding: 0 10px;
  color: var(--color-danger-fg);
  font: 500 12.5px var(--font-sans);
  text-decoration: none;
}
.fw-needed {
  color: var(--color-text-2);
}
.fw-cache-empty {
  margin: 0;
  padding: 12px 16px;
  font-size: 13px;
  color: var(--color-text-2);
  border-bottom: var(--hair-w) solid var(--color-border);
}
.fw-upcard .fw-drop {
  margin: var(--sp-12) var(--sp-16) var(--sp-16);
}
.fw-drop {
  margin: 12px 16px 16px;
  padding: 18px;
  border: 1.5px dashed var(--color-border-strong);
  border-radius: 9px;
  text-align: center;
  color: var(--color-text-2);
  font-size: 13px;
}
.fw-drop .mono {
  font-family: var(--font-mono);
}
.fw-drop a {
  color: var(--color-accent-fg);
}
.fw-drop.is-over {
  border-color: var(--color-accent);
  background: var(--color-accent-bg);
}
.fw-sum {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.fw-sum code {
  font: inherit;
  padding: 0;
  background: none;
}
.fw-sum .btn.btn-sm {
  height: 20px;
  padding: 0 7px;
  font-size: 11.5px;
}
.fw-upload {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.fw-upload-card .fw-upload {
  margin-top: 4px;
}
.fw-prefetch progress {
  width: 100%;
}
@media (prefers-reduced-motion: reduce) {
  .fw-check-dot.pulse {
    animation: none;
  }
}
/* phone: Delete and the bulk delete are 40 px touch targets like every control (QA #13) */
@media (max-width: 44.99rem) {
  .fw-del,
  .fw-needed {
    height: 40px;
  }
  .fw-unused .btn-link {
    min-height: 40px;
  }
  .fw-chip {
    display: inline-flex;
    align-items: center;
    min-height: 40px;
    padding: 0 12px;
  }
  .seg button {
    height: 40px;
  }
}
/* ==========================================================================
   Page: Add devices (design v2 screen "add", package P6): the header, the
   four tabs' bodies (internal/web/adddevices.templ). Values from the
   prototype's markup (design/v2/template.html, screen add).
   ========================================================================== */
.add-head {
  margin-bottom: var(--sp-14);
}
.add-head h1 {
  margin: 0;
  font: 600 22px/1.2 var(--font-sans);
  letter-spacing: -0.015em;
}
.add-gap {
  flex: 1;
}
.add-t2 {
  color: var(--color-text-2);
}

/* inline notice of a tab: prefill, test result, scan finished, discovery off */
.add-note {
  display: flex;
  gap: var(--sp-10);
  align-items: center;
  flex-wrap: wrap;
  padding: var(--sp-10) var(--sp-12);
  margin-bottom: var(--sp-12);
  border-radius: 8px;
  border: var(--hair-w) solid var(--note-line);
  background: var(--note-bg);
  color: var(--note-fg);
  font-size: 13px;
}
.add-note.tone-ok {
  --note-line: var(--color-ok);
  --note-bg: var(--color-ok-bg);
  --note-fg: var(--color-ok-fg);
}
.add-note.tone-warn {
  --note-line: var(--color-warn);
  --note-bg: var(--color-warn-bg);
  --note-fg: var(--color-warn-fg);
}
.add-note.tone-accent {
  --note-line: var(--color-accent);
  --note-bg: var(--color-accent-bg);
  --note-fg: var(--color-accent-fg);
}
.add-note-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
  background: var(--note-line, var(--color-accent));
}
.add-note-dot.pulse {
  animation: tf-pulse 0.9s ease-in-out infinite;
}
.add-note-text {
  flex: 1;
  min-width: 200px;
}
.add-note-text b {
  font-weight: 600;
}
.add-note .btn {
  white-space: nowrap;
}

/* #410: the Add page's boxes hold the add-* blocks; their own frame goes, the card is the frame */
.scard-add .add-manual {
  max-width: none;
  padding: 0;
  background: none;
  border: 0;
}
.scard-add .add-bar h3 {
  margin: 0;
}

/* Add manually */
.add-manual {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  gap: var(--sp-14) var(--sp-18);
  max-width: 720px;
  padding: var(--sp-18);
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
}
.add-manual > .add-note,
.add-manual > .ucheck,
.add-manual > .add-off {
  grid-column: 1 / -1;
  margin: 0;
}
.add-manual .add-note .btn {
  height: 30px;
}
.add-manual input[readonly] {
  color: var(--color-text);
}
.add-more {
  grid-column: 1 / -1;
}
.add-more > summary {
  width: fit-content;
  font: 500 13px var(--font-sans);
  color: var(--color-text-2);
  cursor: pointer;
}
@media (max-width: 44.99rem) {
  .add-more > summary {
    padding: 12px 0; /* a 40 px touch target without moving the layout */
    margin: -12px 0;
  }
}
.add-more-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  gap: var(--sp-14) var(--sp-18);
  margin-top: var(--sp-12);
}
.add-off {
  font-size: 12.5px;
  color: var(--color-text-2);
}
/* "Add device" comes first in the markup, so Enter in a field adds; the row shows it last */
.add-acts {
  grid-column: 1 / -1;
  display: flex;
  flex-direction: row-reverse;
  gap: var(--sp-8);
  padding-top: var(--sp-14);
  border-top: var(--hair-w) solid var(--color-border);
}

/* Discovered: address changes (and conflicts) */
.add-moves {
  margin-bottom: var(--sp-14);
  overflow: hidden;
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
}
.add-moves-head {
  padding: var(--sp-12) var(--sp-16);
  border-bottom: var(--hair-w) solid var(--color-border);
}
.add-moves-head h2,
.add-bar h2,
.add-plan-head h2 {
  margin: 0;
  font: 600 14px var(--font-sans);
  letter-spacing: normal;
}
.add-moves-sub {
  margin-top: 2px;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.add-move {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-6) var(--sp-16);
  padding: var(--sp-10) var(--sp-12) var(--sp-10) var(--sp-16);
  border-bottom: var(--hair-w) solid var(--color-border);
}
.add-move-name {
  flex: 2 1 220px;
  min-width: 0;
}
.add-move-name a,
.add-move-name > span:first-child {
  display: block;
  font-weight: 500;
  color: var(--color-text);
  text-decoration: none;
  overflow-wrap: anywhere;
}
.add-move-name a:hover {
  text-decoration: underline;
}
.add-move-seen {
  display: block;
  font-size: 12px;
  color: var(--color-text-2);
}
.add-move-addr {
  flex: 1 1 220px;
  font-size: 12.5px;
  white-space: nowrap;
}
.add-move-addr b {
  font-weight: 600;
}
.add-move-why {
  flex: 2 1 240px;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.add-move-acts {
  gap: var(--sp-6);
}

/* Discovered: the New devices bar, "Show ignored" (a link drawn as a checkbox) */
.add-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-8);
  flex-wrap: wrap;
  margin-bottom: var(--sp-10);
}
.add-bar h2 {
  margin-right: var(--sp-8);
}
.add-showign {
  display: flex;
  align-items: center;
  gap: var(--sp-8);
  min-height: var(--ctl);
  font-size: 13px;
  color: var(--color-text);
  text-decoration: none;
}
.add-box {
  width: 16px;
  height: 16px;
  flex: none;
  border: var(--hair-w) solid var(--color-text-2);
  border-radius: 3px;
  background: var(--color-surface);
}
.add-showign[aria-checked="true"] .add-box {
  border-color: var(--color-accent);
  background: var(--color-accent)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 8.5l2.5 2.5L12 5.5' fill='none' stroke='white' stroke-width='2'/%3E%3C/svg%3E")
    center / 14px no-repeat;
}

/* the tables of Discovered and Network scan: fixed columns, the actions stay at the right edge */
.add-table {
  overflow-x: auto;
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
}
.add-table table {
  table-layout: fixed;
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 13px;
}
.add-disc-table table {
  min-width: 700px; /* #410: the Discovered box is two thirds wide; below this the table scrolls */
}
.add-scan-table table {
  min-width: 880px;
}
.add-table thead th {
  height: 38px;
  padding: 0 10px;
  background: var(--color-surface-2);
  border-bottom: var(--hair-w) solid var(--color-border);
  text-align: left;
  font: 500 12px var(--font-sans);
  letter-spacing: normal;
  text-transform: none;
  color: var(--color-text-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: middle;
}
.add-table td {
  height: 52px;
  padding: 0 10px;
  border-bottom: var(--hair-w) solid var(--color-border);
  background: var(--color-surface);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.add-table th.sel,
.add-table td.sel {
  width: auto;
  padding: 0 10px 0 14px;
}
.add-table input[type="checkbox"] {
  width: 15px;
  height: 15px;
  accent-color: var(--color-accent);
}
.add-table tbody tr:hover td {
  background: var(--color-surface);
}
.add-table tbody tr:has(> td.sel input:checked) td {
  background: var(--color-accent-bg);
}
.add-table tbody tr:has(> td.sel input:checked) td:first-child {
  box-shadow: none;
}
.add-table tr.is-ign td {
  background: var(--color-surface-2);
  color: var(--color-text-2);
}
.add-table td.mono {
  font-size: 12.5px;
}
.add-table td.add-t2 {
  font-size: 12.5px;
}
.add-table td.mono.add-t2 {
  color: var(--color-text-2);
}
.add-label {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  font-weight: 500;
}
.add-label-t {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.add-label .badge {
  flex: none;
}
.add-table th.act,
.add-table td.act {
  position: sticky;
  right: 0;
  text-align: right;
}
.add-disc-table th.act,
.add-disc-table td.act {
  border-left: var(--hair-w) solid var(--color-border);
}
.add-table td a.btn {
  font-weight: 500;
  text-decoration: none;
}
.add-table td a.btn-primary {
  font-weight: 600;
  color: var(--color-on-accent);
}
.add-table td .btn-link {
  height: var(--ctl);
  padding: 0 10px;
}
.btn.add-ign {
  padding: 0 10px;
  color: var(--color-text-2);
}
.add-disc-table col.c-sel,
.add-scan-table col.c-sel {
  width: 44px;
}
.add-disc-table col.c-name {
  width: auto;
}
.add-disc-table col.c-ip {
  width: 110px;
}
.add-disc-table col.c-src {
  width: 132px;
}
.add-disc-table col.c-last {
  width: 88px;
}
.add-disc-table col.c-act {
  width: 204px;
}
.add-scan-table col.c-name {
  width: 26%;
}
.add-scan-table col.c-ip {
  width: 130px;
}
.add-scan-table col.c-mac {
  width: 160px;
}
.add-scan-table col.c-fw {
  width: 100px;
}
.add-scan-table col.c-st {
  width: 130px;
}
.add-scan-table col.c-act {
  width: 170px;
}
.add-more-known {
  display: block;
  padding: var(--sp-10) var(--sp-16);
  font-size: 12.5px;
  color: var(--color-text-2);
  text-decoration: none;
}
.add-empty h2 {
  font-size: 16px;
}
.add-empty p {
  max-width: 440px;
  font-size: 13.5px;
}
.add-empty {
  padding: 36px var(--sp-20);
}

/* phone: cards instead of the tables */
.add-cards {
  display: none;
}
@media (max-width: 44.99rem) {
  .add-table {
    display: none;
  }
  .add-cards {
    display: flex;
    flex-direction: column;
    gap: var(--sp-8);
  }
}
.add-card {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr);
  gap: 0 4px;
  padding: 8px 12px 10px 2px;
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
}
.add-card.is-ign {
  background: var(--color-surface-2);
}
.add-card-sel {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
}
.add-card-sel input {
  width: 18px;
  height: 18px;
  accent-color: var(--color-accent);
}
.add-card-body {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  padding-top: 8px;
}
.add-card-title {
  display: flex;
  gap: var(--sp-6);
  align-items: flex-start;
  flex-wrap: wrap;
}
.add-card-name {
  font-weight: 600;
  overflow-wrap: anywhere;
}
.add-card.is-ign .add-card-name {
  color: var(--color-text-2);
}
.add-card-meta {
  font: 400 12px var(--font-mono);
  color: var(--color-text-2);
  overflow-wrap: anywhere;
}
.add-card-src {
  font-size: 12px;
  color: var(--color-text-3);
}
.add-card-acts {
  display: flex;
  gap: var(--sp-6);
  margin-top: 4px;
}
.add-card-acts .btn {
  flex: 1;
}
.add-card-btn {
  margin-top: 4px;
}
.add-scan-card {
  display: block;
  padding: 10px 12px;
}
.add-scan-card .add-card-body {
  padding-top: 0;
  gap: 6px;
}
.add-scan-card .add-card-title {
  justify-content: space-between;
  flex-wrap: nowrap;
}

/* Network scan */
.add-scanbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-8);
  align-items: center;
  margin-bottom: var(--sp-12);
}
/* the id beats the global input rule (width 100 %, font inherit) */
#scan input.add-subnet {
  width: 320px; /* two prefilled /24 ranges (#270) */
  max-width: 100%;
  font-family: var(--font-mono);
}
.add-scanstatus {
  font-size: 12.5px;
  color: var(--color-text-2);
}
.add-scanerr {
  flex-basis: 100%;
}
.scard-add .add-scan > * + * {
  margin-top: var(--sp-12);
}
.add-progress {
  margin-bottom: var(--sp-12);
  padding: var(--sp-14) var(--sp-16);
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
}
.add-progress-head {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-10);
  flex-wrap: wrap;
  margin-bottom: var(--sp-8);
  font-size: 13px;
}
.add-progress-t {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-8);
}
.add-progress-t .add-note-dot {
  background: var(--color-accent);
}
.add-progress-t b {
  font-weight: 600;
}
.add-progress progress {
  display: block;
  width: 100%;
  height: 8px;
  border: 0;
  border-radius: 4px;
  overflow: hidden;
  appearance: none;
  background: var(--color-surface-3);
}
.add-progress progress::-webkit-progress-bar {
  background: var(--color-surface-3);
}
.add-progress progress::-webkit-progress-value {
  background: var(--color-accent);
}
.add-progress progress::-moz-progress-bar {
  background: var(--color-accent);
}
.add-progress-found {
  margin-top: var(--sp-8);
  font-size: 12.5px;
  color: var(--color-text-2);
}
.add-chips {
  display: flex;
  gap: var(--sp-6);
  flex-wrap: wrap;
  align-items: center;
  margin-bottom: var(--sp-10);
}
.add-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-6);
  height: var(--ctl);
  padding: 0 11px;
  border-radius: 999px;
  border: var(--hair-w) solid var(--color-border-strong);
  background: var(--color-surface);
  color: var(--color-text);
  font: 500 12.5px var(--font-sans);
  text-decoration: none;
  white-space: nowrap;
}
.add-chip.is-on {
  border-color: var(--color-text);
  background: var(--color-text);
  color: var(--color-surface);
}
.add-chip-n {
  opacity: 0.75;
}

/* Import from TasmoAdmin */
.add-import {
  display: flex;
  flex-direction: column;
  gap: var(--sp-14);
  max-width: 1000px;
}
.add-import > .banner {
  margin: 0;
}
.add-drop {
  display: flex;
  gap: var(--sp-12);
  align-items: center;
  flex-wrap: wrap;
  padding: var(--sp-16);
  background: var(--color-surface);
  border: 1.5px dashed var(--color-border-strong);
  border-radius: var(--r-card);
}
.add-drop.is-over {
  border-color: var(--color-accent);
  background: var(--color-accent-bg);
}
.add-drop-text {
  flex: 1;
  min-width: 220px;
  font-size: 13px;
}
.add-drop-text b {
  font-weight: 600;
}
.add-drop-sub {
  display: block;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.add-drop-btn {
  position: relative;
  display: inline-flex;
  font-weight: 500;
}
.add-drop-btn:focus-within {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
/* the real file input covers its button, invisible: clicking the button opens the chooser */
.add-drop-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}
.add-plan {
  overflow: hidden;
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
}
.add-plan-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-8);
  flex-wrap: wrap;
  padding: var(--sp-12) var(--sp-16);
  border-bottom: var(--hair-w) solid var(--color-border);
}
.add-plan-counts {
  display: flex;
  gap: var(--sp-6);
  flex-wrap: wrap;
}
.add-improw {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-6) var(--sp-14);
  min-height: 52px;
  padding: var(--sp-10) var(--sp-16);
  border-bottom: var(--hair-w) solid var(--color-border);
}
.add-impw {
  flex: none;
  width: 70px;
}
.add-impw-res {
  width: 80px;
}
.add-improw-dev {
  flex: 1 1 220px;
  min-width: 0;
}
.add-improw-name {
  display: block;
  font-size: 13px;
  font-weight: 500;
  overflow-wrap: anywhere;
}
.add-improw-ip {
  display: block;
  font-size: 12px;
  color: var(--color-text-2);
}
.add-improw-why {
  flex: 2 1 260px;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.add-plan-foot {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-10);
  flex-wrap: wrap;
  align-items: center;
  padding: var(--sp-12) var(--sp-16);
  font-size: 12.5px;
}
.add-result-acts {
  justify-content: flex-end;
}
/* the prototype's fields are 13 px; this outweighs the global input rule (13.5 px) */
.add-manual input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
.add-manual textarea,
#scan input.add-subnet {
  font-size: 13px;
}
/* the dots of a prefilled default password read like a filled field */
.add-manual input[type="password"]::placeholder {
  color: var(--color-text);
}
.add-head + .utabs .bdg {
  height: 18px;
  padding: 0 var(--sp-6);
  border-radius: 9px;
  font-size: 11px;
  background: var(--color-accent);
  color: var(--color-on-accent);
}
/* ==== page: Bulk action (WP P7): scope (devices + options aside) and result ==== */
.bk-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: flex-start;
}
.bk-devs {
  flex: 3 1 460px;
  max-width: 100%;
  min-width: 0;
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  overflow: hidden;
}
.bk-devs-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  padding: 10px 8px 10px 16px;
  border-bottom: var(--hair-w) solid var(--color-border);
}
.bk-devs-head h2 {
  margin: 0;
  font: 600 14px var(--font-sans);
  letter-spacing: normal;
}
.bk-change {
  display: inline-flex;
  align-items: center;
  height: var(--ctl);
  padding: 0 10px;
  color: var(--color-accent-fg);
  font: 500 13px var(--font-sans);
  text-decoration: none;
}
.bk-change:hover {
  color: var(--color-accent-hover);
}
.bk-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 4px 4px 16px;
  border-bottom: var(--hair-w) solid var(--color-border);
  min-height: 52px;
}
.bk-dev,
.bk-rdev {
  flex: 1;
  min-width: 0;
}
.bk-name {
  display: block;
  font-weight: 500;
  font-size: 13px;
  overflow-wrap: anywhere;
}
.bk-meta {
  display: block;
  font: 400 12px var(--font-mono);
  color: var(--color-text-2);
}
.bk-row .bk-meta > span {
  display: inline;
}
.bk-x {
  width: 40px;
  height: 40px;
  flex: none;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--color-text-2);
  font: 400 18px var(--font-sans);
  cursor: pointer;
}
.bk-x:hover {
  background: var(--color-surface-2);
  color: var(--color-text-2);
}
.bk-skipnote {
  margin: 0;
  padding: 10px 16px;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.bk-aside {
  flex: 2 1 300px;
  max-width: 100%;
  min-width: 0;
  position: sticky;
  top: 110px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
}
.bk-field {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin: 0;
  font: 500 12.5px var(--font-sans);
  color: var(--color-text-2);
}
.bk-field input:not([type="checkbox"]),
.bk-field select {
  font: 400 13.5px var(--font-sans);
}
.bk-field select {
  padding-left: 8px;
}
.bk-field textarea {
  min-height: 0;
  padding: 8px 10px;
  font: 400 13px/1.5 var(--font-mono);
  resize: vertical;
}
.bk-help {
  display: block;
  font: 400 12px var(--font-sans);
  color: var(--color-text-3);
}
.bk-opt {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 40px;
  margin: 0;
  font: 400 13px var(--font-sans);
  color: var(--color-text);
  cursor: pointer;
}
.bk-opt input {
  width: 16px;
  height: 16px;
  margin: 0;
  flex: none;
  accent-color: var(--color-accent);
}
.bk-warn {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 10px 12px;
  border-radius: 8px;
  border: var(--hair-w) solid var(--color-warn);
  background: var(--color-warn-bg);
  color: var(--color-warn-fg);
  font-size: 13px;
}
.bk-warn-dot {
  width: 8px;
  height: 8px;
  margin-top: 5px;
  border-radius: 50%;
  flex: none;
  background: var(--color-warn);
}
.bk-sum {
  padding-top: 12px;
  border-top: var(--hair-w) solid var(--color-border);
  font-size: 13px;
  color: var(--color-text-2);
}
.bk-titlerow {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.bk-titlerow .bk-state {
  height: 22px;
}
.bk-cmdline {
  margin-bottom: 12px;
  padding: 8px 12px;
  border-radius: 8px;
  border: var(--hair-w) solid var(--color-border);
  background: var(--color-surface-2);
  font: 400 12.5px var(--font-mono);
  overflow-wrap: anywhere;
}
.bk-tiles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  max-width: 640px;
  margin-bottom: 14px;
}
.bk-tile {
  padding: 12px 14px;
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
}
.bk-tile-l {
  display: flex;
  align-items: center;
  gap: 7px;
  font: 500 12.5px var(--font-sans);
  color: var(--color-text-2);
}
.bk-tile-v {
  margin-top: 6px;
  font: 600 24px/1.1 var(--font-sans);
  font-variant-numeric: tabular-nums;
}
.bk-res {
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  overflow: hidden;
}
.bk-rrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
  padding: 10px 16px;
  border-bottom: var(--hair-w) solid var(--color-border);
  min-height: 52px;
}
.bk-rdev {
  flex: 1 1 240px;
}
.bk-rbadge {
  width: 86px;
  flex: none;
}
.bk-detail {
  flex: 2 1 260px;
  min-width: 0;
  font: 400 12.5px var(--font-mono);
  color: var(--color-text-2);
  overflow-wrap: anywhere;
}
/* ==== page: System settings (design v2, WP P8) ==== */
/* the prototype keeps 14 px between the page header and the tabs */
.pghead:has(+ .utabs[aria-label="Settings sections"]) {
  margin-bottom: var(--sp-14);
}
/* Settings page (issue #275): tabs in one form, a grid of cards per tab, one save bar */
.syspage {
  display: block;
}
.sys-tab[hidden],
.sys-after[hidden] {
  display: none;
}
.scard {
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  padding: var(--sp-16) var(--sp-18);
  min-width: 0;
}
/* Backups tab: the TasmoFleet database card and the snapshot schedule (#301) */
.db-incl {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-8);
  font-size: 13px;
}
.db-contents {
  margin: var(--sp-12) 0 0;
  max-width: none;
}
.db-list {
  margin: var(--sp-6) 0 0;
  padding-left: 18px;
  font-size: 13px;
}
.db-list li {
  margin: var(--sp-4) 0;
}
.db-where {
  font-weight: 400;
  color: var(--color-text-3);
  font-size: 12px;
  margin-left: var(--sp-8);
}
.db-restore {
  margin: var(--sp-6) 0;
  padding-left: 20px;
  font-size: 13px;
}
.db-restore li {
  margin: var(--sp-4) 0;
}
/* a path in the narrow Snapshots column wraps inside its own box instead of across the next path (#410) */
.db-restore .mono {
  display: inline-block;
  max-width: 100%;
  overflow-wrap: anywhere;
}
.db-note {
  grid-column: 1 / -1;
  margin: 0;
}
#card-download .st-fail > .error {
  margin: var(--sp-12) 0 0;
  max-width: none;
}
#card-snapshots .sys-status {
  margin-top: var(--sp-4);
}
#card-schedule .sbody {
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
}
.scard.is-off,
.dep.is-off,
.sys-test.is-off {
  opacity: 0.5;
}
.sch {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-10);
  align-items: flex-start;
  margin-bottom: var(--sp-14);
}
.sch h2 {
  margin: 0;
  font: 600 14px var(--font-sans);
  letter-spacing: normal;
}
.cnote {
  margin: var(--sp-2) 0 0;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.cbd {
  display: flex;
  gap: var(--sp-6);
  flex-wrap: wrap;
  justify-content: flex-end;
}
.sbody {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-14);
  align-items: start;
}
.sbody-plain {
  display: block;
}
.sbody-plain > * + * {
  margin-top: var(--sp-12);
}
.sf {
  min-width: 0;
  display: flex;
  flex-direction: column;
  grid-column: span 2;
}
.sf.w-half,
.sf.w-xs {
  grid-column: span 1;
}
.sf-main {
  min-width: 0;
  display: flex;
  flex-direction: column;
  flex: 1;
}
.sf.w-xs input:not([type="checkbox"]):not([type="radio"]) {
  max-width: 120px;
}
.sf input[type="time"] {
  max-width: 140px; /* a time of day needs no more, whatever the field's width */
}
.sf input:not([type="checkbox"]):not([type="radio"]),
.sf select,
.sf textarea {
  width: 100%;
}
.lrow {
  display: flex;
  align-items: center;
  gap: var(--sp-6);
  min-height: 20px;
  margin-bottom: var(--sp-5);
}
.lbl {
  font: 500 13px var(--font-sans);
  color: var(--color-text-2);
}
.meta {
  margin-left: auto;
  display: flex;
  gap: var(--sp-6);
  align-items: center;
}
.sf-nolabel .lrow {
  min-height: 0;
}
.sf-nolabel .meta {
  margin-left: 0;
}
.sf-nolabel .rcards {
  flex-direction: row;
}
.sf-nolabel .rcard {
  flex: 1;
}
.sf-nolabel .rcards legend {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.sf.w-xs .meta {
  margin-left: 0;
}
.srst {
  position: relative;
  font: 500 12px var(--font-sans);
  color: var(--color-accent-fg);
  cursor: pointer;
  white-space: nowrap;
}
.srst:hover {
  text-decoration: underline;
}
.srst input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
}
.srst:has(input:focus-visible) {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}
.srst .srst-on,
.srst:has(input:checked) .srst-off {
  display: none;
}
.srst:has(input:checked) .srst-on {
  display: inline;
  color: var(--color-warn-fg);
}
.sf.w-xs .srst .srst-off,
.sf.w-xs .srst .srst-on {
  font-size: 0;
}
.sf.w-xs .srst .srst-on::before {
  content: "\21BA  Default";
  content: "\21BA  Default" / ""; /* the box is named by its text, "Default, set on save" */
  font-size: 12px;
}
.sf.w-xs .lrow:has(.srst input:checked) {
  flex-wrap: wrap;
}
.sf.w-xs .srst .srst-off::before {
  content: "\21BA";
  font-size: 13px;
}
.sf-locked .in.ro,
.in.ro {
  display: flex;
  align-items: center;
  min-height: var(--ctl);
  padding: 0 10px;
  border: var(--hair-w) dashed var(--color-border-strong);
  border-radius: var(--r-control);
  background: var(--color-surface-2);
  color: var(--color-text-2);
  font-size: 13px;
  overflow-wrap: anywhere;
}
.sf.w-xs .in.ro {
  max-width: 120px;
}
.sopt {
  min-height: 0;
  margin-top: var(--sp-8);
  font-size: 12.5px;
}
.sopt[hidden] {
  display: none;
}
.sbody > .sopt,
.scan-nets {
  grid-column: 1 / -1;
}
.scan-nets {
  margin: 0;
}
.cks {
  display: flex;
  gap: var(--sp-18);
  flex-wrap: wrap;
  min-height: var(--ctl);
  align-items: center;
}
.cks .ucheck {
  min-height: 0;
}
.brow {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 96px auto;
  gap: var(--sp-14);
  align-items: start;
}
.brow .sf {
  grid-column: auto;
}
/* in a half-width card (tablet) the host would get under 100 px and its "Reset to default" would run
   over the Port label: TLS drops to its own row (#633) */
@media (min-width: 45rem) and (max-width: 50.99rem) {
  .brow:has(.w-tls) {
    grid-template-columns: minmax(0, 1fr) 96px;
  }
  .brow .w-tls {
    grid-column: 1 / -1;
  }
}
.brow .tlsrow {
  display: flex;
  align-items: center;
  min-height: var(--ctl);
}
.brow .tlsrow .sf-main {
  flex: none;
}
.brow .tlsrow .lbl {
  white-space: nowrap;
}
/* switches: the track is the label of the hidden checkbox, so a click anywhere on it toggles */
.sf-switch {
  flex-direction: row;
  align-items: flex-start;
  gap: var(--sp-10);
}
.swhit {
  position: relative;
  display: inline-flex;
  flex: none;
}
.swhit input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}
.swtrack {
  position: relative;
  flex: none;
  width: 34px;
  height: 20px;
  margin-top: var(--sp-1);
  border-radius: 999px;
  background: var(--color-surface-3);
  border: var(--hair-w) solid var(--color-border-strong);
  cursor: pointer;
}
.swtrack::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-knob);
  transition: left 0.15s;
}
input:checked + .swtrack {
  background: var(--color-accent);
  border-color: var(--color-accent);
}
input:checked + .swtrack::after {
  left: 16px;
}
.sf-warn input:checked + .swtrack {
  background: var(--color-warn);
  border-color: var(--color-warn);
}
input:focus-visible + .swtrack {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
.sf-switch .lrow {
  margin-bottom: 0;
}
.sf-switch .lbl {
  color: var(--color-text);
  font-size: 13.5px;
  font-weight: 600;
}
.sf-switch .ufield-help {
  margin-top: var(--sp-1);
}
/* settings that only matter while a switch is on: ordinary fields of the card, dimmed while it is off */
.dep {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-14);
}
/* the info button and its popover */
.uinfo {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: var(--hair-w) solid var(--color-border-strong);
  background: var(--color-surface);
  color: var(--color-text-2);
  font: 600 10.5px/1 var(--font-sans);
  padding: 0;
  cursor: pointer;
  display: inline-grid;
  place-items: center;
  flex: none;
}
.uinfo:hover,
.uinfo[aria-expanded="true"] {
  border-color: var(--color-accent);
  color: var(--color-accent-fg);
  background: var(--color-accent-bg);
}
.uinfo-pop {
  position: fixed;
  inset: auto;
  margin: 0;
  max-width: min(300px, calc(100vw - 16px));
  padding: var(--sp-10) var(--sp-12);
  background: var(--color-surface);
  color: var(--color-text);
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-lg);
  font: 12.5px/1.5 var(--font-sans);
}
.uinfo-pop:not(:popover-open):not(.open) {
  display: none;
}
/* the sticky save bar */
.sbar {
  position: sticky;
  bottom: var(--sb-h);
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: var(--sp-8);
  flex-wrap: wrap;
  margin-top: var(--sp-24);
  padding: var(--sp-12) 0;
  background: var(--color-bg);
  border-top: var(--hair-w) solid var(--color-border);
  box-shadow: var(--shadow-bar);
}
.sbar-state {
  font-size: 13px;
  color: var(--color-text-3);
}
.sbar.is-dirty .sbar-state {
  color: var(--color-text);
  font-weight: 500;
}
/* the unsaved dot hangs in the gutter left of the bar (#362): out of the text flow, so it takes no
   room (the phone's one-line state text must not be cut for it) and nothing moves when it shows */
.sbar::before {
  content: "";
  position: absolute;
  left: calc(-1 * (6px + var(--sp-4)));
  top: 50%;
  width: 6px;
  height: 6px;
  margin-top: -3px;
  border-radius: 50%;
  background: var(--color-warn);
  visibility: hidden;
}
.sbar.is-dirty::before {
  visibility: visible;
}
.sbar-chips {
  display: inline-flex;
  gap: var(--sp-6);
  flex-wrap: wrap;
}
.sbar-chip {
  font: 500 12px var(--font-sans);
  padding: var(--sp-2) var(--sp-8);
  border-radius: 999px;
  background: var(--color-warn-bg);
  color: var(--color-warn-fg);
}
.sbar-sp {
  flex: 1;
}
/* the dot's space is always reserved on the Settings tabs (#362): it only becomes visible */
.utabs[aria-label="Settings sections"] .utab::after {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-left: var(--sp-5);
  border-radius: 50%;
  background: var(--color-warn);
  vertical-align: 2px;
  visibility: hidden;
}
.utabs[aria-label="Settings sections"] .utab.is-dirty::after {
  visibility: visible;
}
.sys-picker {
  display: none;
}
.sys-picker select {
  flex: 1;
}
/* after the form: the container's settings and the danger zone */
.syswrap {
  display: flex;
  flex-direction: column;
}
.syswrap > .syspage {
  display: contents;
}
.syswrap .sys-after {
  order: 1;
}
.syswrap .sbar {
  order: 2;
}
.sys-after {
  max-width: 1240px;
  margin-top: var(--sp-16);
  display: flex;
  flex-direction: column;
  gap: var(--sp-40);
}
.sys-danger {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-16);
  flex-wrap: wrap;
  padding: var(--sp-14) var(--sp-18);
  border: var(--hair-w) solid var(--color-danger);
  background: var(--color-danger-bg);
  border-radius: var(--r-card);
}
.sys-subhead {
  margin: var(--sp-16) 0 0;
  font: 600 13px var(--font-sans);
}
/* Advanced (#350): the after-form cards use the full content width; the container settings sit in
   two tables side by side, one table on a phone */
.sys-after {
  max-width: none;
}
.cset {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-16) var(--sp-24);
  margin-top: var(--sp-8);
  align-items: start;
}
.cset table {
  margin: 0;
}
.cset td.grow code {
  overflow-wrap: anywhere;
}
/* tablet (#592): half-width columns leave each table too narrow for the longest key, so the tables stack */
@media (min-width: 45rem) and (max-width: 74.99rem) {
  .cset {
    grid-template-columns: minmax(0, 1fr);
  }
}
@media (max-width: 44.99rem) {
  .cset {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }
}
@media (max-width: 44.99rem) {
  /* phone: a section picker replaces the strip; cards lose their frame and become sections */
  .sys-picker:not([hidden]) {
    display: flex;
    align-items: center;
    gap: var(--sp-8);
    margin: var(--sp-14) 0 var(--sp-6);
    font-size: 12.5px;
    color: var(--color-text-3);
  }
  .sys-picker:not([hidden]) ~ .utabs[aria-label="Settings sections"] {
    display: none;
  }
  .utabs[aria-label="Settings sections"] {
    scrollbar-width: none;
  }
  .utabs[aria-label="Settings sections"]::-webkit-scrollbar {
    display: none;
  }
  .syspage .cstacks,
  .sys-after .cstacks {
    gap: 0;
  }
  .scard {
    border: 0;
    border-top: var(--hair-w) solid var(--color-border);
    border-radius: 0;
    background: transparent;
    padding: var(--sp-16) 0;
  }
  .sch {
    flex-wrap: wrap;
  }
  .cbd {
    justify-content: flex-start;
  }
  .sbody,
  #card-schedule .sbody,
  .dep,
  .brow {
    grid-template-columns: minmax(0, 1fr);
  }
  .sf-nolabel .rcards {
    flex-direction: column;
  }
  .sf,
  .sf.w-half,
  .sf.w-xs {
    grid-column: auto;
  }
  .sf.w-xs input:not([type="checkbox"]):not([type="radio"]),
  .sf.w-xs .in.ro {
    max-width: none;
  }
  /* 40 px touch targets: the switch and the info button reach past their drawn size */
  .swhit {
    min-width: 40px;
    min-height: 40px;
    align-items: center;
    justify-content: center;
    margin: calc(-1 * var(--sp-10)) calc(-1 * var(--sp-3));
  }
  .uinfo {
    width: 40px;
    height: 40px;
    margin: calc(-1 * var(--sp-12));
    border: 0;
    background: radial-gradient(
      circle,
      var(--color-surface) 7px,
      var(--color-border-strong) 7px 8px,
      transparent 8px
    );
  }
  /* hovered or open, the 16px picture changes colour; the 40px box must not fill (#680: it covered the last letters of the label) */
  .uinfo:hover,
  .uinfo[aria-expanded="true"] {
    background: radial-gradient(
      circle,
      var(--color-accent-bg) 7px,
      var(--color-accent) 7px 8px,
      transparent 8px
    );
  }
  .sbar {
    margin-top: var(--sp-8);
  }
  .sbar-chips,
  .sbar-sp {
    display: none;
  }
  .sbar {
    flex-wrap: nowrap;
  }
  .sbar-state {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}
.sbody > .sys-status {
  grid-column: 1 / -1;
}
.sys-test {
  display: flex;
  align-items: center;
  gap: var(--sp-10);
  flex-wrap: wrap;
  grid-column: 1 / -1;
}
.sys-test-res {
  font-size: 12.5px;
  color: var(--t-fg);
}
.sys-test-help {
  font-size: 12px;
  color: var(--color-text-3);
  flex: 1 1 260px;
}
.sys-status {
  display: flex;
  align-items: center;
  gap: var(--sp-12);
  flex-wrap: wrap;
  padding: var(--sp-10) var(--sp-14);
  border-radius: var(--r-control);
  background: var(--color-surface-2);
}
.sys-card-foot {
  grid-column: 1 / -1;
  padding-top: var(--sp-12);
  border-top: var(--hair-w) solid var(--color-border);
}
.sys-status-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex: none;
  background: var(--t);
}
.sys-status-dot.t-ok {
  animation: tf-pulse 2.4s ease-in-out infinite;
}
.sys-status-text {
  flex: 1;
  min-width: 220px;
}
.sys-status-text b {
  display: block;
  font-weight: 600;
  font-size: 13.5px;
}
.sys-status-text span {
  display: block;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.sys-skipped {
  margin: var(--sp-4) 0 0;
  padding-left: var(--sp-18);
}
.sys-container table {
  margin-top: var(--sp-8);
}
@media (prefers-reduced-motion: reduce) {
  .sys-status-dot.t-ok {
    animation: none;
  }
}

/* ==== page: System settings, Backups storage and Reset (design v2, W5 / N9) ==== */
/* Storage: the four totals and the lists below them (Devices, Backups, #409) */
.sys-sums {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: var(--sp-10);
}
.sys-sum {
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  padding: var(--sp-12) var(--sp-14);
}
a.sys-sum {
  display: block;
  color: inherit;
  text-decoration: none;
}
a.sys-sum:hover {
  border-color: var(--color-border-strong);
}
.sys-sum-label {
  font-size: 12.5px;
  color: var(--color-text-2);
}
.sys-sum-value {
  margin-top: var(--sp-4);
  font: 600 22px/1.2 var(--font-sans);
  font-variant-numeric: tabular-nums;
}
.sys-orphans {
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  overflow: hidden;
}
.sys-store-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* backups of deleted devices: one row per run, the device backups' actions on the right */
.sys-orphan {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-10);
  flex-wrap: wrap;
  padding: var(--sp-10) var(--sp-14);
  border-top: var(--hair-w) solid var(--color-border);
}
.sys-orphan:first-child {
  border-top: 0;
}
.sys-orphan-main {
  flex: 1 1 220px;
  min-width: 0;
}
.sys-orphan-title {
  display: flex;
  align-items: center;
  gap: var(--sp-8);
  font-size: 13px;
  font-weight: 500;
}
.sys-unref .sys-store-name {
  white-space: normal;
  overflow-wrap: anywhere;
}
.sys-orphan-detail {
  font-size: 12.5px;
  color: var(--color-text-2);
}
/* Advanced: "Reset all settings" in a danger-bordered box */
.sys-danger-title {
  font-weight: 600;
  color: var(--color-danger-fg);
}
.sys-danger-text {
  font-size: 12.5px;
  color: var(--color-text-2);
}
/* ==== page: Devices → Backups (#409) ==== */
.bk-table td .bk-rsn {
  margin-left: var(--sp-6);
  color: var(--color-text-2);
  font-size: 13px;
  white-space: normal;
}
.bk-tools {
  margin-bottom: var(--sp-10);
}
.sys-sum-sm {
  font-size: 15px;
  font-weight: 600;
}
/* the device table keeps to its card (no sideways scroll) */
.bk-table {
  table-layout: fixed;
}
.bk-table .bk-c-sel {
  width: 40px;
}
.bk-table .bk-c-dev {
  width: 20%;
}
.bk-table .bk-c-new {
  width: 22%;
}
.bk-table .bk-c-runs {
  width: 52px;
}
.bk-table .bk-c-files {
  width: 108px;
}
.bk-table .bk-c-act {
  width: 48px;
}
.bk-table td.nowrap,
.bk-table td {
  overflow: hidden;
  text-overflow: ellipsis;
}
.bk-table td.rowacts {
  overflow: visible;
}
/* a long name and "Today 09:52 PM · 38s ago" wrap inside their fixed columns instead of being clipped
   (#633: cut off at 768 and 1024 px) */
.bk-table td.dv-name {
  overflow-wrap: anywhere;
}
.bk-table td.bk-new .muted {
  display: inline-block;
}
/* two half columns (tablet) leave the device table about 220 px at 768 px, less than its fixed columns
   need: the two cards that lead the page take the full width, Storage and the rest balance below (#633) */
@media (min-width: 45rem) and (max-width: 74.99rem) {
  #card-lastrun,
  #card-devices {
    column-span: all;
  }
}
/* the unit after the run count is for phones only (the column header says it on desktop) */
.bk-u {
  display: none;
}
@media (max-width: 44.99rem) {
  .bk-u {
    display: inline;
  }
}
/* Storage totals: two by two in the side column */
#card-storage .sys-sums {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
/* wide content (>= 1700 px viewport): the result text stays on one line */
@media (min-width: 106rem) {
  .bk-table td .bk-rsn {
    white-space: nowrap;
  }
}

/* ==== page: Audit log (design v2, WP P8) ==== */
/* the filter row: search (220-340 px) and the selects at their content width */
.audit-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-8);
  margin: 0 0 var(--sp-12);
}
.audit-filters input[type="search"] {
  flex: 1 1 220px;
  max-width: 340px;
  font-size: 13.5px;
}
.audit-filters select {
  width: auto;
  font-size: 13px;
  padding-left: var(--sp-8);
}
.audit-filters [hidden] {
  display: none;
}
/* phone: the search over a full row, the selects two by two (three across cut "Last 7 days") */
@media (max-width: 44.99rem) {
  .audit-filters {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .audit-filters input[type="search"] {
    grid-column: 1 / -1;
    max-width: none;
  }
  .audit-filters select {
    width: 100%;
    min-width: 0;
  }
  .audit-filters .btn {
    grid-column: 1 / -1;
  }
}
.audit-choices {
  margin: var(--sp-4) 0 0;
  padding-left: var(--sp-18);
}
.audit-card {
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  /* no overflow: hidden, the Columns menu must never be clipped (0.10.1) */
}
.audit-card .pager {
  padding: var(--sp-10) var(--sp-16);
  margin: 0;
}

/* ==== page: first-run setup (design v2 "setup" step 3, WP P8) ==== */
/* a page without navigation: the brand and the card centred, 96 px from the top */
main.bare {
  padding: 96px var(--sp-16) var(--sp-40);
}
.bare .brand {
  min-height: 40px; /* a touch target on a phone, as every other control */
}
.bare .auth-card:has(.setup-steps) {
  width: min(480px, 100%);
}
.setup-steps {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--sp-8);
}
.setup-steps li {
  display: flex;
  align-items: center;
  gap: var(--sp-7);
  flex: 1;
  min-width: 0;
}
.setup-mark {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  flex: none;
  font: 600 11px var(--font-sans);
  background: var(--color-surface-3);
  color: var(--color-text-2);
}
.setup-steps .is-done .setup-mark {
  background: var(--color-ok);
  color: var(--color-on-accent);
}
.setup-steps .is-current .setup-mark {
  background: var(--color-accent);
  color: var(--color-on-accent);
}
.setup-label {
  font: 500 12px var(--font-sans);
  color: var(--color-text-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.setup-steps .is-current .setup-label {
  color: var(--color-text);
}
.setup-bar {
  flex: 1;
  height: 2px;
  min-width: 8px;
  border-radius: 1px;
  background: var(--color-border);
}
.setup-steps .is-done .setup-bar {
  background: var(--color-ok);
}
.setup-lead {
  margin: 0;
  color: var(--color-text-2);
  font-size: 13.5px;
  text-wrap: pretty;
}
.setup-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-14);
}
.setup-broker {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 96px;
  gap: var(--sp-10);
}
.setup-broker + .setup-broker {
  grid-template-columns: 1fr 1fr;
}
@media (max-width: 44.99rem) {
  main.bare {
    padding-top: var(--sp-40);
  }
}
/* ==========================================================================
   P10 (design v2): the Backups tab, the backup comparison, the restore pages and the
   Replace device tab. Markup: backuppages.templ, comparepage.templ, restorepages.templ,
   replacepage.templ. Behaviour: select.js (selection bar), form.js (comparison pickers),
   dialog.js (delete and prune dialogs).
   ========================================================================== */

/* Backups tab: retention line, Prune now, total, Back up now */
.bk-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-8) var(--sp-12);
  flex-wrap: wrap;
  margin-bottom: var(--sp-10);
}
.bk-retention {
  flex: 1 1 300px;
  font-size: 13px;
  color: var(--color-text-2);
  text-wrap: pretty;
}
.bk-link {
  color: var(--color-accent-fg);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.bk-total {
  font-size: 12.5px;
  color: var(--color-text-3);
}

/* the selection bar: dark, in the flow above the list (the prototype's, not the fixed bulk bar) */
.bk-selbar {
  display: flex;
  align-items: center;
  gap: var(--sp-8);
  flex-wrap: wrap;
  padding: 6px 6px 6px 14px;
  margin-bottom: var(--sp-10);
  border-radius: 9px;
  background: var(--color-text);
  color: var(--color-surface);
}
.bk-selbar[hidden],
.bk-selbar [hidden] {
  display: none;
}
.bk-selcount {
  flex: 1;
  font: 600 13px var(--font-sans);
}
.bk-selbar .btn.btn-primary {
  padding: 0 var(--sp-12);
  border: 0;
}
.bk-selbar .btn.bk-seldel,
.bk-selbar .btn.bk-seldel:hover {
  border-color: color-mix(in oklab, var(--color-surface) 30%, transparent);
  background: transparent;
  color: var(--color-surface);
  font-weight: 500;
}

/* the rows: one per backup run or failed attempt */
.bk-list {
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  overflow: hidden;
}
.bk-list .bk-row {
  display: flex;
  align-items: center;
  gap: 6px var(--sp-12);
  flex-wrap: wrap;
  padding: 6px 10px 6px 4px;
  border-bottom: var(--hair-w) solid var(--color-border);
  min-height: 60px;
}
.bk-check {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  flex: none;
}
.bk-check input {
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--color-accent);
}
.bk-main {
  flex: 1 1 260px;
  min-width: 0;
}
.bk-title {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.bk-title time {
  font-weight: 500;
}
.bk-detail {
  font-size: 12px;
  color: var(--color-text-2);
}
.bk-file {
  font: 400 12px var(--font-mono);
  color: var(--color-text-2);
  overflow-wrap: anywhere;
}
.bk-reason {
  margin-top: 2px;
  font-size: 12.5px;
  color: var(--color-danger-fg);
}
.bk-acts {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  align-items: center;
}
/* when the actions wrap under the text (phone), they get the card's inner padding like the text above */
@media (max-width: 44.99rem) {
  .bk-list .bk-acts {
    margin-left: var(--sp-8);
  }
}
/* a run's Files block (#324), folded into the row: last child, under the text and the actions */
.bk-list .bk-row > details.bk-files {
  flex: 0 0 calc(100% - 44px);
  min-width: 0;
  margin: 0 0 var(--sp-6) 44px;
}
.bk-list .bk-row > details.bk-files > summary {
  min-height: 36px;
  font-size: 13px;
  font-weight: 600;
}
@media (max-width: 44.99rem) {
  .bk-list .bk-row > details.bk-files {
    flex-basis: calc(100% - var(--sp-8));
    margin-left: var(--sp-8);
  }
}
.bk-fl {
  display: flex;
  flex-direction: column;
}
.bk-fl-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 2px var(--sp-12);
  padding: 6px 0;
  border-top: var(--hair-w) solid var(--color-border);
  font-size: 12.5px;
}
.bk-fl-path {
  font-family: var(--font-mono);
  overflow-wrap: anywhere;
}
.bk-fl-tags {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  justify-content: flex-end;
}
.bk-fl-size {
  font-family: var(--font-mono);
  color: var(--color-text-2);
  text-align: right;
  min-width: 64px;
}
.bk-fl-why {
  grid-column: 1 / -1;
  font-size: 12px;
  color: var(--color-text-2);
}
.bk-fl-row.is-skipped .bk-fl-path {
  color: var(--color-text-2);
}
.bk-fl-note {
  margin: var(--sp-8) 0 0;
  font-size: 12px;
  color: var(--color-text-2);
}
.bk-fl-acts {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--sp-8);
}
/* Maintainer decision (#324, 2026-11-01): inside the fold the list is about half the content width,
   badge and size right after the name, not names far left and sizes far right (#419 rule). */
.bk-files .bk-fl {
  max-width: max(50%, 26rem);
}
.bk-files .bk-fl-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
}
.bk-files .bk-fl-path {
  min-width: 0;
}
.bk-files .bk-fl-size {
  min-width: 0;
  text-align: left;
}
.bk-files .bk-fl-why {
  flex-basis: 100%;
}
.btn.bk-keep {
  padding: 0 var(--sp-10);
  gap: 7px;
  font-size: 12.5px;
}
.btn.bk-keep.is-kept,
.btn.bk-keep.is-kept:hover {
  background: var(--color-accent-bg);
  color: var(--color-accent-fg);
}
.bk-pin {
  width: 7px;
  height: 7px;
  border: 1.5px solid currentColor;
  border-radius: 1px;
  transform: rotate(45deg);
}
.bk-foot {
  margin: var(--sp-10) 0 0;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.bk-page > .ulist {
  margin-top: var(--sp-16);
}
.bk-job {
  display: grid;
  grid-template-columns: auto auto auto minmax(0, 1fr);
  gap: var(--sp-12);
  align-items: center;
  font-size: 13px;
}
.bk-job-detail {
  color: var(--color-text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bk-status:empty {
  display: none;
}
.bk-status {
  margin-top: var(--sp-10);
}

/* comparison: pickers in one row, the footer with the restore button */
.cmp-controls {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-10);
  align-items: flex-end;
  margin-bottom: var(--sp-12);
}
.cmp-pick {
  flex: 1 1 220px;
  max-width: 330px;
}
.cmp-pick select {
  font-size: 13.5px;
}
.cmp-all {
  display: flex;
  align-items: center;
  gap: var(--sp-8);
  height: var(--ctl);
  font-size: 13px;
  cursor: pointer;
}
.cmp-all input {
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--color-accent);
}
.cmp-note {
  margin: 0 0 var(--sp-10);
  font-size: 12.5px;
  color: var(--color-text-2);
}
.cmp-none {
  padding: 24px;
  text-align: center;
  color: var(--color-text-2);
  font-size: 13px;
}
.cmp-foot {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-10);
  flex-wrap: wrap;
  align-items: center;
  margin-top: var(--sp-14);
}
.cmp-foot > span {
  font-size: 12.5px;
  color: var(--color-text-2);
}

/* restore: the confirmation and the progress, the full content width in two card stacks (#324, #419) */
.rs-page {
  max-width: none;
  display: flex;
  flex-direction: column;
  gap: var(--sp-14);
}
.rs-page > .pghead {
  margin-bottom: 0;
}
.rs-page .banner {
  margin: 0;
}
.rs-changes {
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  overflow: hidden;
}
.rs-changes > h2 {
  margin: 0;
  padding: var(--sp-12) var(--sp-16);
  border-bottom: var(--hair-w) solid var(--color-border);
  font: 600 14px var(--font-sans);
  letter-spacing: normal;
}
.rs-grid {
  display: grid;
  grid-template-columns: minmax(120px, 0.6fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--sp-12);
  padding: var(--sp-8) var(--sp-16);
}
.rs-headrow {
  background: var(--color-surface-2);
  font: 500 12px var(--font-sans);
  color: var(--color-text-2);
}
.rs-row {
  border-top: var(--hair-w) solid var(--color-border);
  font: 400 12.5px var(--font-mono);
  overflow-wrap: anywhere;
}
.rs-key {
  font-weight: 500;
}
.rs-now {
  color: var(--color-text-2);
}
.rs-none {
  margin: 0;
  padding: var(--sp-12) var(--sp-16);
  font-size: 13px;
  color: var(--color-text-2);
}
.rs-steps-text {
  margin: 0;
  padding-left: 18px;
  font-size: 13px;
  color: var(--color-text-2);
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.rs-path,
.rs-cred,
.rs-fnote {
  margin: 0;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.rs-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-10);
}
.rs-ack {
  align-items: flex-start;
}
.rs-ack input {
  margin-top: 2px;
  flex: none;
}
.ucheck.is-invalid input {
  outline: 2px solid var(--color-danger);
  outline-offset: 1px;
}
.rs-acts {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: var(--sp-8);
  flex-wrap: wrap;
}
.rs-leave {
  font-size: 12.5px;
  color: var(--color-text-2);
}
.rs-status {
  display: flex;
  flex-direction: column;
  gap: var(--sp-14);
}
.rs-progress {
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  padding: 6px var(--sp-16);
}
.rs-step {
  display: flex;
  gap: var(--sp-12);
  align-items: flex-start;
  padding: var(--sp-10) 0;
  border-bottom: var(--hair-w) solid var(--color-border);
}
.rs-mark {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  flex: none;
  background: var(--color-surface-3);
  color: var(--color-text-2);
  font: 600 12px var(--font-sans);
}
.rs-step.is-done .rs-mark {
  background: var(--color-ok-bg);
  color: var(--color-ok-fg);
}
.rs-step.is-active .rs-mark {
  background: var(--color-accent-bg);
  color: var(--color-accent-fg);
}
.rs-step.is-failed .rs-mark {
  background: var(--color-danger-bg);
  color: var(--color-danger-fg);
}
.rs-step-text {
  min-width: 0;
  padding-top: 2px;
}
.rs-step-label {
  display: block;
  font-weight: 500;
  font-size: 13.5px;
}
.rs-step.is-todo .rs-step-label {
  color: var(--color-text-2);
}
.rs-step-detail {
  display: block;
  margin-top: 1px;
  font-size: 12.5px;
  color: var(--color-text-2);
}

.rp-files .bdg {
  margin-left: 4px;
}

/* restore (#324): the files list (confirmation card, progress card) */
.rs-files > .bk-fl {
  padding: 0 var(--sp-16);
}
.rs-files > .bk-fl > .bk-fl-row:first-child,
.rs-filelist .bk-fl-row:first-child {
  border-top: 0;
}
.rs-files-foot {
  border-top: var(--hair-w) solid var(--color-border);
}
.rs-filelist .bk-fl {
  padding: 0 var(--sp-14) var(--sp-6);
}
.ulist-meta {
  font-size: 12.5px;
  color: var(--color-text-2);
}
.rs-what .rs-steps-text {
  margin-bottom: 2px;
}

/* Replace device (#419-D): the form card fills the wide stack, the side cards are the narrow one */
.rp-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-16);
  padding: 18px;
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
}
.rp-card h2 {
  margin: 0 0 4px;
  font: 600 15px var(--font-sans);
  letter-spacing: normal;
}
.rp-lead {
  margin: 0;
  font-size: 13px;
  color: var(--color-text-2);
  text-wrap: pretty;
}
.rp-card .banner {
  margin: 0;
}
.rp-pick,
.rp-start {
  display: flex;
  flex-direction: column;
  gap: var(--sp-16);
}
.rp-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: var(--sp-12);
  align-items: stretch;
}
.rp-old {
  padding: var(--sp-12);
  border: var(--hair-w) solid var(--color-border);
  border-radius: 9px;
  background: var(--color-surface-2);
}
.rp-old-k {
  margin-bottom: 4px;
  font-size: 12px;
  color: var(--color-text-2);
}
.rp-old-name {
  font-weight: 600;
  overflow-wrap: anywhere;
}
.rp-old-line {
  font: 400 12px var(--font-mono);
  color: var(--color-text-2);
}
.rp-grid select {
  padding: 0 8px;
}
.rp-addr {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 90px;
  gap: var(--sp-8);
  align-content: start;
}
.rp-checks {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.rp-checks .ucheck input {
  accent-color: var(--color-accent);
}
.rp-acts {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: var(--sp-8);
  flex-wrap: wrap;
}
.rp-backup {
  margin-right: auto;
  font-size: 13px;
}
.rp-backup summary {
  cursor: pointer;
  color: var(--color-accent-fg);
}
.rp-backup[open] .ufield {
  margin-top: var(--sp-8);
}
.rp-side {
  display: flex;
  flex-direction: column;
  padding: var(--sp-14) var(--sp-16);
  gap: var(--sp-10);
}
.rp-side h2 {
  margin: 0;
  font: 600 15px var(--font-sans);
  letter-spacing: normal;
}
.rp-side .banner {
  margin: 0;
}
.rp-side .rp-start {
  gap: var(--sp-12);
}
.rp-steps {
  margin: 0;
  padding-left: var(--sp-18);
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
  font-size: var(--fs-body);
}
.rp-old-note {
  margin: 0;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.rp-box {
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: 9px;
  overflow: hidden;
}
.rp-box-h {
  padding: var(--sp-10) var(--sp-14);
  background: var(--color-surface-2);
  font: 600 13px var(--font-sans);
}
.rp-kv {
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr);
  gap: var(--sp-10);
  padding: 7px var(--sp-14);
  border-top: var(--hair-w) solid var(--color-border);
  font-size: 13px;
}
.rp-kv > span:first-child {
  color: var(--color-text-2);
}
.rp-kv .mono {
  font-family: var(--font-mono);
  font-size: 12.5px;
  overflow-wrap: anywhere;
}
.rp-skip {
  padding: 7px var(--sp-14);
  border-top: var(--hair-w) solid var(--color-border);
  font-size: 13px;
}
.rp-skip > span {
  display: block;
}
.rp-skip > span + span {
  font-size: 12px;
  color: var(--color-text-2);
}
.rp-new {
  display: flex;
  flex-direction: column;
  gap: var(--sp-8);
}
.rp-other {
  font-size: 13px;
}
.rp-other summary {
  cursor: pointer;
  color: var(--color-accent-fg);
}
.rp-other[open] .rp-addr {
  margin-top: var(--sp-8);
}

/* ==== page: Policy (/policy, /policy/io, /policy/fix; design v2 N3 + N21): status line,
   conflicts, rule cards, editor, live preview, version history, import / export ==== */
.pol-inline {
  display: contents;
}
.pol-line {
  display: flex;
  align-items: center;
  gap: 6px var(--sp-12);
  flex-wrap: wrap;
  min-height: 46px;
  padding: 6px 6px 6px var(--sp-14);
  margin: -4px 0 var(--sp-14);
  border-radius: 9px;
  border: var(--hair-w) solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text-2);
  font-size: 13px;
}
.pol-line-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
  background: var(--color-ok);
  animation: tf-pulse 2.4s ease-in-out infinite;
}
.pol-line-text {
  flex: 1;
  min-width: 220px;
}
.pol-line-acts {
  display: flex;
  gap: 6px;
}
.pol-line .pol-line-btn {
  height: 32px;
}
.pol-line .pol-line-ghost {
  background: transparent;
}
.pol-line-paused {
  background: var(--color-neutral-bg);
  border-color: var(--color-border-strong);
  color: var(--color-neutral-fg);
}
.pol-line-paused .pol-line-dot {
  background: var(--color-neutral);
  animation: none;
}
.pol-line-failed {
  background: var(--color-danger-bg);
  border-color: var(--color-danger);
  color: var(--color-danger-fg);
}
.pol-line-failed .pol-line-dot {
  background: var(--color-danger);
  animation: none;
}
.pol-confs {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: var(--sp-12);
}
.pol-conf {
  display: flex;
  gap: var(--sp-10);
  align-items: center;
  flex-wrap: wrap;
  padding: var(--sp-10) var(--sp-12);
  border-radius: 8px;
  border: var(--hair-w) solid var(--color-accent);
  background: var(--color-accent-bg);
  color: var(--color-accent-fg);
  font-size: 13px;
}
.pol-conf > span:last-child {
  flex: 1;
  min-width: 200px;
}
.pol-conf-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
  background: var(--color-accent);
}
/* overview: the live-check tile's buttons, the rule table (handle, dragged row, setting column,
   compliance bar beside its numbers) */
.uempty + .pol-tpls {
  margin-top: var(--sp-14);
}
.dev-tile .pol-line-acts {
  margin-top: var(--sp-4);
}
tbody[data-rule-list] > tr.is-drag {
  opacity: 0.4;
}
tbody[data-rule-list] td[data-label="Setting"] {
  max-width: 28ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
tbody[data-rule-list] td[data-label="Compliance"] > div {
  display: flex;
  align-items: center;
  gap: var(--sp-10);
}
tbody[data-rule-list] td[data-label="Compliance"] .health-bar {
  flex: 1 1 0;
  min-width: 4rem;
  margin: 0;
}
tbody[data-rule-list] td[data-label="Compliance"] > div > span {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* the approved design gives the Compliance column 230 px (bar + numbers); Rule, Setting and Applies
   to share the rest and do not wrap at desktop width. On a phone the row is a card. */
@media (min-width: 45rem) {
  tbody[data-rule-list] td[data-label="Compliance"] {
    width: 230px;
  }
}
@media (max-width: 44.99rem) {
  tbody[data-rule-list] td[aria-hidden="true"] {
    display: none;
  }
  /* 40 px touch targets: the row menu and the live-check tile's buttons */
  tbody[data-rule-list] summary.btn-icon {
    min-width: 40px;
    min-height: 40px;
  }
  .dev-tile .pol-line-acts .btn {
    min-height: 40px;
  }
  tbody[data-rule-list] td[data-label="Compliance"] > div > span {
    white-space: normal;
  }
  tbody[data-rule-list] td[data-label="Setting"] {
    max-width: none;
    white-space: normal;
    overflow-wrap: anywhere;
  }
}
/* the empty state: a dashed frame with the four templates */
.pol-empty {
  display: flex;
  flex-direction: column;
  gap: var(--sp-10);
  padding: var(--sp-24);
  border: var(--hair-w) dashed var(--color-border-strong);
  border-radius: 12px;
  background: var(--color-surface);
}
.pol-empty h2 {
  margin: 0 0 4px;
  font: 600 16px var(--font-sans);
}
.pol-empty p {
  margin: 0;
  color: var(--color-text-2);
  font-size: 13.5px;
}
.pol-tpls {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr));
  gap: var(--sp-10);
}
.pol-tpls form {
  display: flex;
  min-width: 0;
}
/* a card-shaped button: undo the fixed control height, the centred nowrap label and the line-height of
   the generic button rule, or the content spills out of the box (#131) */
.pol-tpl {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-start;
  gap: 6px;
  height: auto;
  line-height: 1.4;
  font-weight: 400;
  white-space: normal;
  padding: var(--sp-14);
  border-radius: var(--r-card);
  border: var(--hair-w) solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
a.pol-tpl {
  text-decoration: none;
}
.pol-tpl:hover {
  border-color: var(--color-accent);
}
.pol-tpl-name {
  font-weight: 600;
}
.pol-tpl-kv {
  font: 400 12.5px var(--font-mono);
}
.pol-tpl-text {
  font-size: 12.5px;
  color: var(--color-text-2);
}
.pol-tpl-use {
  margin-top: auto; /* cards of one row stretch to the tallest: the link sits at the bottom of each */
  padding-top: 2px;
  font: 500 13px var(--font-sans);
  color: var(--color-accent-fg);
}
.pol-cards,
.pol-list {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-8);
}
.pol-grip {
  width: 24px;
  flex: none;
  display: grid;
  place-items: center;
  color: var(--color-text-3);
  cursor: grab;
  letter-spacing: -2px;
  font-size: 12px;
}
.pol-sev {
  font-weight: 500;
  color: var(--color-neutral-fg);
}
.pol-sev.is-req {
  color: var(--color-accent-fg);
}
.pol-menu .menu-panel form {
  display: contents;
}
.pol-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-12);
}
.pol-ed-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-8);
}
.pol-ed-head h2 {
  margin: 0;
  font: 600 14px var(--font-sans);
}
.pol-form label.ufield-label {
  font: 500 12.5px var(--font-sans);
  color: var(--color-text-2);
}
.pol-enabled {
  font-weight: 400;
  display: flex;
  align-items: center;
  gap: var(--sp-8);
  min-height: 36px;
  font-size: 13px;
  cursor: pointer;
}
.pol-enabled input {
  width: 16px;
  height: 16px;
  accent-color: var(--color-accent);
}
.pol-two {
  /* one column: the editor is ~360 px wide, two halves cut "Choose a setting…" and the value */
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-10);
}
.pol-form select.mono,
.pol-form input.mono,
#want-control select,
#want-control input {
  font-family: var(--font-mono);
  font-size: 13px;
}
.pol-none {
  font: 400 12.5px var(--font-sans);
  color: var(--color-text-3);
}
/* "Applies to" (#235): an always-open, full-width section of the rule form. Nothing in it changes
   height while the scope is edited: the device list is a box of fixed height. */
.pol-sec {
  display: flex;
  flex-direction: column;
  gap: var(--sp-12);
  padding-top: var(--sp-16);
  border-top: var(--hair-w) solid var(--color-border);
}
.pol-sec-head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--sp-4) var(--sp-12);
}
.pol-sec-head h3 {
  margin: 0;
  font: 600 13.5px var(--font-sans);
}
.pol-sec-sum {
  min-width: 0;
  overflow-wrap: anywhere;
}
.pol-sec-sum b {
  color: var(--color-text);
  font-weight: 600;
}
.pol-scope label.ufield-label {
  display: block;
}
.pol-scope .ufield-label small {
  font-weight: 400;
  color: var(--color-text-3);
}
.pol-scope-cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-16);
  align-items: start;
}
@media (min-width: 52rem) {
  .pol-scope-cols {
    grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
  }
}
.pol-pills {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-6);
}
.pol-chips {
  min-height: 28px;
}
/* a pill is a checkbox you can see as a toggle; a ticked tag looks like a chip with a cross */
.pol-pill {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px;
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: 999px;
  background: var(--color-surface);
  color: var(--color-text-2);
  font: 500 12.5px var(--font-sans);
  cursor: pointer;
}
.pol-pill input {
  position: absolute;
  opacity: 0;
  inset: 0;
  margin: 0;
  cursor: pointer;
}
.pol-pill:has(input:checked) {
  background: var(--color-accent-bg);
  border-color: var(--color-accent);
  color: var(--color-text);
}
.pol-pill:has(input:focus-visible) {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
.pol-pill-n::before {
  content: "· ";
}
.pol-pill-n {
  color: var(--color-text-3);
  font-variant-numeric: tabular-nums;
}
.pol-tag .pol-chip-x,
.pol-exc .pol-chip-x {
  display: none;
}
.pol-tag:has(input:checked) .pol-pill-n {
  display: none;
}
.pol-tag:has(input:checked) .pol-chip-x,
.pol-exc:has(input:checked) .pol-chip-x {
  display: inline;
  color: var(--color-text-2);
}
.pol-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 4px 5px 11px;
  border: var(--hair-w) solid var(--color-accent);
  border-radius: 999px;
  background: var(--color-accent-bg);
  color: var(--color-text);
  font: 500 12.5px var(--font-sans);
}
.pol-exc:has(input:checked) {
  border-color: var(--color-warn);
  background: var(--color-warn-bg);
}
button.pol-chip-x {
  padding: 0 6px;
  line-height: 1;
  border: 0;
  background: none;
  color: var(--color-text-2);
  font: 500 14px var(--font-sans);
  cursor: pointer;
}
.pol-form .pol-except input:not([type="hidden"]) {
  flex: 0 1 280px;
  width: auto;
  min-width: 0;
}
.pol-results {
  box-sizing: border-box;
  height: 236px;
  overflow: auto;
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-control);
  background: var(--color-surface);
}
.pol-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 11px;
  border-bottom: var(--hair-w) solid var(--color-border);
  font: 400 13px var(--font-sans);
  cursor: pointer;
}
.pol-row[hidden] {
  display: none;
}
.pol-row:last-of-type {
  border-bottom: 0;
}
.pol-row-meta {
  margin-left: auto;
  color: var(--color-text-3);
  font-size: 11.5px;
  text-align: right;
}
.pol-row-none {
  margin: 0;
  padding: var(--sp-12);
  color: var(--color-text-3);
  font-size: 12.5px;
}
.pol-pickc {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  font: 400 13px var(--font-sans);
  color: var(--color-text);
}
.pol-ed-foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-8);
  padding-top: var(--sp-12);
  border-top: var(--hair-w) solid var(--color-border);
}
/* the live preview */
.pol-preview {
  padding: 0;
  gap: 0;
  overflow: hidden;
}
/* the version history */
.pol-hist {
  grid-column: 1 / -1;
  overflow: hidden;
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  background: var(--color-surface);
}
.pol-hist-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-8);
  flex-wrap: wrap;
  padding: var(--sp-12) var(--sp-16);
  border-bottom: var(--hair-w) solid var(--color-border);
}
.pol-hist-head h2 {
  margin: 0;
  font: 600 14px var(--font-sans);
}
.pol-hist-head span {
  font-size: 12.5px;
  color: var(--color-text-2);
}
.pol-hist-body {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
}
.pol-hist-list {
  flex: 1 1 260px;
  border-right: var(--hair-w) solid var(--color-border);
}
.pol-hv {
  display: flex;
  gap: var(--sp-10);
  align-items: flex-start;
  min-height: 52px;
  padding: var(--sp-10) var(--sp-16);
  border-bottom: var(--hair-w) solid var(--color-border);
  color: var(--color-text);
  text-decoration: none;
}
.pol-hv.is-sel {
  background: var(--color-accent-bg);
  box-shadow: inset 3px 0 0 var(--color-accent);
}
.pol-hv-v {
  width: 26px;
  flex: none;
  font: 600 12.5px var(--font-mono);
}
.pol-hv-main {
  min-width: 0;
}
.pol-hv-what {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap;
  font-size: 13px;
}
.pol-hv-meta {
  display: block;
  font-size: 12px;
  color: var(--color-text-2);
}
.pol-hist-diff {
  flex: 2 1 360px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-10);
  padding: var(--sp-12) var(--sp-16);
}
.pol-hist-diff .pol-none {
  font-size: 13px;
  color: var(--color-text-2);
}
.pol-diff-h {
  font: 600 13px var(--font-sans);
}
.pol-diff {
  align-self: stretch;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: var(--hair-w) solid var(--color-border);
  border-radius: 8px;
}
.pol-diff-row {
  display: grid;
  grid-template-columns: 120px minmax(0, 1fr);
  gap: 4px var(--sp-12);
  padding: var(--sp-8) var(--sp-12);
  border-bottom: var(--hair-w) solid var(--color-border);
  font-size: 12.5px;
}
.pol-diff-row:last-child {
  border-bottom: 0;
}
.pol-diff-l {
  color: var(--color-text-2);
  word-break: break-word;
}
.pol-diff-v {
  font-family: var(--font-mono);
  overflow-wrap: anywhere;
}
/* import / export */
.pio-export {
  display: flex;
  flex-direction: column;
  gap: var(--sp-10);
  max-width: 860px;
  margin-top: var(--sp-16);
}
.pio-acts {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-8);
}
.pio-json {
  margin: 0;
  max-height: 520px;
  overflow: auto;
  padding: var(--sp-16);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  background: var(--color-surface);
  font: 400 12.5px / 1.55 var(--font-mono);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.pio-import {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr));
  gap: var(--sp-14);
  align-items: start;
  margin-top: var(--sp-16);
}
.pio-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-10);
}
.pio-form textarea {
  min-height: 290px;
  font: 400 12.5px / 1.55 var(--font-mono);
}
.pio-preview {
  overflow: hidden;
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  background: var(--color-surface);
}
.pio-pv-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-8);
  flex-wrap: wrap;
  padding: var(--sp-12) var(--sp-16);
  border-bottom: var(--hair-w) solid var(--color-border);
}
.pio-pv-head h2 {
  margin: 0;
  font: 600 14px var(--font-sans);
}
.pio-counts {
  display: flex;
  gap: 6px;
}
.pio-none {
  margin: 0;
  padding: var(--sp-16);
  font-size: 13px;
  color: var(--color-text-2);
}
.pio-row {
  display: flex;
  gap: var(--sp-12);
  align-items: flex-start;
  padding: var(--sp-10) var(--sp-16);
  border-bottom: var(--hair-w) solid var(--color-border);
  font-size: 13px;
}
.pio-row .bdg {
  flex: none;
  width: 56px;
  justify-content: center;
}
.pio-row-t {
  min-width: 0;
}
.pio-row-d {
  display: block;
  font: 400 12px var(--font-mono);
  color: var(--color-text-2);
  word-break: break-word;
}
.pio-foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-12);
  flex-wrap: wrap;
  padding: var(--sp-12) var(--sp-16);
  font-size: 12.5px;
  color: var(--color-text-2);
}
/* Make compliant as a page (no JavaScript) and the started page */
.pol-fix-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 380px), 1fr));
  gap: var(--sp-14);
}
.pol-fix-card h2 a {
  color: inherit;
}
.pol-fix-cmd {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 13px;
}
.pol-fix-note,
.pol-fix-skip {
  margin: 0;
  font-size: 12px;
  color: var(--color-text-2);
}
.pol-fix-foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-8);
  margin-top: var(--sp-16);
}
.pol-fix-left {
  margin: 0;
  padding-left: var(--sp-18);
  font-size: 13px;
}
@media (max-width: 44.99rem) {
  /* the status line's 32 px buttons get a 40 px touch target (e2e smallTargets) */
  .pol-line .pol-line-btn {
    position: relative;
  }
  .pol-line .pol-line-btn::after {
    content: "";
    position: absolute;
    inset: -4px 0;
  }
  .pol-two {
    grid-template-columns: minmax(0, 1fr);
  }
  .pol-hist-list {
    border-right: 0;
    border-bottom: var(--hair-w) solid var(--color-border);
  }
}

/* ==== page: user detail, one-time password, setup step 1 (design v2 N4 + N12, WP W2) ==== */
/* user detail: avatar, name with badges and the "username · method" line */
.ud-head {
  display: flex;
  align-items: center;
  gap: var(--sp-12);
  margin-bottom: var(--sp-16);
}
.ud-head .uavatar {
  width: 44px;
  height: 44px;
  font-size: 14px;
  background: var(--color-surface-3);
  color: var(--color-text-2);
}
.ud-who {
  min-width: 0;
}
.ud-who h1 {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-8);
  margin: 0;
  font: 600 22px/1.2 var(--font-sans);
  letter-spacing: -0.015em;
  overflow-wrap: anywhere;
}
.ud-who h1 .bdg {
  letter-spacing: normal;
}
.ud-sub {
  margin: 4px 0 0;
  font-size: 13px;
  color: var(--color-text-2);
  overflow-wrap: anywhere;
}
.ud-acts {
  gap: var(--sp-6);
  margin-bottom: var(--sp-8);
}
.ud-acts .btn:disabled {
  color: var(--color-text-3);
}
.ud-acts .btn.ud-danger:not(:disabled) {
  color: var(--color-danger-fg);
}
.ud-why {
  margin: 0 0 var(--sp-14);
  font-size: 12.5px;
  color: var(--color-text-2);
}
.ud-acts + .banner,
.ud-acts + .ud-grid {
  margin-top: var(--sp-6);
}
.banner.tone-neutral {
  --banner-line: var(--color-border-strong);
  --banner-bg: var(--color-surface-3);
  --banner-fg: var(--color-text-2);
}
.banner.ud-off b {
  color: var(--color-text-2);
}
.banner.ud-off .banner-text {
  flex: 1;
}
/* three cards side by side, stacked on a phone */
.ud-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-14);
  align-items: start;
}
.ud-details {
  gap: var(--sp-6);
}
.ud-details > h2 {
  margin: 0;
}
.ud-activity .ulist-head {
  padding-block: var(--sp-6);
}
.ud-details .ukv-row {
  grid-template-columns: 120px minmax(0, 1fr);
  padding: var(--sp-6) 0;
}
.ud-details details.fold {
  margin: 0;
}
.ud-details details.fold > summary {
  font-weight: 500;
  font-size: 13px;
}
.ud-edit .acct-form {
  margin: var(--sp-6) 0 var(--sp-10);
}
.ud-edit .acct-form .btn {
  align-self: flex-end;
}
.ud-none {
  margin: 0;
  padding: var(--sp-14);
  font-size: 13px;
  color: var(--color-text-2);
}
.ud-row {
  display: flex;
  align-items: center;
  gap: var(--sp-10);
  padding: var(--sp-8) var(--sp-8) var(--sp-8) var(--sp-16);
  border-top: var(--hair-w) solid var(--color-border);
}
.ulist-head + .ud-row {
  border-top: 0;
}
.ud-row-t {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.ud-row-main {
  font-size: 13px;
  font-weight: 500;
}
.ud-row-sub {
  font: 400 12px var(--font-mono);
  color: var(--color-text-2);
  overflow-wrap: anywhere;
}
.ud-end {
  margin: 0;
  flex: none;
}
.ud-act {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-6) var(--sp-12);
  padding: var(--sp-10) var(--sp-16);
  border-top: var(--hair-w) solid var(--color-border);
  font-size: 12.5px;
}
.ulist-head + .ud-act {
  border-top: 0;
}
.ud-act-when {
  font: 400 12.5px var(--font-mono);
  color: var(--color-text-2);
  white-space: nowrap;
}
.ud-act-what {
  font: 500 12.5px var(--font-mono);
}
.ud-act-sum {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--color-text-2);
  overflow-wrap: anywhere;
}
@media (max-width: 44.99rem) {
  .ud-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}
/* one-time password: the value with Copy, username and expiry, the warning, Done */
.otp-card {
  max-width: 560px;
  gap: var(--sp-12);
  padding: var(--sp-18);
}
.otp-value {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-8);
}
.otp-value output {
  flex: 1 1 260px;
  min-width: 0;
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: 0 var(--sp-14);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-control);
  background: var(--color-surface-2);
  font: 500 18px var(--font-mono);
  letter-spacing: 0.02em;
  overflow-wrap: anywhere;
}
.otp-value .btn {
  min-height: 48px;
  padding: 0 var(--sp-24);
}
.otp-card .ukv-row {
  border-top: 0;
  padding: var(--sp-2) 0;
}
.otp-foot {
  display: flex;
  justify-content: flex-end;
  max-width: 560px;
  margin-top: var(--sp-14);
}
/* setup step 1: the card as wide as the wizard's, the log example */
.page-auth .auth-card:has(.setup-steps) {
  width: min(480px, 100%);
}
.setup-log {
  margin: 0;
  padding: var(--sp-10) var(--sp-12);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-control);
  background: var(--color-surface-2);
  font: 400 12px/1.6 var(--font-mono);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.setup-log + .banner {
  margin: 0;
}

/* ==== page: What's new (/whats-new, N5) and the avatar's news dot (W3) ==== */
.avatar-dot {
  position: absolute;
  top: -2px;
  right: -2px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-accent);
  border: 1.5px solid var(--color-surface);
}
.um-menu .um-news {
  justify-content: space-between;
  gap: var(--sp-8);
}
.wn-rel {
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-card);
  padding: var(--sp-16) var(--sp-18) var(--sp-10);
  min-width: 0;
}
.wn-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px var(--sp-10);
  padding-bottom: var(--sp-10);
}
.wn-v {
  margin: 0;
  font: 700 16px var(--font-mono);
  letter-spacing: normal;
}
.wn-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--color-text-2);
}
.wn-d {
  font-size: 12.5px;
  color: var(--color-text-2);
}
.wn-intro p {
  margin: 0 0 var(--sp-8);
  font-size: 13.5px;
}
.wn-sec {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-10);
  padding: var(--sp-8) 0;
  border-top: var(--hair-w) solid var(--color-border);
  font-size: 13.5px;
  line-height: 22px;
}
/* the label column is at least 72 px and grows with its title up to 120 px; a longer title wraps inside
   the badge instead of running out of it over the list ("Upgrade notes: action may be required", #510) */
.wn-lbl {
  flex: none;
  min-width: 72px;
  max-width: 120px;
  padding-top: 2px;
}
.wn-lbl .bdg {
  box-sizing: border-box;
  min-width: 72px;
  max-width: 100%;
  min-height: 18px;
  height: auto;
  padding-top: 1px;
  padding-bottom: 1px;
  justify-content: center;
  text-align: center;
  white-space: normal;
  line-height: 16px;
}
.wn-sec ul {
  margin: 0;
  padding-left: var(--sp-18);
  min-width: 0;
  overflow-wrap: anywhere;
}
.wn-sec ul ul {
  padding-left: var(--sp-16);
}
/* #419-S: the releases fill the wide stack; the narrow stack (running version, jump list) stays in
   view from 75rem, below the sticky header */
.wn-rel {
  scroll-margin-top: 110px;
}
.wn-you-sub {
  margin: 0;
  color: var(--color-text-2);
}
.wn-jump-l {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-6) var(--sp-12);
  font: 500 13px var(--font-mono);
}
.wn-older {
  margin-top: var(--sp-6);
}
.wn-older > summary {
  cursor: pointer;
  color: var(--color-text-2);
  font-size: var(--fs-meta);
}
/* the gap below the summary belongs to the open list: a collapsed details is as tall as its summary (#633) */
.wn-older[open] > summary {
  margin-bottom: var(--sp-8);
}
@media (min-width: 75rem) {
  .wn-stacks .cstacks > .cstack:last-child {
    position: sticky;
    top: 110px;
  }
}
/* two half columns (tablet) would set 0.28.0 beside 0.11.x and put the running version after the
   last release: one column, the narrow stack's cards first */
@media (max-width: 74.99rem) {
  .wn-stacks .cstacks {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    columns: auto;
    gap: var(--sp-16);
  }
  .wn-stacks .cstack > * {
    margin: 0;
  }
  .wn-stacks .wn-you {
    order: 1;
  }
  .wn-stacks .wn-jump {
    order: 2;
  }
  .wn-stacks .wn-rel {
    order: 3;
  }
}

/* ==== page: release (/firmware/releases/{version}, N6): notes, files aside, "Changes since" ==== */
.rel-title {
  display: flex;
  align-items: center;
  gap: var(--sp-10);
}
.page-release .pghead {
  align-items: flex-end;
}
.rel-notes {
  padding: var(--sp-20) var(--sp-22);
  display: flex;
  flex-direction: column;
  gap: var(--sp-12);
  font-size: 14px;
  line-height: 1.6;
  min-width: 0;
  overflow-wrap: anywhere;
}
.rel-notes p,
.rel-notes ul,
.rel-notes ol,
.rel-notes blockquote {
  margin: 0;
}
.rel-notes ul,
.rel-notes ol {
  padding-left: var(--sp-20);
}
.rel-notes h4 {
  margin: var(--sp-4) 0 -6px;
  font: 600 15px var(--font-sans);
}
.rel-notes pre {
  margin: 0;
  padding: var(--sp-12) var(--sp-14);
  border-radius: var(--r-code);
  font: 400 12.5px/1.6 var(--font-mono);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.rel-breaking h4 {
  display: none;
}
.cstack > .rel-files,
.cstack > .rel-older {
  padding: 0 var(--sp-16) var(--sp-14);
}
.cstack > .rel-files .ulist-head,
.cstack > .rel-older .ulist-head {
  padding: var(--sp-14) 0 var(--sp-6);
}
.cstack > .rel-files .ulist-head form,
.cstack > .rel-older .ulist-head form {
  margin: 0;
}
.cstack > .rel-files .ulist-head .btn-link,
.cstack > .rel-older .ulist-head .btn-link {
  min-height: 0;
  font-size: 12.5px;
}
.rel-file {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-10);
  padding: var(--sp-10) 0;
  border-top: var(--hair-w) solid var(--color-border);
}
.rel-file:first-of-type {
  border-top: 0;
}
.rel-file-t {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.rel-file-n {
  font: 500 12.5px var(--font-mono);
  overflow-wrap: anywhere;
}
.rel-file-m {
  font-size: 11.5px;
  color: var(--color-text-2);
}
.rel-orow {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-10);
  padding: var(--sp-6) 0;
  border-top: var(--hair-w) solid var(--color-border);
  font-size: 12.5px;
  color: var(--color-text-2);
}
.rel-orow .mono {
  color: var(--color-text);
  font-size: 12.5px;
}
.rel-start {
  margin-top: var(--sp-8);
}
.rel-start .btn {
  width: 100%;
  justify-content: center;
}
.rel-empty {
  margin: var(--sp-8) 0 0;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.rel-since-lead {
  margin: 0 0 var(--sp-12);
  font-size: 13px;
  color: var(--color-text-2);
}
.rel-since-lead b {
  color: var(--color-text);
}
.rel-since {
  display: flex;
  flex-direction: column;
  gap: var(--sp-10);
  max-width: 900px;
}
.rel-srel {
  padding: var(--sp-14) var(--sp-16);
  gap: 0;
}
.rel-shead {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-10);
  margin-bottom: 6px;
}
.rel-sv {
  font: 700 14px var(--font-mono);
}
.rel-sd {
  font-size: 12.5px;
  color: var(--color-text-2);
}
.rel-srow {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-10);
  padding: 2px 0;
  font-size: 13px;
  line-height: 21px;
}
.rel-slbl {
  flex: none;
  width: 68px;
  padding-top: 2px;
}
.rel-slbl .bdg {
  display: block;
  width: 68px;
  height: 17px;
  line-height: 17px;
  text-align: center;
  font-size: 11px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rel-stxt {
  min-width: 0;
  overflow-wrap: anywhere;
}
.fw-rel-links a {
  color: inherit;
  white-space: nowrap;
}
@media (max-width: 44.99rem) {
  /* a 40 px touch target without moving the line, like the crumbs */
  .fw-rel-links a {
    display: inline-block;
    padding: 11px 0;
    margin: -11px 0;
  }
  .rel-notes {
    padding: var(--sp-18) var(--sp-22);
  }
  .wn-rel {
    padding: var(--sp-14) var(--sp-18) var(--sp-8);
  }
}

/* rule page and New rule page (#197): the pairs of the Rule card, the 55/45 and 62/38 columns,
   the footer row, the template grid */
.pol-page .dev-pair {
  align-items: start;
}
/* .devlive spaces its children with a gap: the pair's own bottom margin would double it above the history */
.pol-page .devlive > .dev-pair {
  margin-bottom: 0;
}
@media (min-width: 45rem) {
  .pol-page .dev-pair {
    grid-template-columns: minmax(0, 11fr) minmax(0, 9fr);
  }
  /* the editor and the devices card sit side by side only when both have room (the mockup wraps
     them the same way); until then the Rule card takes the full width */
  .pol-page .dev-pair:has(#rule-form) {
    grid-template-columns: minmax(0, 1fr);
  }
  .pol-page .dev-pair:has(.pol-cards) {
    grid-template-columns: minmax(0, 62fr) minmax(0, 38fr);
  }
  .devcard .pol-two {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: start;
  }
}
@media (min-width: 84rem) {
  .pol-page .dev-pair:has(#rule-form) {
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  }
}
.devcard .pol-ed-foot {
  flex-wrap: wrap;
  align-items: center;
}
.devcard .pol-ed-foot .ufield {
  flex: 1 1 14rem;
}
.dev-pair .pol-tpls {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
@media (max-width: 63.99rem) {
  .dev-pair .pol-tpls {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 44.99rem) {
  .dev-pair .pol-tpls {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ==== Policy: Deviating devices tab (/policy/deviations) ==== */
.pol-dev-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-8) var(--sp-12);
  margin: var(--sp-16) 0 var(--sp-12);
}
.pol-dev-bar .dv-chips {
  flex: 1 1 auto;
}
.pol-dev-q input {
  width: 14rem;
  max-width: 100%;
}
.pol-dev-v {
  display: block;
  max-width: 24rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pol-dev-bad {
  color: var(--color-warn-fg);
}
tr.pol-grp td {
  background: var(--color-surface-2);
  font-size: var(--fs-small);
  color: var(--color-text-2);
}
.pol-dev-fold {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-8) var(--sp-12);
  padding: var(--sp-8) 0;
}
.pol-dev-fold .pol-none {
  flex: 1 1 100%;
  margin: 0;
}
.pol-fix-adv {
  margin: var(--sp-8) 0 0;
  padding: var(--sp-8) 0 0;
  border: 0;
  border-top: var(--hair-w) solid var(--color-border);
  font-size: 12.5px;
}
.pol-fix-adv legend {
  padding: 0;
  font-weight: 600;
}
@media (max-width: 44.99rem) {
  .pol-dev-q,
  .pol-dev-q input {
    width: 100%;
  }
  .pol-dev-bar .btn-primary {
    width: 100%;
  }
  #deviations td.p1 .bdg {
    flex: none;
    white-space: nowrap;
  }
  table.dense tr.pol-grp {
    padding-left: 0 !important;
    background: var(--color-surface-2);
  }
  .pol-dev-v {
    max-width: none;
    white-space: normal;
    overflow-wrap: anywhere;
  }
}
/* policy history tab: day headers, folded quick saves, notes */
.pol-hday {
  padding: var(--sp-8) var(--sp-16);
  border-bottom: var(--hair-w) solid var(--color-border);
  font-size: 12px;
  color: var(--color-text-2);
}
.pol-hfold > summary {
  cursor: pointer;
  list-style: none;
}
.pol-hfold > summary::-webkit-details-marker {
  display: none;
}
.pol-hfold.is-sel > summary {
  background: var(--color-accent-bg);
}
.pol-hsub {
  padding-left: var(--sp-32);
}
.pol-hnote {
  color: var(--color-text-2);
  overflow-wrap: anywhere;
}
.pol-hdiff-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-12);
  flex-wrap: wrap;
  align-self: stretch;
}
.pol-hpage {
  display: flex;
  gap: var(--sp-8);
  padding: var(--sp-12) var(--sp-16);
}
.pol-hfold > summary .pol-hv-v {
  width: auto;
  min-width: 26px;
  white-space: nowrap;
}
details.devcard > summary {
  display: flex;
  align-items: center;
  min-height: 40px;
  cursor: pointer;
  font: 600 14px var(--font-sans);
}

/* Identity card (#289): the StatusFWR block and TasmoFleet's own reading under it. */
.ukv-h {
  margin: var(--sp-12) 0 var(--sp-4);
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--color-text-2);
}
.ukv-sub {
  font-weight: 400;
  margin-left: var(--sp-4);
}

/* Command field of the rule editor: suggestions, index box, help, fleet check (#269) */
.cmd-sug {
  position: absolute;
  z-index: 20;
  top: calc(var(--ctl) + 22px);
  left: 0;
  width: min(520px, calc(100vw - 64px));
  background: var(--color-surface);
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: var(--r-control);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.cmd-sug[hidden] {
  display: none;
}
.cmd-idx {
  display: flex;
  align-items: stretch;
  height: var(--ctl);
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: var(--r-control);
  background: var(--color-surface);
  overflow: hidden;
}
.cmd-check {
  margin: 0;
  align-items: flex-start;
}
.cmd-rel {
  position: relative;
}
.cmd-lrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.cmd-kind {
  font: 500 11px var(--font-mono);
  color: var(--color-text-3);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--r-badge);
  padding: 0 6px;
}
.cmd-help {
  display: block;
  margin: 0;
}
.cmd-doc {
  color: var(--color-accent-fg);
  white-space: nowrap;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.cmd-doc svg {
  margin-left: 3px;
  vertical-align: baseline;
}
.cmd-sug-g {
  font: 600 11px var(--font-sans);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-3);
  padding: 8px 12px 4px;
}
.cmd-opt {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 1px 12px;
  padding: 7px 12px;
  cursor: pointer;
}
.cmd-opt.on {
  background: var(--color-accent-bg);
}
.cmd-opt-n {
  font-size: 13px;
  font-weight: 600;
  color: var(--color-text);
}
.cmd-opt-n i {
  font-style: normal;
  color: var(--color-text-3);
  font-weight: 400;
}
.cmd-opt-r {
  font-size: 11.5px;
  color: var(--color-text-2);
  text-align: right;
  align-self: center;
}
.cmd-opt-r.mono {
  background: var(--color-surface-3);
  border-radius: var(--r-badge);
  padding: 0 6px;
}
.cmd-opt-d {
  grid-column: 1/-1;
  font-size: 12px;
  color: var(--color-text-2);
}
.cmd-sug-f {
  border-top: var(--hair-w) solid var(--color-border);
  padding: 6px 12px;
  font-size: 11.5px;
  color: var(--color-text-3);
  background: var(--color-surface-2);
}
.cmd-sug-f kbd {
  font: 500 10.5px var(--font-mono);
  border: 1px solid var(--color-border-strong);
  border-radius: 4px;
  padding: 0 4px;
  margin: 0 2px;
  background: var(--color-surface);
}
.cmd-idx.bad {
  border-color: var(--color-danger);
}
.cmd-idx-base {
  display: flex;
  align-items: center;
  padding: 0 8px 0 10px;
  background: var(--color-surface-2);
  border-right: var(--hair-w) solid var(--color-border);
  font-size: 13px;
  color: var(--color-text-2);
}
.cmd-idx input {
  width: 64px;
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  background: transparent !important;
  height: calc(100% + 2px) !important;
  margin: -1px 0 !important;
  padding: 0 8px !important;
  min-width: 0;
}
.cmd-idx-range {
  display: flex;
  align-items: center;
  font-size: 11.5px;
  color: var(--color-text-3);
  white-space: nowrap;
}
.cmd-idx-x {
  margin-left: auto;
  border: 0;
  background: none;
  color: var(--color-accent-fg);
  font: 500 12px var(--font-sans);
  padding: 0 10px;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.cmd-check .banner-dot {
  margin-top: 5px;
}
.cmd-vers {
  list-style: none;
  margin: 6px 0 4px;
  padding: 0;
  display: grid;
  gap: 3px;
}
.cmd-v {
  display: grid;
  grid-template-columns: 64px 112px minmax(0, 1fr);
  gap: 10px;
  align-items: center;
  font-size: 12.5px;
}
.cmd-vr {
  display: flex;
  gap: 6px;
  align-items: center;
}
.cmd-i {
  flex: none;
}
.cmd-i.ok {
  color: var(--color-ok-fg);
}
.cmd-i.no {
  color: var(--color-danger-fg);
}
.cmd-v.no .cmd-vr {
  font-weight: 500;
}
.cmd-foot {
  display: block;
  font-size: 12px;
  opacity: 0.9;
}
@media (max-width: 719px) {
  .cmd-v {
    grid-template-columns: 56px minmax(0, 1fr);
  }
  .cmd-vr {
    grid-column: 1/-1;
    padding-left: 0;
  }
  .cmd-sug {
    top: calc(var(--ctl) + 40px);
  }
}

.sys-pre {
  margin: 0;
  padding: var(--sp-10) var(--sp-12);
  border: var(--hair-w) solid var(--color-border);
  border-radius: var(--r-control);
  background: var(--color-surface-2);
  font-family: var(--font-mono);
  font-size: 12px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
/* Online + Boot loop / Weak Wi-Fi + a policy deviation does not fit the 170 px Status column on one line:
   the badges wrap instead of being cut (#325). */
.devtable td.st-wrap {
  white-space: normal;
}

/* Stability card of the device Overview (#325): badge, notice, numbers, sparkline, recent restarts. */
.stab-head {
  display: flex;
  align-items: center;
  gap: var(--sp-10);
}
.stab-head h2 {
  margin: 0;
}
.stab-meta {
  margin-left: auto;
  font-size: var(--fs-meta);
  color: var(--color-text-2);
}
.stab-note {
  margin: var(--sp-10) 0 var(--sp-8);
  padding: var(--sp-8) var(--sp-12);
  border-radius: var(--r-badge, 6px);
  background: var(--t-bg);
  color: var(--t-fg);
  font-size: 13px;
  line-height: 1.45;
}
.stab-card .ukv {
  margin-top: var(--sp-10);
}
.stab-card .ukv-row .sub {
  display: block;
  color: var(--color-text-2);
  font-size: 12px;
}
.stab-card .num-bad {
  color: var(--color-danger-fg);
  font-weight: 600;
}
.stab-card .num-warn {
  color: var(--color-warn-fg);
  font-weight: 600;
}
.stab-spark {
  display: block;
  width: 100%;
  height: 28px;
}
.stab-spark .line {
  fill: none;
  stroke: var(--t);
  stroke-width: 1.6;
  vector-effect: non-scaling-stroke;
}
.stab-spark .ref {
  stroke: var(--color-border-strong);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  vector-effect: non-scaling-stroke;
}
.stab-boots {
  list-style: none;
  margin: var(--sp-4) 0 0;
  padding: 0;
  font-size: 13px;
}
.stab-boots li {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr) auto;
  gap: var(--sp-10);
  padding: var(--sp-6) 0;
  border-top: var(--hair-w) solid var(--color-border);
  align-items: baseline;
}
.stab-boots .when {
  color: var(--color-text-2);
  font: 400 12.5px var(--font-mono);
}
.stab-boots .ran {
  color: var(--color-text-2);
  font-size: 12.5px;
  white-space: nowrap;
}
.stab-boots .raw {
  color: var(--color-text-3);
  font: 400 11.5px var(--font-mono);
}
@media (max-width: 640px) {
  .stab-boots li {
    grid-template-columns: 78px minmax(0, 1fr);
  }
  .stab-boots .ran {
    grid-column: 2;
  }
}

/* GPIO templates tab, preview and transaction (#327) */
/* tab bar: summary, when read, Read again */
.gt-bar .gt-sum {
  flex: 1 1 auto;
  margin: 0;
  font-size: 13px;
  font-weight: 600;
}
/* the line that says how old the values shown from memory are (#599): settings tab, GPIO templates */
.agebar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-6) var(--sp-8);
  max-width: 760px;
  margin: 0 0 var(--sp-12);
  font-size: 12.5px;
  color: var(--color-text-2);
}
.agebar-warn {
  color: var(--color-warn-fg);
}
.agebar.is-failed {
  color: var(--color-danger-fg);
}
.agebar[aria-busy="true"]::after {
  content: "";
  flex: none;
  width: 0.85em;
  height: 0.85em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: ds-spin 0.8s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .agebar[aria-busy="true"]::after {
    animation: none;
  }
}
.gt-read {
  font-size: 12.5px;
  color: var(--color-text-2);
}
.gt-intro {
  margin: 0 0 var(--sp-16);
  font-size: 12.5px;
  color: var(--color-text-2);
}

/* group card */
.gt-card .sch h2 {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-6);
}
.gt-n {
  font-weight: 400;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.gt-choose {
  color: var(--color-warn-fg);
}
.gt-rows {
  list-style: none;
  margin: 0;
  padding: 0;
}
.gt-row {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  gap: var(--sp-4);
  padding: var(--sp-8) 0;
  border-top: var(--hair-w) solid var(--color-border);
}
.gt-row:first-child {
  border-top: 0;
  padding-top: 0;
}
/* the group's reference (#547): always the first row, tinted so it reads as the yardstick of the
   others; the tint reaches a little past the text, the row after it needs no rule above it */
.gt-row.is-ref {
  margin-inline: calc(-1 * var(--sp-8));
  padding-inline: var(--sp-8);
  border-radius: var(--r-banner);
  background: var(--color-accent-bg);
}
.gt-row.is-ref:first-child {
  padding-top: var(--sp-8);
}
.gt-row.is-ref + .gt-row {
  border-top: 0;
}
.gt-sel {
  display: flex;
  align-items: flex-start;
  padding-top: 2px;
}
.gt-main {
  min-width: 0;
}
.gt-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4) var(--sp-8);
  font-size: 13px;
}
.gt-name {
  font-weight: 600;
  color: var(--color-text);
}
.gt-meta {
  font-size: 12px;
  color: var(--color-text-2);
}
.gt-act {
  margin-left: auto;
  font-size: 12.5px;
  white-space: nowrap;
}
.gt-diff {
  list-style: none;
  margin: var(--sp-6) 0 0;
  padding: 0;
  font-size: 12.5px;
}
.gt-diff li + li {
  margin-top: var(--sp-2);
}
.gt-pin {
  display: inline-block;
  min-width: 4.5em;
  font: 600 12px var(--font-mono);
}
.gt-want {
  color: var(--color-ok-fg);
  font-weight: 600;
}
.gt-why {
  margin: var(--sp-4) 0 0;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.gt-fine {
  margin: var(--sp-6) 0 0;
  font-size: 12px;
  color: var(--color-text-2);
}
.gt-foot {
  margin-top: var(--sp-10);
}

/* pin table inside a row */
.gt-pins {
  margin-top: var(--sp-8);
}
.gt-pins > summary {
  cursor: pointer;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.gt-ptab {
  width: 100%;
  margin-top: var(--sp-6);
  border-collapse: collapse;
  font: 400 12px var(--font-mono);
}
.gt-ptab th,
.gt-ptab td {
  padding: 3px var(--sp-8);
  text-align: left;
  border-top: var(--hair-w) solid var(--color-border);
}
.gt-ptab thead th {
  font: 600 11.5px var(--font-sans);
  color: var(--color-text-2);
  border-top: 0;
}
.gt-ptab tr.gt-bad td,
.gt-ptab tr.gt-bad th {
  background: var(--color-warn-bg);
  color: var(--color-warn-fg);
  font-weight: 600;
}

/* plain lists: Not judged, Built-in modules, one device, Not included */
.gt-list {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 13px;
}
.gt-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-8);
  padding: var(--sp-6) 0;
  border-top: var(--hair-w) solid var(--color-border);
}
.gt-list li:first-child {
  border-top: 0;
  padding-top: 0;
}
.gt-list .gt-why {
  margin: 0;
  flex: 1 1 14rem;
}
.gt-acts {
  margin-top: var(--sp-10);
}
/* a card that is only a fold */
.gt-fold > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  cursor: pointer;
  list-style: none;
  font-size: 14px;
}
.gt-fold > summary::-webkit-details-marker {
  display: none;
}
.gt-fold > summary::after {
  content: "";
  width: 6px;
  height: 6px;
  margin: 0 4px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
}
.gt-fold[open] > summary::after {
  transform: translateY(2px) rotate(-135deg);
}
.gt-fold-in {
  margin-top: var(--sp-12);
}

/* preview */
.gt-cmd {
  margin: var(--sp-4) 0 0;
  padding: var(--sp-8) var(--sp-10);
  border-radius: var(--r-code);
  background: var(--color-surface-2);
  font: 400 12px/1.5 var(--font-mono);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.gt-card .sys-subhead {
  margin: 0 0 var(--sp-4);
}
.gt-restart {
  margin: 0;
  padding: var(--sp-8) var(--sp-10);
  border-radius: var(--r-code);
  background: var(--color-warn-bg);
  color: var(--color-warn-fg);
  font-size: 12.5px;
}
.gt-steps {
  margin: 0;
  padding-left: 20px;
  font-size: 13px;
}
.gt-steps li + li {
  margin-top: var(--sp-6);
}
.gt-ptab-2 {
  margin-top: 0;
}
.gt-foot-form {
  flex-wrap: wrap;
  align-items: center;
}
.gt-confirm {
  display: flex;
  align-items: center;
  gap: var(--sp-8);
  margin-right: auto;
  font-size: 13px;
}
.pol-page > .banner {
  margin-bottom: var(--sp-16);
}

/* transaction: one card per device in column stacks; the steps are the change rows */
.gt-txn {
  margin-top: var(--sp-16);
}
.gt-step {
  font-family: var(--font-sans);
}
.gt-step a {
  font-family: var(--font-sans);
}
.gt-cmd-d > summary {
  cursor: pointer;
  font: 400 12.5px var(--font-mono);
  overflow-wrap: anywhere;
}
.gt-stopped {
  color: var(--color-text-2);
}
/* the pin table stays a table on a phone: three short columns fit 375px, phone cards would split each pin into a box */
@media (max-width: 44.99rem) {
  table.gt-ptab {
    display: table;
  }
  table.gt-ptab thead {
    display: table-header-group;
    position: static;
  }
  table.gt-ptab tbody {
    display: table-row-group;
  }
  table.gt-ptab tr,
  table.gt-ptab tbody tr:hover {
    display: table-row;
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
  }
  table.gt-ptab th,
  table.gt-ptab td {
    display: table-cell;
    padding: 3px var(--sp-6);
    text-align: left;
  }
}

/* Times (#635). The server writes <time data-ts> in its own fixed format and time.js rewrites it in the
   browser's. The script is deferred, so the browser can draw a frame before it runs: the unpainted time is
   hidden (it keeps its width, so nothing moves) until time.js has handled it and set data-painted, also where
   it keeps the server's text. If time.js never runs, the animation shows the server's text after one second;
   without JavaScript noscript.css shows it at once. A data-ts="title" time only gets a title, its text is never
   rewritten, so it is never hidden. */
time[data-ts]:not([data-painted]):not([data-ts="title"]) {
  visibility: hidden;
  animation: tf-time-reveal 0s linear 1s forwards;
}
@keyframes tf-time-reveal {
  to {
    visibility: visible;
  }
}

/* weekday chips of the backup schedule (#601): the checkbox stays in the page for keyboard and
   screen readers and covers the label; the chip is the label. Selected = the accent tint. */
.cks.chips {
  gap: var(--sp-8);
}
.ucheck.chip {
  position: relative;
  min-width: 3rem;
  min-height: var(--ctl);
  justify-content: center;
  padding: 0 var(--sp-12);
  border: 1px solid var(--color-border-strong);
  border-radius: 999px;
  cursor: pointer;
}
.ucheck.chip input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.ucheck.chip:has(input:checked) {
  background: var(--color-accent-bg);
  border-color: var(--color-accent);
  color: var(--color-accent-fg);
  font-weight: 600;
}
.ucheck.chip:has(input:focus-visible) {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* A control a person changes (a field, a drop zone, a live preview; form.js sets aria-busy on it) draws
   a ring at its right edge while its request runs (#634). It is a background, not a box, so nothing
   moves; it shows after 0.3 s, so a quick answer never flashes it. Where motion is reduced the ring
   stands still. */
:root {
  --busy-ring: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cg opacity='0'%3E%3Cset attributeName='opacity' to='1' begin='0.3s'/%3E%3Ccircle cx='8' cy='8' r='6' fill='none' stroke='%238a8f98' stroke-width='2' stroke-dasharray='28 10'/%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 8 8' to='360 8 8' dur='0.8s' repeatCount='indefinite'/%3E%3C/g%3E%3C/svg%3E");
}
@media (prefers-reduced-motion: reduce) {
  :root {
    --busy-ring: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cg opacity='0'%3E%3Cset attributeName='opacity' to='1' begin='0.3s'/%3E%3Ccircle cx='8' cy='8' r='6' fill='none' stroke='%238a8f98' stroke-width='2' stroke-dasharray='28 10'/%3E%3C/g%3E%3C/svg%3E");
  }
}
input[aria-busy="true"]:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
select[aria-busy="true"],
textarea[aria-busy="true"],
.add-drop[aria-busy="true"] {
  background-image: var(--busy-ring);
  background-repeat: no-repeat;
  background-position: right 0.6rem center;
  background-size: 16px 16px;
  cursor: progress;
}
.devcard[aria-busy="true"] .devcard-head {
  background-image: var(--busy-ring);
  background-repeat: no-repeat;
  background-position: right center;
  background-size: 16px 16px;
}
/* room for the ring is kept all the time, so the note does not move when a preview starts */
#rule-devices .devcard-note {
  margin-right: 24px;
}
/* a link to a page that reads a device before it answers: a ring after its text until the page arrives */
@keyframes ds-show {
  from {
    opacity: 0;
  }
}
a[aria-busy="true"]::after {
  content: "";
  display: inline-block;
  width: 0.85em;
  height: 0.85em;
  margin-left: 0.45em;
  vertical-align: -0.1em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation:
    ds-spin 0.8s linear infinite,
    ds-show 0.01s 0.3s backwards;
}
@media (prefers-reduced-motion: reduce) {
  a[aria-busy="true"]::after {
    animation: ds-show 0.01s 0.3s backwards;
  }
}

/* Transactions: scheduled updates (#604) */
.upd-sched {
  margin-bottom: var(--sp-14);
}
.upd-srow {
  align-items: center;
}
.upd-srow a.upd-hid,
.upd-srow a.upd-htitle {
  color: var(--color-text);
  text-decoration: none;
}
.upd-srow a.upd-htitle:hover,
.upd-srow a.upd-hid:hover {
  text-decoration: underline;
}
.upd-sstart {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  font-size: 13px;
}
.upd-sstart b {
  font-weight: 600;
}
.upd-sstart small {
  font-size: 12px;
  color: var(--color-text-2);
}
.upd-sact {
  display: flex;
  justify-content: flex-end;
}
@media (min-width: 45rem) {
  .upd-srow,
  .upd-shead {
    grid-template-columns: 96px minmax(0, 2fr) 110px minmax(0, 1.5fr) 90px;
  }
}
@media (max-width: 44.99rem) {
  .upd-srow {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "id act"
      "title title"
      "start start"
      "n n";
  }
  .upd-srow .upd-hid {
    grid-area: id;
  }
  .upd-srow .upd-htitle {
    grid-area: title;
  }
  .upd-srow .upd-sstart {
    grid-area: start;
  }
  .upd-srow .upd-hn {
    grid-area: n;
  }
  .upd-srow .upd-sact {
    grid-area: act;
  }
}
/* one planned, not started or cancelled transaction */
.upd-when-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-10);
}
.upd-when-big {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-8);
  margin: 0;
  font-size: 18px;
  font-weight: 600;
}
.upd-when-big small {
  font-size: 13px;
  font-weight: 400;
  color: var(--color-text-2);
}
.upd-notif {
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
  padding: var(--sp-12) var(--sp-14);
  border: var(--hair-w) solid var(--color-border-strong);
  border-radius: var(--r-banner);
  background: var(--color-surface);
}
.upd-notif b {
  font-size: 13.5px;
}
.upd-notif p {
  margin: 0;
  font-size: 13px;
}
.upd-notif small {
  font-size: 12px;
  color: var(--color-text-2);
}
.upd-prog-note {
  margin: var(--sp-10) 0 0;
  font-size: 12.5px;
  color: var(--color-text-2);
}
.upd-cols-one .upd-rows {
  flex: 1 1 100%;
}
