/* 2026-05-15 — nikshep-dash styles (paranoid auth + connectedworld-inspired
 * authenticated shell).
 *  - No CDNs; Inter Variable bundled locally via @font-face.
 *  - Light mode follows the connectedworld palette (deep teal sidebar,
 *    teal-to-pink gradient topbar, slate body text).
 *  - Dark mode kept with same structure but our own muted palette.
 *  - No inline styles in templates; everything classes-driven.
 */

/* ──────────────────────────────────────────────────────────
 * Inter Variable — local, no CDN
 * ────────────────────────────────────────────────────────── */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/InterVariable.woff2') format('woff2-variations'),
       url('../fonts/InterVariable.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: italic;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/InterVariable-Italic.woff2') format('woff2-variations'),
       url('../fonts/InterVariable-Italic.woff2') format('woff2');
}

/* ──────────────────────────────────────────────────────────
 * Theme tokens (light = default; dark via [data-theme="dark"])
 * ────────────────────────────────────────────────────────── */
:root, :root[data-theme="light"] {
  color-scheme: light;
  --bg-0:           #f5f7fa;
  --bg-1:           #ffffff;
  --bg-2:           #eef1f5;
  --bg-3:           #e2e7ee;
  --border:         #e1e6ec;
  --border-strong:  #c9d2dc;
  --fg-0:           #1f2937;
  --fg-1:           #374151;
  --fg-2:           #5C667A;       /* tertiary */
  --fg-3:           #8b97a8;
  /* 2026-05-26 — light-blue tints for the inline-editor "selected"
     state. Pale enough to be calm; clearly blue (not grey). The card
     tint is one step lighter than the drawer so cards stand out
     against the drawer background. */
  --editing-bg:        #e8f0fe;
  --editing-card-bg:   #f5f9ff;

  /* Brand palette */
  --accent:         #1168F5;       /* PRIMARY  */
  --accent-hover:   #0e57d2;
  --accent-deep:    #0a47ad;
  --secondary:      #694DFA;       /* SECONDARY (used sparingly: highlights / links on hover) */
  --secondary-hover:#5a3ff5;
  --tertiary:       #5C667A;       /* TERTIARY (muted text / borders / inactive) */

  --accent-fg:      #ffffff;
  --accent-ring:    rgba(17, 104, 245, 0.22);

  --ok:             #1ea97c;
  --warn:           #b45309;
  --err:            #dc2626;
  --info:           #1168F5;
  /* 2026-05-18 — Running-spinner accent. Shared across themes so the
     animation reads identically against either backdrop. Operator-set
     coral (#E8587C). Used by .spinner-claimed via currentColor. */
  --running:        #E8587C;
  --code-bg:        #eef1f5;
  --shadow:         0 4px 16px rgba(20, 36, 70, 0.06);
  --radius:         6px;

  /* 2026-05-19 — Light theme: sidebar + topbar use genuinely light
     surfaces with dark text. Previous tokens were slate (#1a2235)
     for both themes, which made the chrome dark even on light. */
  --sidebar-bg:     #ffffff;
  --sidebar-bg-2:   #f5f7fa;
  --sidebar-hover:  #eef1f5;
  --sidebar-active: color-mix(in srgb, var(--accent) 12%, transparent);
  --sidebar-fg:     #1f2937;
  --sidebar-fg-2:   #5C667A;

  /* Topbar — white surface with thin bottom border (replaces the
     prior slate fill). Text uses --fg-0 so the username + Sign out
     are clearly readable on white. */
  --topbar-grad:    #ffffff;
  --topbar-fg:      #1f2937;
  --topbar-fg-2:    #5C667A;
  --topbar-border:  #e1e6ec;

  --sidebar-w:      240px;
  --topbar-h:       60px;
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --bg-0:           #0f1419;
  --bg-1:           #161c24;
  --bg-2:           #1c242e;
  --bg-3:           #25303b;
  --border:         #25303b;
  --border-strong:  #354554;
  --fg-0:           #e5e7eb;
  --fg-1:           #c2c8d1;
  /* 2026-05-26 — DARK THEME ONLY: lifted the muted-text colour from
     #5C667A to #727478 per operator. Light theme keeps its own --fg-2
     unchanged. Affects every tile-h-sub, sched-card-preview, page-h .sub,
     audit/queue pager-info, hint, .badge-muted, etc. — all of which
     already read from var(--fg-2). */
  --fg-2:           #727478;       /* tertiary (dark theme) */
  --fg-3:           #4a5364;
  /* 2026-05-26 — dark-theme editor tints: faint blue on the dark
     surface, lighter blue for the cards inside. Same intent as the
     light-theme pair (--editing-bg / --editing-card-bg). */
  --editing-bg:        #1c2a44;
  --editing-card-bg:   #233152;

  /* 2026-05-27 — dark-theme accent shifted from #1168F5 → #0081d7 per
     operator. Light theme accent is UNCHANGED. */
  --accent:         #0081d7;
  --accent-hover:   #1f9bea;
  --accent-deep:    #006bb3;
  --secondary:      #694DFA;
  --secondary-hover:#8568ff;
  --tertiary:       #5C667A;

  --accent-fg:      #ffffff;
  --accent-ring:    rgba(0, 129, 215, 0.30);

  --ok:             #34d399;
  --warn:           #f0b429;
  --err:            #f87171;
  --info:           #0081d7;
  /* 2026-05-18 — Same running-spinner accent as the light theme so the
     animation reads identically when the operator toggles themes. */
  --running:        #E8587C;
  --code-bg:        #0a0e12;
  --shadow:         0 8px 24px rgba(0, 0, 0, 0.35);

  --sidebar-bg:     #121826;
  --sidebar-bg-2:   #0c111c;
  --sidebar-hover:  #1c2438;
  --sidebar-active: #1c2438;
  --sidebar-fg:     #d4d8e0;
  --sidebar-fg-2:   rgba(212, 216, 224, 0.55);
  --topbar-border:  #25303b;

  --topbar-grad:    #121826;
  --topbar-fg:      #e6eaf2;
  --topbar-fg-2:    rgba(230, 234, 242, 0.70);
}

/* ──────────────────────────────────────────────────────────
 * Reset / base
 * ────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; overflow: hidden; }
/* 2026-05-27 — kill body scroll; the scroll container is now .shell-main
   so the scrollbar appears below the fixed topbar, only when content
   exceeds the viewport. See .shell-main rule below. */
body {
  font-family: 'Inter', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* 2026-05-16 → 2026-05-17 — base bumped back to 13 px so general copy
     reads at parity with the table-row size (14 px) requested below. */
  font-size: 13px;
  font-variation-settings: "opsz" 16;
  background-color: var(--bg-0);
  color: var(--fg-0);
  line-height: 1.55;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
h1, h2, h3, h4 { font-family: 'Inter', system-ui, sans-serif; font-weight: 600; letter-spacing: -0.1px; }
/* 2026-05-17 — Standard h2 size across every page (operator-set). */
h2 { font-size: 16px; }

/* 2026-05-16 — force form controls + tables off the UA default font so the
   whole UI renders in Inter Variable. */
input, select, textarea, button, table, th, td, label, dt, dd, code, pre {
  font-family: 'Inter', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-variation-settings: "opsz" 16;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
/* 2026-05-16 — there is no code on this dashboard: render <code> tags as
   plain inline text (no bg, no monospace, no padding). Operator request. */
code, pre {
  font: inherit;
  background: transparent;
  padding: 0;
  border-radius: 0;
}
hr { border: 0; border-top: 1px solid var(--border); margin: 15px 0; }

/* ──────────────────────────────────────────────────────────
 * AUTH pages (paranoid — no shell, no brand, no disclosures).
 *
 * Layout: two halves.
 *   • Left half:  data-center server-rack photo, horizontally flipped.
 *   • Right half: the photo fades into a deep dark teal, on top of
 *                 which a floating "glass" login card sits centered
 *                 both vertically and horizontally.
 *
 * The image is drawn via a fixed pseudo-element with transform:
 * scaleX(-1) so the *image* is mirrored without affecting layout
 * direction. A second fixed pseudo-element paints the left→right
 * gradient (transparent on the left, almost-solid dark teal on the
 * right) so the right half is essentially a flat color while the
 * left half remains photo.
 * ────────────────────────────────────────────────────────── */
.auth-page {
  position: relative;
  min-height: 100vh;
  margin: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  color: #e7ecf2;
  background-color: #0a1620;
  overflow: hidden;
}

/* Layer 1: the photo, fixed full-bleed, ADAPTIVE.
 *   • image-set() picks WebP when supported (~141 KB), JPG otherwise.
 *   • background-size: contain shows the *entire* 16:9 image at every
 *     viewport size — nothing is cropped. If the viewport isn't 16:9,
 *     a warm-dark background-color (matching the image's shadow tone)
 *     fills the letterbox area so the transition reads as a vignette,
 *     not a frame.
 *   • Image is centered horizontally and vertically.
 */
.auth-page::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  background-image: url("../img/login-bg.jpg");          /* universal fallback */
  background-image: -webkit-image-set(
                      url("../img/login-bg.webp") type("image/webp") 1x,
                      url("../img/login-bg.jpg")  type("image/jpeg") 1x);
  background-image:         image-set(
                      url("../img/login-bg.webp") type("image/webp") 1x,
                      url("../img/login-bg.jpg")  type("image/jpeg") 1x);
  background-repeat: no-repeat;
  background-position: center center;
  background-size: contain;
  background-color: #0a1018;     /* cool blue-grey letterbox fill, matches c4 shadows */
  pointer-events: none;
}

/* Layer 2: left→right darkening overlay. Tone now matches the cool
   blue-grey shadows of c4 so the right-edge fade reads as a vignette,
   not a coloured wash. */
.auth-page::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 1;
  background: linear-gradient(
    to right,
    rgba(12, 18, 26, 0.08) 0%,
    rgba(12, 18, 26, 0.45) 30%,
    rgba(10, 16, 22, 0.85) 60%,
    rgba(8,  12, 18, 0.97) 100%
  );
  pointer-events: none;
}

/* All page content sits above both layers */
.auth-page > * { position: relative; z-index: 2; }

/* Right-column host: vertically + horizontally centers the card */
.auth-page .card-host {
  grid-column: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px 3vw;
}

/* Mobile / narrow: collapse to a single column, card centered */
@media (max-width: 800px) {
  .auth-page { grid-template-columns: 1fr; }
  .auth-page .card-host { grid-column: 1; padding: 32px 16px; }
}

/* 2026-05-17 — auth-card dimensions are intentionally LARGER than the
   dense dashboard. Login/MFA is a once-per-session focal point; it
   reads at arm's length on a 1080p monitor. Sizes here are absolute px
   tuned to the 16-px-rem world the card was originally designed in. */
.auth-card {
  position: relative;
  width: 100%;
  max-width: 360px;            /* ~the original 20rem at 16-px-rem */
  padding: 32px 28px;
  background: rgba(14, 20, 28, 0.78);
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 12px;
  color: #e7ecf2;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  font-size: 14px;
}
.auth-card .brand-logo {
  display: block;
  margin: 0 auto 22px auto;
  max-width: 168px;
  height: auto;
  user-select: none;
  -webkit-user-drag: none;
}
.auth-card h2 {
  margin: 0 0 22px 0;
  font-size: 21px;
  font-weight: 600;
  letter-spacing: -0.2px;
  color: #f1f5f9;
}
.auth-card p  { color: rgba(231, 236, 242, 0.78); line-height: 1.55; font-size: 13px; }
.auth-card label {
  color: rgba(231, 236, 242, 0.7);
  font-weight: 500;
  font-size: 13px;
  margin: 14px 0 6px 0;
  display: block;
}
.auth-card input[type="text"],
.auth-card input[type="password"],
.auth-card input[type="email"] {
  width: 100%;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: #f1f5f9;
  border-radius: 8px;
  padding: 10px 13px;
  font-size: 14px;
}
.auth-card input:focus {
  outline: none;
  border-color: rgba(102, 126, 234, 0.6);
  box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.22);
  background: rgba(255, 255, 255, 0.08);
}
.auth-card input:-webkit-autofill {
  -webkit-box-shadow: 0 0 0 30px rgba(14, 20, 28, 0.92) inset !important;
  -webkit-text-fill-color: #f1f5f9 !important;
}

/* 2026-05-16/17 — flat sign-in button. Sized for the auth card, not the
   dashboard — full width, comfortable hit target. */
.btn-gradient {
  display: block;
  width: 100%;
  margin-top: 22px;
  padding: 11px 16px;
  border: 0;
  border-radius: 6px;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.1px;
  color: #ffffff;
  cursor: pointer;
  background: var(--accent);
  transition: background 0.15s ease;
}
.btn-gradient:hover { background: var(--accent-hover); }

/* 2026-05-17 — auth-page overrides for shared rules: bring text + spacing
   back up to "focal page" sizes inside the auth card. */
.auth-card .errors  { font-size: 13px; margin: 8px 0; padding-left: 18px; }
.auth-card .msgs    { margin-bottom: 16px; }
.auth-card .msg     { padding: 9px 12px; margin-bottom: 6px; font-size: 13px; }
.auth-card .hint    { font-size: 12px; margin-top: 16px; line-height: 1.55; }
.auth-card .steps   { padding-left: 20px; line-height: 1.7; font-size: 13px; }
.auth-card .qr      { margin: 18px 0; }
.auth-card .qr figcaption { font-size: 12px; margin-top: 8px; }
.auth-card .btn-row { gap: 8px; margin-top: 16px; }
.auth-card .secret  { font-size: 14px; padding: 4px 8px; }

/* ──────────────────────────────────────────────────────────
 * Inputs / buttons / messages (shared)
 * ────────────────────────────────────────────────────────── */
label { display: block; margin: 11px 0 4px 0; font-size: 11px; color: var(--fg-2); font-weight: 500; }
input[type="text"], input[type="password"], input[type="email"],
input[type="number"], textarea, select {
  width: 100%;
  padding: 7px 9px;
  background: var(--bg-1);
  color: var(--fg-0);
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  font: inherit;
}
textarea { resize: vertical; min-height: 70px; }
/* 2026-05-23 — focus halo only on text-style inputs. Excluding radio
   and checkbox stops the 3-px box-shadow rendering as a square BG
   behind the round/square widget. */
input:not([type="radio"]):not([type="checkbox"]):focus,
textarea:focus, select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}
/* Suppress browser autofill yellow */
input:-webkit-autofill {
  -webkit-box-shadow: 0 0 0 30px var(--bg-1) inset !important;
  -webkit-text-fill-color: var(--fg-0) !important;
  transition: background-color 9999s ease-in-out 0s;
}

/* 2026-05-17 — buttons (operator-supplied spec verbatim).
   .btn-warn is in the same comma-list so it picks up the same weight,
   transition, and shape — only the background/border colour differs. */
.btn-primary, .btn-secondary, .btn-danger, .btn-ghost, .btn-warn {
  display: inline-block;
  margin-top: 3px;
  padding: 8px 15px;
  border: 1px solid transparent;
  border-radius: 5px;
  font: inherit;
  font-weight: inherit;
  font-size: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  transition: background 0.15s ease, border-color 0.15s ease, filter 0.15s ease;
}
/* 2026-05-16 — flat buttons, no glow/shadow per operator request. */
.btn-primary  {
  background: var(--accent);
  color: #ffffff;
}
.btn-primary:hover { background: var(--accent-hover); text-decoration: none; }

/* Secondary: deep slate to match the sidebar/topbar; subtle, not flashy. */
.btn-secondary {
  background: #1a2235;
  color: #e5e7eb;
  border-color: #242d44;
}
.btn-secondary:hover { background: #242d44; text-decoration: none; }

/* 2026-05-17 — Aligned with the Services Stop button so destructive
   actions (Delete, Cancel session, Close-editor, Discard-changes) all
   carry the same red as Stop. */
.btn-danger      { background: #dc2626; color: #ffffff; border-color: #dc2626; }
.btn-danger:hover { background: #b91c1c; border-color: #b91c1c; text-decoration: none; }

/* 2026-05-17 — .btn-warn: bright vibrant orange (matches the reference
   "Add" button style — Tailwind orange-500 / -600 on hover). Used for
   mid-impact actions like "Reload policy". */
.btn-warn       { background: #f97316; color: #ffffff; border-color: #f97316; }
.btn-warn:hover { background: #ea580c; text-decoration: none; }

/* 2026-05-17 — .btn-ghost now carries a visible outline + faint surface
   so it's discoverable in light theme. In dark theme the same rule still
   reads as a subtle outlined button. */
.btn-ghost     { background: var(--bg-2); color: var(--fg-0); border-color: var(--border-strong); }
.btn-ghost:hover { background: var(--bg-3); text-decoration: none; }

/* 2026-05-17 — Services-page action buttons.
   Equal width sized to the longest label ("Restart"), so Start / Stop /
   Restart all line up cleanly. Coloured by intent: green start, red
   stop, amber restart. The "disabled" state stays muted but keeps the
   semantic colour so the operator can still read intent at a glance. */
.svc-actions { text-align: right; white-space: nowrap; }
.svc-actions .btn-svc + .btn-svc { margin-left: 6px; }
.btn-svc {
  display: inline-block;
  min-width: 96px;            /* fits "Restart" with breathing room */
  padding: 6px 12px;
  border: 1px solid transparent;
  border-radius: 5px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  transition: background 0.15s ease, filter 0.15s ease;
}
.btn-svc-start   { background: #16a34a; color: #ffffff; }   /* green */
.btn-svc-stop    { background: #dc2626; color: #ffffff; }   /* red   */
.btn-svc-restart { background: #d97706; color: #ffffff; }   /* amber */
/* 2026-05-25 — primary (accent blue) labelled button. Uses the canonical
   .btn-svc dimensions (min-width 96, padding 6×12, font 13/600) so it
   lines up with Start/Stop/Restart sizing per the standard-button-size
   HARD RULE; colour matches .btn-primary's var(--accent). */
.btn-svc-primary       { background: var(--accent); color: #ffffff; border-color: var(--accent); }
.btn-svc-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
/* 2026-05-26 — labelled red "Close" / "Discard" for inline editors.
   Reuses the canonical .btn-svc dimensions (min-width 96, padding 6×12,
   font 13/600) and the .btn-danger colour palette (#dc2626 → #b91c1c)
   so it visually parallels the Save (.btn-svc-primary) sitting next
   to it — same size, opposite intent. */
.btn-svc-close         { background: #dc2626; color: #ffffff; border-color: #dc2626; }
.btn-svc-close:hover   { background: #b91c1c; border-color: #b91c1c; }
/* 2026-05-29 — Servers / Manage top-bar bulk-action buttons (Delete,
   Suspend, Resume). Drive colour from data-intent so live.js can flip
   the same DOM node between Suspend (warn) and Resume (ok) without
   rebuilding the element. Disabled styling matches the icon-btn
   disabled rule (0.45 opacity, not-allowed cursor, no events). */
.btn-svc[data-intent="err"]        { background: #dc2626; color: #ffffff; border-color: #dc2626; }
.btn-svc[data-intent="err"]:hover  { background: #b91c1c; border-color: #b91c1c; }
.btn-svc[data-intent="warn"]       { background: #d97706; color: #ffffff; border-color: #d97706; }
.btn-svc[data-intent="warn"]:hover { background: #b45309; border-color: #b45309; }
.btn-svc[data-intent="ok"]         { background: #16a34a; color: #ffffff; border-color: #16a34a; }
.btn-svc[data-intent="ok"]:hover   { background: #15803d; border-color: #15803d; }
.btn-svc:disabled,
.btn-svc[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}
/* Gap between the top-bar bulk buttons. .page-h-right's existing
   layout doesn't space inline-block children, so add it here. */
.page-h-right .btn-svc + .btn-svc,
.page-h-right .btn-svc + .btn-primary,
.page-h-right .btn-svc + .btn-primary.btn-sm { margin-left: 8px; }
/* 2026-05-29 — When .btn-svc sits in .page-h-right beside the
   primary action (.btn-primary.btn-sm), normalize its box-model so
   all three top-bar buttons share visual dimensions. The .btn-svc
   defaults (min-width 96, padding 6×12) are designed for the
   Services Start/Stop row; the page-h-right row uses .btn-sm
   sizing (padding 5×10, font 13/600, margin-top 3 to baseline-
   align with the H1's natural rhythm). Override only inside this
   container so we don't disturb other .btn-svc call sites. */
.page-h-right .btn-svc {
  padding: 5px 10px;
  font-size: 13px;
  font-weight: 600;
  margin-top: 3px;
  min-width: 96px;
  line-height: inherit;
  vertical-align: middle;
}
.btn-svc-start:hover   { background: #15803d; }
.btn-svc-stop:hover    { background: #b91c1c; }
.btn-svc-restart:hover { background: #b45309; }
/* 2026-05-19 — Manual Retry (session-level). Sibling of start/stop/restart
   so the canonical .btn-svc sizing applies (96 × 6/12). Uses --accent
   (theme-aware blue) because retry is a positive "send back into the
   queue" action — visually distinct from Stop (red) and Restart-service
   (amber). Hover dims via color-mix to match the other svc variants. */
.btn-svc-retry {
  background: var(--accent);
  color: #ffffff;
  border-color: var(--accent);
}
.btn-svc-retry:hover {
  background: color-mix(in srgb, var(--accent) 85%, #000);
  border-color: color-mix(in srgb, var(--accent) 85%, #000);
}

/* 2026-05-19 — Drawer action wrapper inside the Servers-row accordion.
   Absolutely positioned top-right of the drawer cell so the buttons
   line up directly under the row's Actions column icon buttons.
   Holds Backup History (always) + Retry session (conditional);
   flex-row with 8px gap so the two stay side-by-side. */
.srv-detail-cell { position: relative; }
/* 2026-05-28 — Two-row stack: top row holds Backup History / List Accounts
   (and the conditional Retry session); the second row holds Edit Ownership
   alone. enterEditMode swaps Edit Ownership for Save / Cancel in the same
   second row by hiding Edit Ownership and appending Save+Cancel to this
   container (column-flex puts them on a new line under the top row). */
.srv-detail-actions {
  position: absolute;
  top: 14px;
  right: 14px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  /* 2026-05-28 — 25 px between the top button row (Backup History / List
     Accounts) and the second row (Edit Ownership, or Save+Cancel in edit
     mode). Within each row, .srv-detail-actions-top keeps its own 8 px
     internal gap. */
  gap: 25px;
}
.srv-detail-actions-top {
  display: flex;
  gap: 8px;
  align-items: center;
}
/* 2026-05-28 — Amber outline variant for the drawer's Edit Ownership
   button. Same state machine as btn-svc-outline / btn-svc-outline-danger
   (outline at rest → filled on hover → darker filled on active). Distinct
   from grey-default so operators can pick the action at a glance. */
.btn-svc-warn {
  background: transparent;
  color: var(--warn);
  border: 1px solid var(--warn);
  text-decoration: none;
}
.btn-svc-warn:hover,
.btn-svc-warn:focus,
.btn-svc-warn:visited {
  background: var(--warn);
  color: #1a1a1a;
  border-color: var(--warn);
  text-decoration: none;
}
.btn-svc-warn:active {
  background: color-mix(in srgb, var(--warn) 85%, #000);
  border-color: color-mix(in srgb, var(--warn) 85%, #000);
  color: #1a1a1a;
}
.srv-detail-actions .inline-form { margin: 0; }

/* 2026-05-19 — Outline variant of .btn-svc. Same dimensions as the
   filled variants (start/stop/restart/retry); transparent background
   with an accent-coloured border + text. Used by the drawer's Backup
   History link so Retry session (filled accent) stays the visual
   focus when both buttons are present. */
.btn-svc-outline {
  background: transparent;
  color: var(--accent);
  border: 1px solid var(--accent);
  text-decoration: none;
}
.btn-svc-outline:hover,
.btn-svc-outline:focus,
.btn-svc-outline:active,
.btn-svc-outline:visited {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
  text-decoration: none;
}

/* 2026-05-28 — Outlined-danger variant for Cancel buttons (Edit / New
   forms). Aligned with the canonical destructive crimson used by
   .btn-danger (Close button on Policy templates editor) so Cancel and
   Close share one identifiable colour. Hard-coded to #dc2626 / #b91c1c
   instead of var(--err) — same reason as .btn-danger: --err shifts to
   salmon (#f87171) in dark theme, which makes Cancel look like a
   different button family. Hard rule: every "destructive labelled
   action" (Cancel / Close / Delete / Discard) renders in #dc2626 at
   rest and #b91c1c on hover, in both themes. */
.btn-svc-outline-danger {
  background: transparent;
  color: #dc2626;
  border: 1px solid #dc2626;
  text-decoration: none;
}
.btn-svc-outline-danger:hover,
.btn-svc-outline-danger:focus,
.btn-svc-outline-danger:visited {
  background: #dc2626;
  color: #ffffff;
  border-color: #dc2626;
  text-decoration: none;
}
.btn-svc-outline-danger:active {
  background: #b91c1c;
  border-color: #b91c1c;
  color: #ffffff;
}
/* 2026-05-19 — Equal-width pair modifier. Forces both buttons inside
   .srv-detail-actions to the same min-width so "Backup History" and
   "Retry session" align flush regardless of which string is longer.
   Slightly wider than the base .btn-svc (96px) to fit either label
   comfortably without text wrap. */
.btn-svc-equal { min-width: 128px; }

/* 2026-05-23 — Fluorescent-orange outline variant used by the
   Servers-page drawer's "List Accounts" button so it doesn't
   compete visually with the primary-accent (blue) buttons elsewhere.
   Same colour in both themes per operator spec. */
.btn-svc-list {
  background: transparent;
  color: #ff6a00;
  border: 1px solid #ff6a00;
  text-decoration: none;
}
.btn-svc-list:hover,
.btn-svc-list:focus,
.btn-svc-list:active,
.btn-svc-list:visited {
  background: color-mix(in srgb, #ff6a00 14%, transparent);
  color: #ff6a00;
  text-decoration: none;
}
.btn-svc:disabled,
.btn-svc[disabled] {
  cursor: not-allowed;
  opacity: 0.55;
  filter: saturate(0.85);
}
.btn-row { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 12px; }

.errors { color: var(--err); padding-left: 14px; margin: 6px 0; }
.msgs   { list-style: none; padding: 0; margin: 0 0 12px 0; }
.msg    { padding: 7px 11px; border-radius: 4px; background: var(--bg-3); margin-bottom: 5px; }
.msg.success { background: color-mix(in srgb, var(--ok) 20%, var(--bg-3)); }
.msg.error, .msg.danger { background: color-mix(in srgb, var(--err) 22%, var(--bg-3)); }
.msg.warning { background: color-mix(in srgb, var(--warn) 22%, var(--bg-3)); }
.hint   { color: var(--fg-2); font-size: 10px; margin-top: 12px; }

/* TOTP enrollment specifics */
.steps  { padding-left: 16px; line-height: 1.7; color: var(--fg-1); }
.qr     { margin: 12px 0; text-align: center; }
.qr img { background: #fff; padding: 8px; border-radius: 4px; max-width: 220px; }
.qr figcaption { font-size: 10px; color: var(--fg-2); margin-top: 7px; }
/* 2026-05-17 — `.secret` is the one place we keep a faint background so
   operators visually find a click-to-select-all box on the MFA QR-secret
   page; everywhere else, bare text. */
.secret {
  background: var(--bg-2);
  padding: 2px 6px;
  border-radius: 3px;
  user-select: all;
  word-break: break-all;
  font: inherit;
}

/* ──────────────────────────────────────────────────────────
 * Authenticated shell (connectedworld-inspired)
 * Fixed full-width topbar + fixed sidebar below it + content offset.
 * ────────────────────────────────────────────────────────── */
.shell-top {
  position: fixed; top: 0; left: 0; right: 0;
  height: var(--topbar-h);
  background: var(--topbar-grad);
  color: var(--topbar-fg);
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 15px;
  z-index: 1001;
  /* 2026-05-19 — Soft 1px border replaces the heavy drop shadow that
     suited the slate topbar; on a light-theme white topbar the shadow
     looked like a smear. Border keeps the boundary crisp in both themes. */
  border-bottom: 1px solid var(--topbar-border);
  box-shadow: 0 1px 2px rgba(15, 20, 25, 0.04);
}
.shell-top .left-section { display: flex; align-items: center; gap: 9px; height: 100%; }
.shell-top .topbar-logo {
  height: 32px;
  width: auto;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}
/* 2026-05-19 — Theme-driven logo swap. Both <img> tags are emitted
   by base_shell.html; CSS hides the one that doesn't match the active
   theme. Selectors at the :root level so the swap is instant on
   toggle (theme.js flips data-theme on <html>). */
:root[data-theme="light"] .shell-top .topbar-logo-dark  { display: none; }
:root[data-theme="dark"]  .shell-top .topbar-logo-light { display: none; }
/* Fallback: when no data-theme is set yet (rare race before head
   script runs), prefer the color logo as the visible one. */
:root:not([data-theme="dark"]) .shell-top .topbar-logo-dark { display: none; }
.shell-top .right-section { display: flex; align-items: center; gap: 5px; }

.top-icon-btn {
  width: 36px; height: 36px;
  border-radius: 5px;
  background: transparent;
  border: 1px solid rgba(255,255,255,0.0);
  color: var(--topbar-fg);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; font-size: 15px;
}
/* 2026-05-19 — theme-aware hover: tint of current foreground rather
   than hardcoded white (which was invisible on the new white topbar). */
.top-icon-btn:hover {
  background: color-mix(in srgb, var(--topbar-fg) 10%, transparent);
  border-color: color-mix(in srgb, var(--topbar-fg) 18%, transparent);
}
/* 2026-05-26 — colour the theme-toggle icon for clearer intent:
   dark theme shows ☀ in amber (signals "click to switch to light");
   light theme shows 🌚 (NEW MOON WITH FACE) which is already a
   coloured emoji and needs no override. Larger font-size so the moon
   face is recognisable. */
[data-theme="dark"] #theme-toggle {
  color: #f59e0b;          /* amber-500 — warm sun */
  font-size: 17px;
}
#theme-toggle { font-size: 17px; line-height: 1; }
/* Username — plain text, no chip / pill / avatar */
.user-name {
  color: var(--topbar-fg);
  font-size: 13px;
  font-weight: 500;
  margin: 0 6px;
  letter-spacing: 0.05px;
}

/* Sign-out is a form (for CSRF + POST), but rendered as plain text. */
.signout-form { display: inline; margin: 0; }
.text-link {
  background: transparent;
  border: 0;
  padding: 0 5px;
  /* 2026-05-26 — colour per theme. Light = soft red (resting) →
     darker red on hover. Dark = amber (resting) → bright/saturated
     orange on hover. Hard-coded fall-back colour for unthemed root. */
  color: #dc2626;            /* red-600 — safe default */
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  text-decoration: none;
  transition: color 0.12s ease;
}
.text-link:hover,
.text-link:focus {
  color: #991b1b;            /* red-800 — darker on hover (light theme) */
  outline: none;
  text-decoration: underline;
}
[data-theme="dark"] .text-link        { color: #f59e0b; }   /* amber-500 — resting in dark */
[data-theme="dark"] .text-link:hover,
[data-theme="dark"] .text-link:focus  { color: #ff7a00; outline: none; text-decoration: underline; }   /* fluorescent orange on hover in dark */

/* Sidebar */
.shell-side {
  position: fixed;
  top: var(--topbar-h); left: 0;
  width: var(--sidebar-w);
  height: calc(100vh - var(--topbar-h));
  background: var(--sidebar-bg);
  color: var(--sidebar-fg);
  overflow-y: auto;
  z-index: 1000;
  /* 2026-05-19 — Right border so the light-theme sidebar (now white)
     is visually separated from the white main content. No-op on dark. */
  border-right: 1px solid var(--border);
  /* 2026-05-25 — 3 px width is RESERVED at all times so layout never
     shifts. At rest the thumb is fully transparent (invisible). On
     hover the thumb fades in as a faint grey hairline — visible enough
     to grab, not loud enough to compete with the nav text. */
  scrollbar-width: thin;
  scrollbar-color: transparent transparent;
}
.shell-side:hover { scrollbar-color: rgba(120, 120, 120, 0.45) transparent; }
.shell-side::-webkit-scrollbar { width: 3px; background: transparent; }
.shell-side::-webkit-scrollbar-track { background: transparent; }
.shell-side::-webkit-scrollbar-thumb { background: transparent; border-radius: 3px; }
.shell-side:hover::-webkit-scrollbar-thumb { background: rgba(120, 120, 120, 0.45); }
/* 2026-05-17 — sidebar typography bumped to navigation-grade legibility.
   Group titles 11 px uppercase; nav links 14 px; active link 15 px.
   The dashboard content density is unchanged; only the sidebar grows. */
.shell-side .nav { list-style: none; margin: 0; padding: 8px 0; }
.shell-side .nav-group-title {
  padding: 14px 16px 6px;
  font-size: 11px;
  letter-spacing: 1px;
  color: var(--sidebar-fg-2);
  text-transform: uppercase;
  font-weight: 600;
}
.shell-side .nav li a {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 16px;
  color: var(--sidebar-fg);
  font-size: 15px;
  border-left: 3px solid transparent;
}
.shell-side .nav li a:hover { background: var(--sidebar-hover); text-decoration: none; }
.shell-side .nav li a.active {
  background: var(--sidebar-active);
  font-weight: 600;
  font-size: 15px;
  border-left-color: var(--accent);
}
/* 2026-05-17 — caret indicator for expandable nav groups. */
.shell-side .nav-caret {
  margin-left: auto;
  font-size: 11px;
  opacity: 0.7;
  transition: transform 0.18s ease;
}
.shell-side .nav-group.open > a .nav-caret { transform: rotate(180deg); }
/* 2026-05-25 — nav-icon container now hosts an inline SVG (was a
   unicode glyph). The svg inside uses stroke="currentColor" so the
   icon recolours automatically per theme + active state. Height is
   fixed at 16 px so the row's vertical rhythm stays exactly the same
   as the prior glyph era. */
.shell-side .ico {
  width: 16px;
  height: 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  vertical-align: middle;
  opacity: 0.9;
  flex-shrink: 0;
}
.shell-side .ico svg { width: 16px; height: 16px; display: block; }
.shell-side .nav li a.active .ico { opacity: 1; }
/* 2026-05-17 — Sidebar nested submenu (e.g. Policy → Manage / Templates).
   Collapsed by default; expands when the parent .nav-group has .open
   (set by the click handler in base_shell.html) or contains an .active
   link. Sub-items indent slightly past the icon column so they read as
   clearly "child of" the parent. */
.shell-side .nav .nav-sub {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.18s ease;
}
.shell-side .nav .nav-group.open > .nav-sub,
.shell-side .nav .nav-group:has(.active) > .nav-sub {
  max-height: 240px;
}
.shell-side .nav .nav-sub li a {
  padding-left: 50px;
  font-size: 14px;
}
.shell-side .side-foot {
  padding: 12px 16px;
  font-size: 11px;
  color: var(--sidebar-fg-2);
  border-top: 1px solid rgba(255,255,255,0.08);
  margin-top: 6px;
}

/* 2026-05-17 — compact density sweep. No padding-to-fill; sizes tuned for
   screen real estate, not visual marketing. */
/* 2026-05-17 — 20 px top padding gives a clean breathing-room band
   between the fixed topbar and the page title. Left/right gutters
   stay at 12 px. */
/* 2026-05-19 — Topbar offset moved from margin-top to padding-top so
   it lives INSIDE the shell-main box (box-sizing: border-box). The
   prior margin-top + min-height combo summed to ~100vh + 60px on
   browsers where the margin didn't collapse with body's top edge,
   leaving short pages (Dashboard, Servers list) with a permanent
   thin scrollbar even when content barely filled the viewport.
   With padding-top covering the topbar AND min-height: 100vh, the
   box height equals exactly one viewport regardless of content. */
.shell-main {
  /* 2026-05-27 — main is now the document scroll container.
     Anchored to the right of the fixed sidebar and below the fixed topbar.
     overflow-y:auto means the scrollbar only appears when content > viewport.
     scrollbar-gutter:stable reserves the scrollbar's width even when no
     scrollbar is showing, so a page-h-right button always sits the same
     distance from the visible right edge regardless of content length. */
  position: fixed;
  top: var(--topbar-h);
  left: var(--sidebar-w);
  right: 0;
  bottom: 0;
  padding: 20px 12px 11px 12px;
  overflow-y: auto;
  overflow-x: auto;
  scrollbar-gutter: stable;
}

/* Page header */
.page-h {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin: 0 0 20px 0;          /* 20 px breathing room between subtitle and first tile */
}
.page-h h1 { margin: 0; font-size: 22px; font-weight: 600; letter-spacing: -0.2px; line-height: 1.2; }
.page-h .sub { color: var(--fg-2); font-size: 12px; margin-top: 2px; line-height: 1.3; }

/* Cards / grids */
.grid { display: grid; gap: 8px; }
.grid.cards-4 { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
.grid.two     { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.tile {
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px 16px;
}
.tile.kpi .label { color: var(--fg-2); font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600; }
.tile.kpi .value { font-size: 22px; font-weight: 600; margin-top: 4px; }
.tile.kpi.primary .value { color: var(--accent); }
.tile.kpi.ok      .value { color: var(--ok); }
.tile.kpi.warn    .value { color: var(--warn); }
.tile.kpi.err     .value { color: var(--err); }
/* 2026-05-23 — scoped to <section class="tile"> only. The previous
   selector also matched .browse-grid .tile (file-icon tiles), which
   gave every-tile-except-the-first a 12 px top margin and broke
   row alignment in the explorer grid once items stopped stretching. */
section.tile + section.tile { margin-top: 12px; }
.tile-h { margin: 0 0 8px 0; font-size: 16px; font-weight: 600; }
/* 2026-05-24 — tile-h-row: heading + small meta on the same line.
   Used by the Sessions page's Recent panel ("Showing N of M …").
   baseline-aligns the meta sub-text against the heading. */
.tile-h-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  /* 2026-05-28 — bottom margin widened from 8px → 14px so the right-side
     button (taller than the heading text) leaves clear space above the
     content below (e.g. table on cust_org_detail's Users / API Keys
     tiles). Aligns items center-axis now that the row contains buttons
     rather than text-only sub-labels. */
  margin: 0 0 14px 0;
}
.tile-h-row .tile-h { margin: 0; }

/* 2026-05-24 — Same-sid retry: Attempts widget on the session
   detail page. Badge sits inline near the page header; clicking it
   toggles a small drawer listing per-attempt start/end/status/who.
   Drawer styling mirrors the audit-log drawer so the visual
   language is consistent across the dashboard. */
.attempts-bar {
  margin: 8px 0 12px 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.attempts-bar .attempts-badge {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  width: max-content;
}
.attempts-bar .attempts-badge:hover .badge { filter: brightness(1.1); }
.attempts-bar .attempts-drawer.hidden { display: none; }
.attempts-bar .attempts-drawer {
  background: var(--bg-1);
  border-radius: 5px;
  padding: 8px 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 12px;
  color: var(--fg-0);
  line-height: 1.5;
}
.attempts-bar .attempts-drawer .att-row {
  display: grid;
  grid-template-columns: 28px 1fr 1fr auto auto;
  gap: 12px;
  align-items: baseline;
  padding: 2px 0;
}
.attempts-bar .attempts-drawer .att-row .att-idx { color: var(--fg-2); }
.attempts-bar .attempts-drawer .att-row .att-by  { color: var(--fg-2); font-size: 11px; }

/* 2026-05-24 — Audit log: single-row rendering by default. The
   wrapping seen earlier (Timestamp split across two lines, Actor
   broken mid-word) was caused by default `white-space: normal` on
   table cells. nowrap keeps every cell on one line. The Detail
   column gets a soft cap of 150 chars in JS (renderAuditLog) +
   "……" ellipsis; if the full detail exceeds that, the row becomes
   clickable and toggles a drawer row underneath with the full text.
   Short detail rows stay single-row and non-clickable. */
table.t tr.audit-row > td {
  white-space: nowrap;
}
/* 2026-05-26 — Audit table: 8 columns with PERCENTAGE-based widths so
   the layout scales with viewport instead of bursting at narrow widths.
   Detail is given a moderate share; long detail strings truncate at
   150 chars + an inline "more" button (rendered server-side) that
   toggles the row to wrap the full string across multiple lines.
   Click "more" → row expands. Click again → collapses. */
table.t.audit-table { table-layout: fixed; width: 100%; }
table.t.audit-table th,
table.t.audit-table td,
table.t.audit-table tr.audit-row > td {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  padding-left: 6px; padding-right: 6px;
  vertical-align: top;
}
/* Percentage widths (sum to 100). Wider Action/Target/Detail so the
   strings that actually carry signal stay readable; narrow numeric/
   short columns get their minimum. */
table.t.audit-table th:nth-child(1), table.t.audit-table td:nth-child(1) { width: 13%; }  /* Timestamp */
table.t.audit-table th:nth-child(2), table.t.audit-table td:nth-child(2) { width: 9%;  }  /* Actor */
table.t.audit-table th:nth-child(3), table.t.audit-table td:nth-child(3) { width: 11%; }  /* Service */
table.t.audit-table th:nth-child(4), table.t.audit-table td:nth-child(4) { width: 14%; }  /* Action */
table.t.audit-table th:nth-child(5), table.t.audit-table td:nth-child(5) { width: 16%; }  /* Target */
table.t.audit-table th:nth-child(6), table.t.audit-table td:nth-child(6) { width: 7%;  }  /* Result */
table.t.audit-table th:nth-child(7), table.t.audit-table td:nth-child(7) { width: 11%; }  /* IP */
table.t.audit-table th:nth-child(8),
table.t.audit-table td:nth-child(8),
table.t.audit-table tr.audit-row > td:nth-child(8) {
  width: 19%; padding-left: 10px; padding-right: 10px;
}
/* 2026-05-26 — Detail cell behaviour. The WHOLE cell is clickable
   (cursor: pointer) and toggles the row's .expanded class. Collapsed
   state shows the full string single-line with a CSS-driven ellipsis;
   a small caret at the right edge hints the cell can be expanded.
   Expanded state lets the text wrap across multiple lines. No
   150-char gate — any non-empty detail is expandable, because the
   visible truncation is driven by column width (CSS), not character
   count. */
table.t.audit-table td.audit-detail-cell {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  position: relative;
}
table.t.audit-table tr.audit-row td.audit-detail-cell:has(.audit-detail-text) {
  cursor: pointer;
}
table.t.audit-table td.audit-detail-cell .audit-detail-text {
  /* Inline so ellipsis applies to the cell as a whole. */
}
.audit-detail-caret {
  display: inline-block; margin-left: 6px;
  color: var(--fg-2); font-size: 11px; opacity: 0.7;
}
.audit-detail-caret::after { content: "▾"; }
table.t.audit-table tr.audit-row.expanded td.audit-detail-cell,
table.t.audit-table tr.audit-row.expanded > td.audit-detail-cell {
  white-space: normal !important;
  word-break: break-word !important;
  overflow: visible !important;
  text-overflow: clip !important;
}
table.t.audit-table tr.audit-row.expanded td.audit-detail-cell .audit-detail-text {
  white-space: normal !important;
  display: inline-block;
  max-width: 100%;
}
table.t.audit-table tr.audit-row.expanded .audit-detail-caret::after { content: "▴"; }
/* Defense-in-depth: if any of these column-width estimates underflow on
   a narrower viewport, scroll INSIDE the tile (not the whole page). */
section.tile:has(table.t.audit-table) { overflow-x: auto; }

/* 2026-05-26 — audit pagination controls (same shape as Scheduler Logs). */
.audit-pager {
  display: flex; gap: 10px; align-items: center; justify-content: center;
  padding: 12px 0 4px 0;
}
.audit-pager-info { color: var(--fg-2); font-size: 12px; min-width: 220px; text-align: center; }

/* 2026-05-24 — Queue History (System → Logs → Queue History) gets
   the same treatment as the audit log: single-row default, no wrap,
   long result_summary truncates at 150 chars with click-to-expand
   drawer. 20px right-padding on the first 7 columns (Hostname / Kind
   / Queued / Claimed / Acked / Status / Requested by) so the
   result_summary column has every spare pixel for its inline form. */
table.t tr.history-row > td {
  white-space: nowrap;
}
table.t tr.history-row > td.history-result-cell {
  white-space: nowrap;
}
table.t tr.history-row-expandable {
  cursor: pointer;
}
table.t tr.history-row-expandable:hover > td {
  background: var(--bg-2);
}
table.t tr.history-drawer-row.hidden { display: none; }
table.t tr.history-drawer-row > td.history-drawer-cell {
  background: var(--bg-1);
  padding: 8px 12px;
  white-space: pre-wrap;
  word-break: break-word;
  border-top: 0;
}
table.t tr.history-drawer-row pre.history-drawer-full {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 12px;
  color: var(--fg-0);
  line-height: 1.45;
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;
}
/* 2026-05-26 — Queue History table: same fixed-layout + percentage
   widths treatment as audit. Result column was unbounded and stretched
   the table off the right of the viewport on rows whose result_summary
   carried a long JSON blob. Detail (Result) now wraps within its
   share of the row; click-to-expand drawer still works for rows that
   exceed the 150-char threshold. */
table.t.history-table { table-layout: fixed; width: 100%; }
table.t.history-table th,
table.t.history-table td,
table.t.history-table tr.history-row > td {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  padding-left: 6px; padding-right: 6px;
  vertical-align: top;
}
/* Columns: Hostname · Kind · Queued · Claimed · Acked · Status · Requested by · Result */
table.t.history-table th:nth-child(1), table.t.history-table td:nth-child(1) { width: 14%; }   /* Hostname */
table.t.history-table th:nth-child(2), table.t.history-table td:nth-child(2) { width: 10%; }   /* Kind */
table.t.history-table th:nth-child(3), table.t.history-table td:nth-child(3) { width: 12%; }   /* Queued */
table.t.history-table th:nth-child(4), table.t.history-table td:nth-child(4) { width: 12%; }   /* Claimed */
table.t.history-table th:nth-child(5), table.t.history-table td:nth-child(5) { width: 12%; }   /* Acked */
table.t.history-table th:nth-child(6), table.t.history-table td:nth-child(6) { width: 7%;  }   /* Status */
table.t.history-table th:nth-child(7), table.t.history-table td:nth-child(7) { width: 12%; }   /* Requested by */
table.t.history-table th:nth-child(8),
table.t.history-table td:nth-child(8),
table.t.history-table tr.history-row > td:nth-child(8) {
  width: 21%; padding-left: 10px; padding-right: 10px;
}
/* Result-cell behaviour mirrors the audit Detail cell: collapsed
   single-line with ellipsis; clicking the row toggles .expanded which
   the existing drawer-row pattern uses (already wired in live.js). */
table.t tr.history-row > td.history-result-cell {
  white-space: nowrap !important;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Defense-in-depth: scroll inside the tile if widths still underflow on a narrower viewport. */
section.tile:has(table.t.history-table) { overflow-x: auto; }
table.t tr.audit-row > td.audit-detail-cell {
  white-space: nowrap;
}
table.t tr.audit-row-expandable {
  cursor: pointer;
}
table.t tr.audit-row-expandable:hover > td {
  background: var(--bg-2);
}
table.t tr.audit-drawer-row.hidden { display: none; }
table.t tr.audit-drawer-row > td.audit-drawer-cell {
  background: var(--bg-1);
  padding: 8px 12px;
  white-space: pre-wrap;
  word-break: break-word;
  border-top: 0;
}
table.t tr.audit-drawer-row .audit-drawer-full {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 12px;
  color: var(--fg-0);
  line-height: 1.45;
}

/* 2026-05-17 — table sizing: body rows 14 px, headers same 14 px but
   weight 700 + uppercase + letter-spacing for visual hierarchy. Padding
   bumped accordingly so 14-px text doesn't crowd against the borders. */
table.t {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  font-size: 14px;
  table-layout: auto;
}
table.t th, table.t td {
  text-align: left;
  padding: 5px 12px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
table.t th {
  background: color-mix(in srgb, var(--accent) 10%, var(--bg-1));
  color: var(--fg-1);
  font-weight: 700;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding-top: 7px;
  padding-bottom: 7px;
}
table.t tr:last-child td { border-bottom: 0; }
table.t tr:hover td { background: var(--bg-2); }

/* 2026-05-24 — path-cell: hard-cap the Changes / Uploads path column
   so a 170-char caldav .ics path can't push Size / SHA / Timestamp off
   screen. JS truncates the string at 100 chars; CSS caps the visual
   width at 540 px and clips with ellipsis on top (defense in depth in
   case a future renderer forgets to truncate). Full path stays in the
   cell's title= attribute so hovering reveals the original string. */
table.t td.path-cell {
  max-width: 540px;
  width: 540px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
table.t td.path-cell code {
  font-size: 12px;
  white-space: nowrap;
}
/* 2026-05-23 — Labelled buttons (.btn-primary/.btn-secondary/etc.) get
   a global `margin-top: 3px` to space them in stacked layouts. Inside
   a table cell that margin pushes the row taller than rows whose
   Actions cell uses icon-only buttons. Zero it out so all table rows
   share the same vertical rhythm. */
table.t td .btn-primary,
table.t td .btn-secondary,
table.t td .btn-danger,
table.t td .btn-ghost,
table.t td .btn-warn,
table.t td .btn-sm { margin-top: 0; vertical-align: middle; }

/* 2026-05-23 — List Accounts modal (Servers drawer → Edit → List Accounts).
   Hard size cap at 40vw × 70vh per operator spec. Table body scrolls;
   header + footer stay pinned so default 20 rows are visible without
   the operator hunting for the Save button.

   Layout:
     .am-modal       fixed backdrop covering the viewport
       .am-card     centred card, capped at 40vw × 70vh
         .am-close  top-right × icon button (.icon-btn state machine)
         .am-h      title
         .am-sub    subtitle (defaults notice)
         .am-table-wrap  scroll container — table.t inside
         .am-foot   selection counts + Cancel + Save */
.am-modal {
  position: fixed; inset: 0;
  z-index: 9000;
  background: rgba(0, 0, 0, 0.55);
  display: flex; align-items: center; justify-content: center;
}
.am-card {
  position: relative;
  width: 40vw;  min-width: 480px;  max-width: 40vw;
  max-height: 70vh;
  background: var(--bg-1);
  color: var(--fg-0);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.18);
  padding: 18px 22px;
  display: flex; flex-direction: column;
}
.am-close {
  position: absolute;
  top: 10px; right: 10px;
  width: 28px; height: 28px;
  display: flex; align-items: center; justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--fg-2);
  font-size: 20px; line-height: 1;
  cursor: pointer; padding: 0;
}
.am-close:hover {
  background: var(--err); border-color: var(--err); color: #fff;
}
.am-close:active {
  background: color-mix(in srgb, var(--err) 85%, #000);
  border-color: color-mix(in srgb, var(--err) 85%, #000);
  color: #fff;
}
.am-h {
  margin: 0 0 4px 0;
  font-size: 16px; font-weight: 600;
  color: var(--fg-0);
  padding-right: 32px;
}
.am-sub {
  margin: 0 0 12px 0;
  font-size: 12px; color: var(--fg-2);
}
.am-table-wrap {
  flex: 1 1 auto;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: var(--bg-0);
}
.am-table { margin: 0; border-radius: 0; border: 0; }
.am-table thead th {
  position: sticky; top: 0; z-index: 1;
}
.am-table .am-col-check { width: 32px; text-align: center; padding-left: 12px; padding-right: 4px; }
.am-table .am-col-num   { width: 90px; text-align: right; }
.am-table input[type="checkbox"] {
  width: 14px; height: 14px; margin: 0;
  accent-color: var(--accent); cursor: pointer;
}
.am-foot {
  margin-top: 12px;
  display: flex; align-items: center; gap: 10px;
}
.am-foot .am-counts { color: var(--fg-2); font-size: 12px; margin-right: auto; }

/* 2026-05-23 — Suspended row: greyed name + small chip; checkbox left
   to operator to opt-in. Master "select all" deliberately skips these
   rows (see live.js::openAccountsModal). */
.am-table .am-row-suspended td { color: var(--fg-3); }
/* 2026-05-24 — ITEM-014: dirty indicator for rows that have been
   staged (checkbox toggled away from loaded value) but not yet
   Saved. Subtle left-edge accent so the operator sees which rows
   the next Save will commit. */
.am-table .am-row-dirty td:first-child {
  box-shadow: inset 3px 0 0 var(--accent);
}
/* 2026-05-24 — ITEM-016: brief flash on the first row that failed
   the partial-Save batch; scrollIntoView pairs with this so the
   operator's eye lands on the right row. */
@keyframes am-row-failed-save-flash {
  0%, 100% { background-color: transparent; }
  20%, 80% { background-color: color-mix(in srgb, var(--err) 22%, transparent); }
}
.am-table .am-row-failed-save td {
  animation: am-row-failed-save-flash 1.6s ease-in-out 2;
}
.am-chip-suspended {
  margin-left: 8px;
  padding: 0 6px;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  border-radius: 3px;
  border: 1px solid color-mix(in srgb, var(--warn) 35%, transparent);
  background:    color-mix(in srgb, var(--warn) 12%, transparent);
  color:         var(--warn);
  vertical-align: middle;
}
.empty { color: var(--fg-3); padding: 14px; text-align: center; border: 1px dashed var(--border); border-radius: var(--radius); font-size: 14px; }

/* 2026-05-16 — pills/badges flattened to plain inline text per operator
   request. Status colour is conveyed via colour only, no background pill. */
.badge {
  display: inline;
  padding: 0;
  background: transparent;
  border: 0;
  font-weight: 500;
  font-size: inherit;
  color: inherit;
}
.badge-ok,  .status-success, .status-done, .status-added    { color: var(--ok); }
.badge-warn, .status-partial, .status-completed_with_errors, .status-running, .status-claimed, .status-warn, .status-modified, .status-suspended { color: var(--warn); }
.badge-err, .status-failed,  .status-offline, .status-denied, .status-error, .status-expired, .status-deleted, .status-cancelled { color: var(--err); }
.badge-info, .status-active, .status-queued  { color: var(--accent); }
.badge-muted, .status-unknown                 { color: var(--fg-2); }
/* 2026-05-29 — generic muted label used inline in cells where a
   value is absent but the absence is meaningful (e.g. "Agent
   unavailable" in the IP column when the agent hasn't heartbeat'd
   yet). Italic + tertiary colour so it visually reads as a state,
   not a real value. */
.muted { color: var(--fg-2); font-style: italic; font-size: 12px; }

/* 2026-05-29 — Enroll Server wizard cosmetics. Keeps the form
   visually consistent with the existing wizard styles
   (.wizard-form / .wiz-step / .wiz-controls already defined). */
.enr-derived {
  padding: 8px 12px;
  background: color-mix(in srgb, var(--accent) 7%, transparent);
  border-left: 3px solid var(--accent);
  border-radius: 4px;
  font-size: 12px;
  line-height: 1.7;
}
.enr-derived-k { color: var(--fg-2); margin-right: 6px; }

/* 2026-05-29 — Enroll Server modal title: bigger and louder than the
   default 16px modal h2, with a one-line subtitle below. The default
   .cb-modal-card h2 sized at 16px reads as a section header rather
   than a wizard title; the operator couldn't tell what the wizard
   was for. 22/600 sits comfortably with the modal padding and the
   subtitle line uses the same .sub greyscale the page-h pattern uses. */
.cb-modal-card .cb-modal-title-lg {
  margin: 0 0 4px 0;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--fg-0);
  padding-right: 32px;
}
.cb-modal-card .cb-modal-sub {
  margin: 0 0 14px 0;
  font-size: 12px;
  color: var(--fg-2);
  line-height: 1.4;
}

/* 2026-05-29 — Enroll Server wizard: 2-column field grid + tighter
   spacing so the whole form fits in the 92vh-capped modal card
   without a scrollbar. Spans for fields that read better full-width
   (Hostname, Organization, derived block) via .enr-span2. */
.enr-form-section { margin: 0; }
.enr-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 14px;
  row-gap: 8px;
  align-items: end;
}
.enr-grid > label,
.enr-grid > .enr-derived { margin: 0; }
.enr-grid > label > input,
.enr-grid > label > select { width: 100%; }
.enr-span2 { grid-column: 1 / -1; }

/* 2026-05-29 — Enroll Server Step 2 acknowledgement row + Step 3
   summary list + Step 3 warning text. All scoped to .enr-* so they
   don't bleed into other wizards. */
.enr-ack {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 12px 0 0 0;
  padding: 10px 12px;
  background: color-mix(in srgb, var(--warn) 8%, transparent);
  border-left: 3px solid var(--warn);
  border-radius: 4px;
  font-size: 12px;
  line-height: 1.5;
  cursor: pointer;
}
.enr-ack input[type="checkbox"] { margin-top: 2px; flex-shrink: 0; }
.enr-no-contact-warn {
  margin: 12px 0 0 0;
  padding: 10px 12px;
  background: color-mix(in srgb, var(--err) 10%, transparent);
  border-left: 3px solid var(--err);
  border-radius: 4px;
  font-size: 12px;
  color: var(--fg-0);
  line-height: 1.5;
}
.enr-bullet {
  margin: 6px 0 10px 18px;
  padding: 0;
  font-size: 12px;
  color: var(--fg-1);
  line-height: 1.7;
}
.enr-bullet li { margin: 0; }
.enr-warn {
  color: var(--warn);
  font-size: 12px;
  margin: 6px 0 14px 0;
  line-height: 1.5;
}
.enr-step3-controls { margin-top: 8px; justify-content: flex-start; }
.enr-nav-controls { margin-top: 16px; }
.enr-done { margin-top: 14px; }
.wiz-done-title { margin: 0 0 12px 0; font-size: 18px; }
.token-row {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.token-row .token-once {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  padding: 4px 8px;
  background: color-mix(in srgb, var(--fg) 10%, transparent);
  border-radius: 4px;
  user-select: all;
}
.enr-token-btn { padding: 5px 10px; font-size: 12px; min-width: 80px; }
.token-warn {
  margin-top: 8px;
  font-size: 12px;
  color: var(--warn);
  line-height: 1.5;
}
.next-steps { margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap; }

/* 2026-05-28 — .badge-pill modifier: renders as a visible pill (rounded
   tinted background) instead of bare coloured text. Use alongside an
   intent class (badge-ok / badge-warn / badge-err / badge-info / badge-muted).
   Applied to status badges in page-h headers where the bare-text form is
   not distinctive enough at the 22 px H1 size. */
.badge-pill {
  display: inline-block;
  padding: 3px 12px;
  border-radius: 12px;
  background: color-mix(in srgb, currentColor 15%, transparent);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.5;
  vertical-align: middle;
}
.page-h h1 .badge-pill { margin-left: 50px; }   /* gap from h1 title */

/* 2026-05-16 — small button variant for inline action rows + tight forms.
   2026-05-17 — Trimmed horizontal padding from 20→10 px so the row's
   Actions column no longer carries dead space. */
.btn-sm {
  padding: 5px 10px;
  font-size: 13px;
}
.inline-form { display: inline-block; margin: 0 4px 0 0; }
.actions { white-space: nowrap; text-align: right; }
.inline-form:last-child { margin-right: 0; }
/* 2026-05-17 — Pin the Servers-table Actions column to the buttons'
   intrinsic width so the leftover row width can't bloat it any more.
   The three buttons (Ping · Run backup · Reload policy) at the new
   compact padding total ~250 px including inter-button gaps. */
table.t.srv-table td.actions,
table.t.srv-table th:last-child {
  width: 1%;          /* shrink-to-fit in table-layout:auto */
  white-space: nowrap;
}

/* 2026-05-17 — Standard button size — apply to any labelled button
   that should match Services Start/Stop dimensions. See
   memory/feedback_standard_button_size.md. Existing `.btn-svc` carries
   the same numbers; this is the universal alias. */
.btn-standard {
  display: inline-block;
  min-width: 96px;
  padding: 6px 12px;
  border: 1px solid transparent;
  border-radius: 5px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
}
/* Apply the standard dimensions to every labelled button on the Global
   tile (Save / Close / Edit, regardless of intent class). 10 px gap
   between adjacent buttons in the .ovr-actions row. */
.ovr-actions { display: inline-flex; gap: 10px; align-items: center; }
.ovr-actions .btn-primary,
.ovr-actions .btn-ghost,
.ovr-actions .btn-danger,
.ovr-actions .btn-warn {
  min-width: 96px;
  padding: 6px 12px;
  margin-top: 0;
  font-size: 13px;
  font-weight: 600;
}

/* Action bar / forms / detail */
.action-bar { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 12px 0; }
.field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 9px 12px; }
.field-grid .full { grid-column: 1 / -1; }
@media (max-width: 700px) { .field-grid { grid-template-columns: 1fr; } }
.dl { display: grid; grid-template-columns: 160px 1fr; gap: 5px 12px; }
.dl dt { color: var(--fg-2); font-size: 10px; }
.dl dd { margin: 0; color: var(--fg-0); }
/* 2026-05-18 — Three-column equal grid for session-detail header.
   Outer grid: 3 equal columns of pair-containers. Inner pair:
   left-aligned label + value on the same baseline. Predictable
   alignment across rows because both columns of every pair come
   from the same inner-grid template (auto-width label, fluid value)
   resolved per cell, and the outer 3 columns are equal 1fr each. */
/* 2026-05-18 — Three equal-width columns that fill the parent tile.
   `width: 100%` is explicit so the grid never collapses to content
   width when an ancestor doesn't constrain it. Inner pair uses flex
   so label sits flush-left next to its value with a fixed gap — no
   auto-column tricks, no inherited text-align. !important on the
   grid template + width because the cascade had a couple of legacy
   .dl rules competing. */
.dl.dl-3col {
  display: grid !important;
  grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
  column-gap: 24px;
  row-gap: 10px;
  width: 100%;
  text-align: left;
}

/* 2026-05-24 — Overall progress: ONE 4-column × 3-row grid (not a
   wrapper around a nested grid). Every cell shares the same grid
   rows so the 4 columns line up horizontally. Column 4 contains
   only the 3 accounts metrics (Total / Completed / Pending), one
   per row. Previous attempt nested a flex column inside the wrapper,
   which made column 4's rows pack tightly (no shared row line with
   the dl-3col on the left) — fixed by collapsing into a single grid. */
.dl.overall-meta-4col {
  display: grid !important;
  grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
  column-gap: 24px;
  row-gap: 10px;
  width: 100%;
  margin: 0;
  text-align: left;
}
.dl.overall-meta-4col .dl-pair {
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
}
.dl.overall-meta-4col .dl-pair dt {
  flex: 0 0 auto;
  color: #787c84;
  font-size: 13px;
  margin: 0;
  white-space: nowrap;
}
.dl.overall-meta-4col .dl-pair dd {
  flex: 1 1 auto;
  color: var(--fg-0);
  font-size: 13px;
  margin: 0;
}
/* 2026-05-24 — removed dead .overall-meta-accounts rules (right-align,
   space-between, font-weight 600) — that was an improvisation. The
   4th column now uses the same .dl-pair styling as dl-3col via the
   .overall-meta-4col cascade above. */
.dl.dl-3col .dl-pair {
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
}
.dl.dl-3col .dl-pair dt {
  flex: 0 0 auto;
  color: var(--fg-2);
  font-size: 11px;
  margin: 0;
  text-align: left;
  white-space: nowrap;
}
.dl.dl-3col .dl-pair dd {
  flex: 1 1 auto;
  color: var(--fg-0);
  font-size: 13px;
  margin: 0;
  text-align: left;
  word-break: break-word;
}

/* 2026-05-16 — dashboard infographic tile + chart container */
.chart-host { width: 100%; min-height: 220px; }
.chart-host svg { display: block; max-width: 100%; height: 220px; }
.grid.charts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 900px) {
  .grid.charts { grid-template-columns: 1fr; }
}

/* ──────────────────────────────────────────────────────────────────
 * 2026-05-17 — Dashboard redesign: proper card system.
 *   .card        — generic container with header + body
 *   .card-h      — flex header: title left, subtitle right
 *   .kpi-card    — featured stat (top row): glyph + label + value, accent stripe
 *   .stat-card   — secondary stat (sessions-today): compact, label-over-value
 *   .empty-state — graceful "no data" centre block
 *   .focal-value — hero number for "Files uploaded (7d)"-style tiles
 * ────────────────────────────────────────────────────────────────── */
.card {
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 16px 18px;
  margin-top: 16px;
}
.card-h {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px;
  margin: 0 0 12px 0;
}
.card-h h3 {
  margin: 0;
  font-size: 14px; font-weight: 600; color: var(--fg-0);
  letter-spacing: -0.1px;
}
.card-h-sub {
  font-size: 11px; color: var(--fg-2);
  text-transform: uppercase; letter-spacing: 0.6px;
}

/* Featured KPI cards (top row) */
.kpi-row { margin-top: 4px; }
.kpi-card {
  position: relative;
  display: flex; align-items: center; gap: 14px;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 8px;
  /* 2026-05-18 — symmetric horizontal padding now that the coloured
     left stripe is gone. */
  padding: 16px 18px;
  overflow: hidden;
  transition: border-color 0.15s ease;
}
/* 2026-05-18 — Left coloured stripe removed. Intent colour now lives
   only on the icon tint to keep the card visually quieter. */
.kpi-card::before { content: none; }
.kpi-card:hover { border-color: var(--border-strong); }

.kpi-icon {
  flex: 0 0 36px;
  width: 36px; height: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 20px; line-height: 1;
  border-radius: 6px;
  background: var(--bg-2);
  color: var(--fg-1);
}
.kpi-card.kpi-primary .kpi-icon { color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, var(--bg-2)); }
.kpi-card.kpi-ok      .kpi-icon { color: var(--ok);     background: color-mix(in srgb, var(--ok)     14%, var(--bg-2)); }
.kpi-card.kpi-warn    .kpi-icon { color: var(--warn);   background: color-mix(in srgb, var(--warn)   14%, var(--bg-2)); }
.kpi-card.kpi-err     .kpi-icon { color: var(--err);    background: color-mix(in srgb, var(--err)    14%, var(--bg-2)); }

.kpi-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.kpi-label {
  font-size: 11px; font-weight: 600; color: var(--fg-2);
  text-transform: uppercase; letter-spacing: 0.6px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.kpi-value {
  font-size: 26px; font-weight: 700;
  /* 2026-05-18 — Coral (--running) so the big number on every KPI
     card matches the running-spinner colour used elsewhere across the
     dashboard (the Overall progress bar, the spinner ring in the
     Servers and Sessions activity columns). */
  color: var(--running);
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}

/* Compact stat strip (sessions-today row) */
.stat-row { margin-top: 12px; }
.stat-card {
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 14px 16px;
  display: flex; flex-direction: column; gap: 4px;
  border-left-width: 3px;
}
.stat-card.stat-primary { border-left-color: var(--accent); }
.stat-card.stat-ok      { border-left-color: var(--ok); }
.stat-card.stat-warn    { border-left-color: var(--warn); }
.stat-card.stat-err     { border-left-color: var(--err); }
.stat-label {
  font-size: 11px; color: var(--fg-2);
  text-transform: uppercase; letter-spacing: 0.6px; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.stat-value {
  font-size: 22px; font-weight: 700; color: var(--fg-0);
  line-height: 1.1; font-variant-numeric: tabular-nums;
}
.stat-card.stat-primary .stat-value { color: var(--accent); }
.stat-card.stat-ok      .stat-value { color: var(--ok); }
.stat-card.stat-warn    .stat-value { color: var(--warn); }
.stat-card.stat-err     .stat-value { color: var(--err); }

/* Graceful empty state inside a table */
.t .t-empty { padding: 0; }
.empty-state {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; padding: 36px 12px; color: var(--fg-2);
  text-align: center;
}
.empty-glyph {
  font-size: 24px; line-height: 1;
  width: 44px; height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--bg-2); color: var(--fg-2);
  margin-bottom: 4px;
}
.empty-title { font-size: 14px; font-weight: 600; color: var(--fg-1); }
.empty-sub   { font-size: 12px; color: var(--fg-2); max-width: 420px; line-height: 1.5; }

/* Focal big-number tile */
.stat-focal { display: flex; flex-direction: column; }
.focal-value {
  font-size: 44px; font-weight: 700; color: var(--accent);
  line-height: 1.05; margin: 4px 0 8px 0;
  font-variant-numeric: tabular-nums;
}
.focal-sub { color: var(--fg-2); font-size: 12px; line-height: 1.55; margin: 0; }

@media (max-width: 700px) {
  .grid.cards-4 { grid-template-columns: 1fr 1fr; }
  .kpi-value    { font-size: 22px; }
  .stat-value   { font-size: 19px; }
  .focal-value  { font-size: 32px; }
}

/* 2026-05-17 — Themed confirmation modal (replaces window.confirm).
   The <dialog> element handles backdrop + focus trap natively. */
.confirm-dialog {
  border: 1px solid var(--border);
  background: var(--bg-1);
  color: var(--fg-0);
  border-radius: 10px;
  padding: 0;
  min-width: 360px;
  max-width: 90vw;
  box-shadow: 0 20px 60px rgba(0,0,0,0.45);
}
.confirm-dialog::backdrop {
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(2px);
}
.confirm-body {
  padding: 20px 22px 16px 22px;
  display: flex; flex-direction: column; gap: 8px;
}
.confirm-title {
  font-size: 15px; font-weight: 700; color: var(--fg-0);
  letter-spacing: -0.1px;
}
.confirm-msg {
  font-size: 13px; color: var(--fg-1); line-height: 1.55;
  margin: 4px 0 12px 0;
}
.confirm-row {
  display: flex; justify-content: flex-end; gap: 8px;
}
.confirm-row .btn-ghost,
.confirm-row .btn-primary {
  margin-top: 0;
  min-width: 92px;
}

/* 2026-05-17 — tab strip (used on session-detail for filtering Changes). */
.tab-row { display: flex; gap: 4px; margin: 4px 0 12px 0; flex-wrap: wrap; }
.tab {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--fg-1);
  padding: 5px 12px;
  font: inherit; font-size: 13px;
  border-radius: 6px;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.tab:hover { background: var(--bg-2); }
.tab.active {
  background: color-mix(in srgb, var(--accent) 18%, var(--bg-2));
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
  color: var(--fg-0);
}

/* 2026-05-17 — log viewer (session-detail). Monospace box, auto-tail. */
.log-toolbar {
  display: flex; align-items: center; gap: 8px;
  margin: 4px 0 8px 0; font-size: 12px;
  color: var(--fg-2);
  flex-wrap: wrap;
}
.log-toolbar label { font-size: 12px; color: var(--fg-2); margin: 0; }
.log-toolbar select,
.log-toolbar input {
  background: var(--bg-2); color: var(--fg-0);
  border: 1px solid var(--border); border-radius: 4px;
  padding: 3px 8px;
  font: inherit; font-size: 12px;
}
.log-toolbar input { min-width: 220px; }
.log-tail-marker {
  margin-left: auto;
  font-size: 11px; color: var(--fg-2);
  text-transform: uppercase; letter-spacing: 0.5px;
}
.log-viewer {
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 10px;
  max-height: 360px;
  overflow-y: auto;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 12px;
  line-height: 1.55;
}
.log-line { white-space: pre-wrap; word-break: break-all; }
.log-line .log-ts    { color: var(--fg-2); margin-right: 8px; }
.log-line .log-ev    { color: var(--accent); font-weight: 600; margin-right: 8px; }
.log-line .log-lvl   { display: inline-block; min-width: 38px; font-weight: 600; margin-right: 8px; }
.log-line .log-lvl-info  { color: var(--fg-1); }
.log-line .log-lvl-warn  { color: var(--warn); }
.log-line .log-lvl-error { color: var(--err); }
.log-line .log-msg   { color: var(--fg-0); }
.log-line .log-fld   { color: var(--fg-2); margin-left: 6px; }
.log-empty { color: var(--fg-3); font-style: italic; }

/* 2026-05-16 — progress bar + status badge colours + sticky table heads */
.progress-row { display: flex; align-items: center; gap: 10px; }
.progress-row progress {
  appearance: none;
  -webkit-appearance: none;
  height: 7px;
  width: 220px;
  border-radius: 4px;
  overflow: hidden;
  background: color-mix(in srgb, var(--fg-2) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--fg-2) 30%, transparent);
}
.progress-row progress::-webkit-progress-bar { background: transparent; }
.progress-row progress::-webkit-progress-value { background: var(--accent); }
.progress-row progress::-moz-progress-bar { background: var(--accent); }
.progress-label { font-variant-numeric: tabular-nums; font-size: 10px; color: var(--fg-1); min-width: 42px; }

/* 2026-05-18 — Overall progress (session header) variant. Wider bar
   so the operator sees session-level progress at a glance: 10 px
   tall, 60 % of the section width, percentage label at the end.
   Per-account progress bars keep the compact 7 px / 220 px form.
   The wrapper .overall-progress-head puts the section title and the
   bar on a single row so the page header doesn't waste a vertical
   strip on the progress widget. */
/* 2026-05-18 — Use CSS grid for the Overall-progress row instead of
   flexbox. Grid is immune to the flex-basis-auto / min-content quirks
   that were squeezing the progress-row into a content-sized stub
   when something further down the cascade reset its `flex` shorthand.
   `auto 1fr` pins the title at its natural width and forces the
   progress row to fill the rest, guaranteed. */
.overall-progress-head {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  /* 2026-05-18 — Title sits at the same left position as other tile
     headers (the parent `.tile` already supplies 16 px left padding).
     A 30 px gap separates the title from the progress bar; the bar
     itself sits within the .tile's right padding so the bar/percentage
     don't kiss the tile edge. */
  gap: 30px;
  width: 100%;
}
.overall-progress-head .tile-h { margin: 0; }
.overall-progress-head .progress-row-overall {
  width: 100%;
  min-width: 0;
}
.progress-row.progress-row-overall {
  display: flex;
  align-items: center;
  gap: 10px;
}
.progress-row.progress-row-overall progress {
  height: 10px;
  width: 100%;
  flex: 1 1 auto;
  min-width: 0;
  border-radius: 5px;
}
/* 2026-05-18 — Overall progress bar uses the running-spinner accent
   (coral) so the bar's colour matches the .spinner-claimed circle that
   marks the same session as running. Per-account progress bars keep
   the default --accent (blue). */
.progress-row.progress-row-overall progress::-webkit-progress-value { background: var(--running); }
.progress-row.progress-row-overall progress::-moz-progress-bar      { background: var(--running); }
.progress-row.progress-row-overall .progress-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--fg-1);
  min-width: 60px;
}
/* 2026-05-16 — flat status colours (no pill background). The .badge rule
   below makes the element render inline; here we only set the text colour. */
.t thead th { position: sticky; top: 0; background: color-mix(in srgb, var(--accent) 10%, var(--bg-1)); z-index: 1; }

/* 2026-05-16 — gateway-reachability banner + dashboard tile heading */
.banner {
  padding: 7px 11px;
  border-radius: 6px;
  margin-bottom: 12px;
  font-size: 11px;
}
.banner-warn {
  background: color-mix(in srgb, var(--warn) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--warn) 45%, transparent);
  color: var(--fg-0);
}
.tile-h { margin: 0 0 7px 0; font-size: 16px; font-weight: 600; }

/* 2026-05-17 — Servers list: chevron + selection + accordion drawer.
   Visual model mirrors firecage profile rows: collapsed row shows a
   right-pointing chevron; clicking it rotates the chevron 90° and slides
   a full-width detail row out beneath the host row. Selected/expanded
   rows are tinted with the accent so the active row is unambiguous. */
.page-h-right {
  margin-left: auto;
  color: var(--fg-2);
  font-size: 12px;
  display: flex;
  align-items: center;
  gap: 12px;
}
.page-h-right .btn-sm { font-size: 13px; }
/* 2026-05-22 — Page-header CTA buttons share a min-width so labels of
   different lengths (e.g. "+ Enroll server" vs "+ Create Backup") line
   up at identical pill widths across pages. text-align centres the
   label inside the wider pill. */
.page-h-right > a.btn-primary,
.page-h-right > button.btn-primary,
.page-h-right > a.btn-secondary,
.page-h-right > button.btn-secondary {
  min-width: 160px;
  text-align: center;
}
/* 2026-05-17 — 50 px right margin on the header-row CTAs so neither
   "+ Enroll server" (Servers page) nor "← Back to Servers" (Enroll page)
   sits flush against the content gutter. */
.page-h-right > a.btn-primary,
.page-h-right > a.btn-sm,
.page-h > a.btn-ghost {
  margin-right: 50px;
}
table.t.srv-table th.col-check,
table.t.srv-table td.col-check { width: 32px; padding-left: 12px; padding-right: 4px; }
table.t.srv-table th.col-chev,
table.t.srv-table td.col-chev  { width: 24px; padding-left: 4px;  padding-right: 4px; cursor: default; }
table.t.srv-table input[type="checkbox"] {
  width: 14px; height: 14px; margin: 0; vertical-align: middle; cursor: pointer;
  accent-color: var(--accent);
}
.chev {
  display: inline-block;
  width: 14px; height: 14px;
  text-align: center;
  font-size: 10px;
  line-height: 14px;
  color: var(--accent);
  cursor: pointer;
  transition: transform 0.15s ease;
  user-select: none;
}
.chev.open { transform: rotate(90deg); }
.srv-row.open td {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}
.srv-detail-row.hidden { display: none; }
.srv-detail-row td.srv-detail-cell {
  background: color-mix(in srgb, var(--accent) 6%, transparent);
  border-top: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  /* 2026-05-28 — Extra bottom padding so the absolutely-positioned action
     stack (Edit Ownership in particular) has breathing room from the
     drawer's bottom border instead of crashing into it. */
  padding: 14px 18px 26px;
}
.srv-detail {
  /* 2026-05-28 — Three column-pairs of (dt | dd) separated by 100 px
     gutters. Layout:
       col 1 dt 180 px  · col 2 dd 200 px  · col 3 gutter 100 px
       col 4 dt 180 px  · col 5 dd 200 px  · col 6 gutter 100 px
       col 7 dt 180 px  · col 8 dd auto
     Column-gap is 6 px globally so labels and values sit CLOSE within
     each pair (operator spec); the visual separation between pairs
     comes from the explicit 100 px gutter columns 3 + 6.

     Live.js emits pairs ROW-MAJOR (see comment above the pair() calls
     in detailRow). nth-of-type(3n+1) → col 1, nth-of-type(3n+2) →
     col 4, nth-of-type(3n) → col 7. */
  display: grid;
  /* 2026-05-28 — dt columns: cols 1 & 4 = 100 px, col 7 = 180 px (room
     for "AVERAGE BACKUP DURATION"). dd columns = 200 px. Inter-pair
     gutters = 50 px. Column-gap forced to 5 px so the ":" sits exactly
     5 px from the value side. Labels right-align so they hug the colon. */
  grid-template-columns: 115px 200px 50px 115px 200px 50px 180px auto;
  justify-content: start;
  gap: 8px 5px;
  margin: 0;
}
.srv-detail dt {
  text-align: right;
}
.srv-detail dt::after {
  /* 2026-05-28 — Colon separator between label and value with 5 px on
     each side. 5 px LEFT comes from margin-left here; 5 px RIGHT comes
     from the grid column-gap (gap: 8px 5px above). */
  content: ":";
  margin-left: 5px;
}
.srv-detail dt:nth-of-type(3n+1) { grid-column: 1; }
.srv-detail dd:nth-of-type(3n+1) { grid-column: 2; }
.srv-detail dt:nth-of-type(3n+2) { grid-column: 4; }
.srv-detail dd:nth-of-type(3n+2) { grid-column: 5; }
.srv-detail dt:nth-of-type(3n)   { grid-column: 7; }
.srv-detail dd:nth-of-type(3n)   { grid-column: 8; }
.srv-detail dt {
  color: var(--fg-2);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  font-weight: 600;
}
.srv-detail dd { margin: 0; color: var(--fg-0); font-size: 14px; }
/* 2026-05-17 — Token blob + agent.yaml snippet on the enroll-success
   page. Both render in the mono font; the token blob wraps cleanly
   on small viewports without overflowing its tile. */
.token-blob {
  word-break: break-all;
  font-size: 13px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 6px 10px;
}
.agent-yaml-snippet {
  display: block;
  margin: 0 0 12px 0;
  padding: 12px 14px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 5px;
  font-size: 12.5px;
  line-height: 1.55;
  white-space: pre;
  overflow-x: auto;
}

/* 2026-05-17 — Copyable field-value: value text + 10 px gap + small
   clipboard icon that flashes accent-green when the value is copied. */
.copy-wrap { display: inline-flex; align-items: center; gap: 10px; }
.copy-icon {
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  color: var(--fg-2);
  display: inline-flex;
  align-items: center;
  line-height: 0;
  transition: color 0.15s ease, transform 0.15s ease;
}
.copy-icon:hover  { color: var(--accent); }
.copy-icon:focus  { outline: none; color: var(--accent); }
.copy-icon.copied { color: var(--ok); transform: scale(1.15); }
@media (max-width: 900px) {
  .srv-detail { grid-template-columns: 140px 1fr; }
}

.hidden { display: none !important; }

/* 2026-05-17 — Top-notification overlay (Atlas pattern).
   Adopted from ~/scripts/connectedworld/markdown/NOTIFICATION_STYLING_SYSTEM.md
   and refined to match the operator's reference screenshots:
     • Saturated solid backgrounds (blue / green / red / amber) with
       white text. No theme-mix tint; the same look across light/dark.
     • Centered body text; close button vertically centered on the right.
     • Latest notification renders on TOP of the vertical stack (template
       loops the messages in reverse order — newest first).
     • No auto-dismiss — alert persists until the operator clicks × or
       navigates to another page (Django's messages framework consumes
       them on next render, so navigation clears them automatically).
   Slide-down keyframe stays for the entry animation. */
/* 2026-05-17 — AWS-style "deck of cards" stacking per the Atlas
   notification spec at
   ~/scripts/connectedworld/markdown/NOTIFICATION_STYLING_SYSTEM.md §
   "AWS-Style Layered Notifications". All alerts share the same anchor
   point (top:0 inside the container) but each older one is shifted
   down + scaled smaller behind the newest, so only a sliver of colour
   peeks out from the bottom. nth-last-child(1) = newest = front. */
.top-notification-container {
  position: fixed;
  /* 2026-05-27 — Operator: slide down from the very TOP of the
     viewport so the toast overlays the nav bar (was below it via
     calc(var(--topbar-h) + 6px)). 8px breathing room so the toast
     doesn't kiss the browser chrome on macOS/Linux. */
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  width: 50%;
  max-width: 800px;
  /* Was 9999; bump above the top-bar's z-index so the toast covers
     the nav bar instead of slipping behind it. */
  z-index: 11000;
  pointer-events: none;
  /* No flex layout — alerts are absolutely positioned children. */
}
.top-notification-container .alert {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  pointer-events: auto;
  animation: slideDown 0.30s ease-out;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.28);
  padding: 13px 44px 13px 44px;
  border-radius: 5px;
  border: 0;
  font-size: 14px;
  font-weight: 500;
  line-height: 1.4;
  color: #ffffff;
  text-align: center;
  transition: transform 0.18s ease, opacity 0.18s ease;
}
/* Deck-of-cards stack rules. Cards keep CONSTANT dimensions (no scale);
   each older card is shifted down AND to the right by a small offset
   so its top + right edges peek out from behind the front card. */
.top-notification-container .alert:nth-last-child(1) {
  z-index: 10;
  transform: translate(0, 0);
  opacity: 1;
}
.top-notification-container .alert:nth-last-child(2) {
  z-index: 9;
  transform: translate(7px, 7px);
  opacity: 1;
}
.top-notification-container .alert:nth-last-child(3) {
  z-index: 8;
  transform: translate(14px, 14px);
  opacity: 1;
}
.top-notification-container .alert:nth-last-child(4) {
  z-index: 7;
  transform: translate(21px, 21px);
  opacity: 0.95;
}
.top-notification-container .alert:nth-last-child(n+5) {
  z-index: 6;
  transform: translate(28px, 28px);
  opacity: 0;
  pointer-events: none;
}
.top-notification-container .alert-body { display: block; }
.alert-success, .alert-ok { background: #198754; }   /* solid green */
.alert-error, .alert-danger, .alert-err { background: #b21f44; } /* solid crimson */
.alert-warning, .alert-warn { background: #d97706; color: #ffffff; } /* amber */
.alert-info               { background: #1d72e0; }   /* solid blue */
.top-notification-container .btn-close {
  position: absolute;
  top: 50%;
  right: 10px;
  transform: translateY(-50%);
  background: transparent;
  border: 0;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.85;
  padding: 4px 8px;
  color: #ffffff;
}
.top-notification-container .btn-close:hover { opacity: 1; }
@keyframes slideDown {
  from { opacity: 0; transform: translateY(-22px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (max-width: 800px) {
  .top-notification-container {
    left: 8px; right: 8px;
    transform: none;
    width: auto;
    max-width: none;
  }
}

/* 2026-05-17 — Activity cell on the Servers page: a small revolving
   circle next to a short status label ("Queued: run_backup" /
   "Running: run_backup"). The spinner is a single CSS keyframe — no
   image, no JS animation tick. Queued spins blue (accent); claimed/
   running spins yellow (warn) so the operator can tell at a glance
   whether the agent has picked the work up yet. */
/* 2026-05-17 — Activity column reserves a fixed 280 px (≈ +75 % over
   the previous auto-sized width) so the spinner + "Queued: run_backup"
   / "Running: run_backup" / future longer phase labels never wrap and
   the Actions column always sits at the same horizontal position
   regardless of whether any agent is busy. min/max-width pin it. */
table.t.srv-table th.col-activity,
table.t.srv-table td.activity-cell {
  width: 230px;
  min-width: 230px;
  max-width: 230px;
}
/* 2026-05-25 — Schedule column squeeze. Tighten the horizontal padding
   on the columns adjacent to Schedule (Last backup, Result, Activity,
   Action) so the Schedule cell has breathing room without expanding
   the whole table. The Schedule cell itself gets a touch more
   horizontal padding and white-space:nowrap so the formatted string
   ("Hourly @ :15 min, Daily 02:00 …") never wraps to two lines. */
table.t.srv-table td.col-lastbackup,
table.t.srv-table td.col-result,
table.t.srv-table td.actions,
table.t.srv-table td.activity-cell {
  padding-left: 6px;
  padding-right: 6px;
}
table.t.srv-table td.sched-cell {
  padding-left: 14px;
  padding-right: 14px;
  white-space: nowrap;
}
/* 2026-05-17 — Selection-count toolbar between the page header and
   the table. Pushes the counter all the way to the right edge of
   the content column. */
.srv-toolbar {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  margin: 0 0 8px 0;
  min-height: 18px;
}
/* 2026-05-17 — Ping is the only Action button with very short text;
   give it a min-width so it isn't visibly narrower than its siblings
   and so the Actions column carries an extra ~50 px overall. */
table.t.srv-table td.actions form[action$="/action/ping/"] .btn-ghost {
  min-width: 100px;
}
.activity-cell { white-space: nowrap; }
.activity-cell.empty-cell { color: var(--fg-3); }
.activity-label { margin-left: 6px; font-size: 13px; color: var(--fg-1); }
/* 2026-05-17 — Live file uploads tree-view. Account → folder → file
   with collapsible nodes, in-place search, mounted-row cap. Driven by
   static/js/upload_tree.js polling the cursor-mode uploads endpoint.
   Layout principles:
   • Toolbar row: search input + summary stats.
   • Tree container: scrollable (vertical), monospace path display,
     subtle row hover. Indent comes from inline padding-left on each
     row so virtual paging stays cheap.
   • Folder rows are clickable everywhere (caret + name + meta act as
     a single hit target).
   • Footer: total file count + bounding-window message. */
.upload-tree { border: 1px solid var(--border-1); border-radius: 6px; background: var(--bg-1); }
.ut-toolbar {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-bottom: 1px solid var(--border-1);
  background: color-mix(in srgb, var(--accent) 4%, var(--bg-1));
}
.ut-search {
  flex: 0 1 320px;
  padding: 6px 8px; font-size: 13px;
  border: 1px solid var(--border-1); border-radius: 4px;
  background: var(--bg-0); color: var(--fg-1);
}
.ut-stats { margin-left: auto; font-size: 12px; color: var(--fg-2); font-variant-numeric: tabular-nums; }
.ut-tree { max-height: 480px; overflow-y: auto; padding: 4px 0; font-size: 13px; }
.ut-row {
  display: flex; align-items: center; gap: 8px;
  width: 100%; box-sizing: border-box;
  padding: 4px 8px;
  cursor: default;
  border-left: 2px solid transparent;
}
.ut-folder-row, .ut-account-row { cursor: pointer; }
.ut-folder-row:hover, .ut-account-row:hover { background: var(--bg-2); border-left-color: var(--accent); }
.ut-caret { width: 14px; text-align: center; color: var(--fg-2); font-size: 11px; }
.ut-caret-leaf { color: var(--fg-3); }
.ut-name { font-family: ui-monospace, "JetBrains Mono", monospace; }
.ut-account-name { font-weight: 700; color: var(--fg-1); }
.ut-folder-name { color: var(--fg-1); }
.ut-file-name {
  color: var(--fg-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 60ch;
}
.ut-meta { margin-left: auto; color: var(--fg-2); font-size: 12px; font-variant-numeric: tabular-nums; }
.ut-action { margin-left: 8px; font-size: 11px; color: var(--fg-3); text-transform: uppercase; letter-spacing: 0.5px; }
.ut-more { color: var(--fg-3); font-style: italic; font-size: 12px; }
.ut-empty { color: var(--fg-3); font-style: italic; }
.ut-footer { padding: 6px 10px; border-top: 1px solid var(--border-1); font-size: 11px; color: var(--fg-3); }

/* 2026-05-17 — Bytes cell: right-aligned, tabular numerals so the
   ladder values (843 B / 47.2 KB / 184 MB / 2.1 GB) line up vertically
   when stacked in a table column. The header label stays "BYTES". */
.bytes-cell {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* 2026-05-17 — Activity cell deep-link: spinner + label form one
   clickable target that opens the live session detail page.
   2026-05-18 — Use var(--warn) to match the status-running badge so
   the running state reads consistently against the dark backdrop;
   var(--accent) blue had poor contrast in dark mode. Both colours
   come from the theme palette — no hardcoded values. */
.activity-link {
  display: inline-flex;
  align-items: center;
  color: var(--warn);
  text-decoration: none;
}
.activity-link:hover { text-decoration: underline; }
.activity-link .activity-label { color: var(--warn); }
.spinner {
  display: inline-block;
  width: 12px; height: 12px;
  border: 2px solid transparent;
  border-top-color: var(--accent);
  border-right-color: var(--accent);
  border-radius: 50%;
  vertical-align: middle;
  animation: spin 0.9s linear infinite;
}
.spinner.spinner-claimed {
  /* 2026-05-18 — Use the dedicated --running token (coral, same in both
     themes) so the revolving ring stands out against the dark backdrop
     and matches its lighter-theme counterpart. */
  border-top-color: var(--running);
  border-right-color: var(--running);
}
/* 2026-05-24 — ITEM-033: cell-level layout helper for the Backup
   Sessions page Live panel. Lays badge + spinner in a flex row so
   they sit on the same baseline with a small visible gap. */
table.t td.status-cell {
  display: flex;
  align-items: center;
  gap: 15px;
}
.spinner.status-spinner {
  /* Slightly larger than the default 12px so it reads at the same
     visual weight as the status badge text. */
  width: 14px; height: 14px;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* 2026-05-17 — Canonical icon-button color states.
   Square 28×28 (24×24 compact), outline-only at rest, filled on hover,
   darker filled on active. Each intent has its own colour.
   See: memory/feedback_icon_button_states.md */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px; height: 28px;
  padding: 0;
  border-radius: 4px;
  background: transparent;
  border: 1px solid currentColor;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
  margin-right: 6px;
  line-height: 0;
}
.icon-btn:focus { outline: none; box-shadow: 0 0 0 2px color-mix(in srgb, currentColor 35%, transparent); }
.icon-btn[disabled] { opacity: 0.45; cursor: not-allowed; pointer-events: none; }

/* 2026-05-17 — Per-intent edit-button colour. Defaults to the theme
   accent (blue) in light mode; overridden to a muted cyan in dark mode
   so the icon stays visible without going neon. */
:root, :root[data-theme="light"] {
  --edit-icon-color: var(--accent);
}
:root[data-theme="dark"] {
  --edit-icon-color: #22B8CF;       /* muted cyan, visible on dark bg */
}

.icon-btn.btn-edit  { color: var(--edit-icon-color); }
.icon-btn.btn-save  { color: var(--ok); }
.icon-btn.btn-close { color: var(--err); }
/* 2026-05-18 — Retry intent (Backup completed → failed row → Action).
   Uses --warn (yellow) which already signals "in-flight running" via
   the activity-link colour scheme, so retrying a failed account reads
   as "send it back into the running queue". */
.icon-btn.btn-retry { color: var(--warn); }

.icon-btn.btn-edit:hover  { background: var(--edit-icon-color); color: #fff; }
.icon-btn.btn-save:hover  { background: var(--ok);              color: #fff; }
.icon-btn.btn-close:hover { background: var(--err);             color: #fff; }
.icon-btn.btn-retry:hover { background: var(--warn);            color: #fff; }

.icon-btn.btn-edit:active  { background: color-mix(in srgb, var(--edit-icon-color) 85%, #000); border-color: color-mix(in srgb, var(--edit-icon-color) 85%, #000); color: #fff; }
.icon-btn.btn-save:active  { background: color-mix(in srgb, var(--ok)               85%, #000); border-color: color-mix(in srgb, var(--ok)               85%, #000); color: #fff; }
.icon-btn.btn-close:active { background: color-mix(in srgb, var(--err)              85%, #000); border-color: color-mix(in srgb, var(--err)              85%, #000); color: #fff; }
.icon-btn.btn-retry:active { background: color-mix(in srgb, var(--warn)             85%, #000); border-color: color-mix(in srgb, var(--warn)             85%, #000); color: #fff; }

/* 2026-05-19 — Servers-row Action column icon intents. Same state
   machine as btn-edit/save/close/retry: outline at rest, filled on
   hover, darker-filled on active. Per-intent colour:
     btn-ping   → accent  (neutral diagnostic)
     btn-run    → ok      (green, start work)
     btn-stop   → err     (red,  halt work)
     btn-reload → warn    (yellow, configuration change) */
.icon-btn.btn-ping   { color: var(--accent); }
.icon-btn.btn-run    { color: var(--ok); }
.icon-btn.btn-stop   { color: var(--err); }
.icon-btn.btn-reload { color: var(--warn); }
/* 2026-05-28 — Calendar icon for the Manage Backups row → Scheduler
   shortcut. Uses --info so it's visually distinct from the surrounding
   accent/ok/err/warn icons (none of which represent "open a side panel
   on a different page"). */
.icon-btn.btn-sched  { color: var(--info); }

.icon-btn.btn-ping:hover   { background: var(--accent); color: #fff; }
.icon-btn.btn-run:hover    { background: var(--ok);     color: #fff; }
.icon-btn.btn-stop:hover   { background: var(--err);    color: #fff; }
.icon-btn.btn-reload:hover { background: var(--warn);   color: #fff; }
.icon-btn.btn-sched:hover  { background: var(--info);   color: #fff; }

.icon-btn.btn-ping:active   { background: color-mix(in srgb, var(--accent) 85%, #000); border-color: color-mix(in srgb, var(--accent) 85%, #000); color: #fff; }
.icon-btn.btn-run:active    { background: color-mix(in srgb, var(--ok)     85%, #000); border-color: color-mix(in srgb, var(--ok)     85%, #000); color: #fff; }
.icon-btn.btn-stop:active   { background: color-mix(in srgb, var(--err)    85%, #000); border-color: color-mix(in srgb, var(--err)    85%, #000); color: #fff; }
.icon-btn.btn-reload:active { background: color-mix(in srgb, var(--warn)   85%, #000); border-color: color-mix(in srgb, var(--warn)   85%, #000); color: #fff; }
.icon-btn.btn-sched:active  { background: color-mix(in srgb, var(--info)   85%, #000); border-color: color-mix(in srgb, var(--info)   85%, #000); color: #fff; }

/* 2026-05-18 — Action column in the Backup completed tile. Fixed
   narrow width so the retry icon-button sits flush right; empty
   cells (success rows) take no visual real estate. */
.completed-table th.col-action,
.completed-table td.col-action {
  width: 56px;
  text-align: center;
  white-space: nowrap;
}

/* 2026-05-17 — Toggle switch for per-row override enable/disable. */
.toggle-switch {
  display: inline-block;
  position: relative;
  width: 38px;
  height: 20px;
  cursor: pointer;
  vertical-align: middle;
}
.toggle-switch input { position: absolute; opacity: 0; pointer-events: none; }
.toggle-slider {
  position: absolute; inset: 0;
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: 999px;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.toggle-slider::after {
  content: ""; position: absolute;
  top: 2px; left: 2px;
  width: 14px; height: 14px;
  background: #fff;
  border-radius: 50%;
  transition: transform 0.15s ease;
}
.toggle-switch input:checked + .toggle-slider {
  background: var(--accent);
  border-color: var(--accent);
}
.toggle-switch input:checked + .toggle-slider::after { transform: translateX(18px); }

/* 2026-05-17 — Policy per-host table: fixed widths on Override and
   Actions so the row geometry never shifts when Save/Close icons
   appear or disappear. Operator's spec: Actions +50 px over the
   previous shrink-fit, Override -50 px to compensate. */
.policy-hosts-table th.col-policy-override,
.policy-hosts-table td.col-policy-override {
  width: 100px;
  min-width: 100px;
  max-width: 100px;
  text-align: left;
}
.policy-hosts-table th.col-policy-actions,
.policy-hosts-table td.col-policy-actions {
  width: 170px;
  min-width: 170px;
  max-width: 170px;
  white-space: nowrap;
  text-align: left;
}
/* 2026-05-17 — Templates page: trailing action cell holds the
   labelled Edit Policy / Save / Close buttons (btn-standard). Width
   covers all three buttons + 10 px gaps. */
.policy-templates-table td.col-template-actions {
  white-space: nowrap;
  text-align: right;
  padding-right: 16px;
}
.policy-templates-table td.col-template-actions > button + button { margin-left: 10px; }

/* 2026-05-17 — Row highlight while its policy drawer is open, so the
   operator never loses track of which host they're editing. */
tr.ovr-editing td {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}
/* 2026-05-27 — Operator: when a Panel-templates row enters edit mode
   the editor expansion sits flush against the thead, making the two
   header rows look merged. Drop a 30 px transparent top spacer on
   the editing row's cells so it visually detaches from the
   PANEL / DESCRIPTION header. Scoped to policy-templates-table only
   so per-host policy tables elsewhere are unaffected.
   background-clip:padding-box keeps the editing-row tint from
   bleeding into the transparent border-top. */
.policy-templates-table tr.ovr-editing > td {
  border-top: 30px solid transparent;
  background-clip: padding-box;
}
/* 2026-05-27 — Operator: drop the hover tint on a row that's already
   in edit mode. The accent-tint editing background was being briefly
   overwritten by the generic table.t tr:hover td → var(--bg-2) rule,
   producing a flicker as the mouse passed over. Force-keep the
   editing tint with !important since the hover rule lives at the same
   specificity.
   2026-05-27 (operator follow-up) — the `background:` shorthand reset
   background-clip back to border-box, so on hover the editing tint
   started painting into the 30 px transparent border-top "gap" above
   the editing row. Re-pin background-clip: padding-box !important so
   the gap stays truly empty regardless of hover state. */
table.t tr.ovr-editing:hover > td {
  background: color-mix(in srgb, var(--accent) 14%, transparent) !important;
  background-clip: padding-box !important;
  cursor: default;
}
section[data-scope="global"].ovr-editing {
  outline: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  outline-offset: -1px;
}

/* 2026-05-17 — Per-host policy override drawer + structured form. */
.ovr-drawer-row.hidden { display: none; }
.ovr-drawer-cell {
  background: color-mix(in srgb, var(--accent) 6%, transparent);
  border-top: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  padding: 16px 20px;
}

/* 2026-05-18 — Failed-account row in the Backup completed tile.
   Click toggles a sibling drawer row that shows the agent's error
   message + per-account window. Same dark/light-aware tinting as the
   policy-edit drawer, scoped via --err so the visual reads as
   failure rather than informational accent. */
.completed-table tr.failed-row { cursor: pointer; }
.completed-table tr.failed-row:hover {
  background: color-mix(in srgb, var(--err) 8%, transparent);
}
.completed-table tr.failed-drawer-row.hidden { display: none; }
.completed-table tr.failed-drawer-row > td {
  background: color-mix(in srgb, var(--err) 6%, transparent);
  border-top: 1px solid color-mix(in srgb, var(--err) 35%, transparent);
  padding: 14px 20px;
}
.failed-drawer .failed-drawer-h {
  font-weight: 600;
  font-size: 12px;
  color: var(--err);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 6px;
}
.failed-drawer .failed-drawer-error {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 12px;
  color: var(--fg-1);
  background: var(--bg-1);
  border: 1px solid var(--border-1);
  border-radius: 4px;
  padding: 8px 10px;
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0;
  flex: 1 1 auto;
  max-height: 200px;
  overflow: auto;
}
/* 2026-05-25 — Failure-reason row: <pre> + copy icon button side-by-side.
   Replaces the standalone <pre> so the operator can grab the error
   without selecting wrapping text. */
.failed-drawer-err-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-bottom: 10px;
}
.failed-drawer-copy {
  flex: 0 0 auto;
  margin-top: 2px;
}
.failed-drawer .dl {
  grid-template-columns: 120px 1fr;
  font-size: 12px;
}
/* 2026-05-17 — Compact 4-per-row layout. Each fieldset is full-width
   and itself a 4-column grid; field labels are grid items so up to four
   fields sit side-by-side. Textareas and checkbox grids span all four
   columns. */
.ovr-form-grid {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.ovr-form-grid fieldset {
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 14px 14px 12px;
  margin: 0;
  background: var(--bg-1);
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px 14px;
}
/* 2026-05-17 — Section label sits INSIDE the box (not straddling the
   border like a default <legend>). Slightly bigger/bolder than the
   parameter labels so the box hierarchy is obvious. The template now
   uses <div class="ovr-section-label"> in place of <legend>. */
/* 2026-05-22 — promoted to a page-agnostic class so any form-section
   inside any fieldset can use the same header style (Policy form,
   Restores page, future forms). The grid-column rule only kicks in when
   the fieldset is itself a grid (i.e. inside .ovr-form-grid). */
.ovr-section-label {
  font-size: 13px;
  font-weight: 700;
  color: var(--fg-0);
  padding: 0;
  margin: 0 0 6px 0;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.ovr-form-grid .ovr-section-label,
.ovr-form-grid legend { grid-column: 1 / -1; }
.ovr-form-grid label {
  display: flex;
  flex-direction: column;
  font-size: 13px;
  color: var(--fg-2);
  margin: 0;
}
.ovr-form-grid label.cb {
  flex-direction: row;
  align-items: center;
  gap: 6px;
  color: var(--fg-1);
  font-size: 13px;
}
.ovr-form-grid label.cb input { margin: 0; }
.ovr-form-grid input[type="number"],
.ovr-form-grid input[type="text"],
.ovr-form-grid select {
  width: 100%;
  margin-top: 3px;
  background: var(--bg-2); color: var(--fg-0);
  border: 1px solid var(--border); border-radius: 4px;
  padding: 5px 8px; font-size: 13px;
  box-sizing: border-box;
}
.ovr-form-grid fieldset > textarea,
.ovr-form-grid fieldset .cb-grid,
.ovr-form-grid fieldset.span-2 > textarea {
  grid-column: 1 / -1;
}
.ovr-form-grid .cb-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 4px 12px;
}
.ovr-form-foot { margin-top: 10px; }
/* Legacy class no longer carries meaning since fieldsets are full-width;
   keep as a no-op so existing templates don't break. */
.ovr-form-grid fieldset.span-2 { /* identical to plain fieldset */ }

/* 2026-05-26 — Paired fieldset row. Two fieldsets sit side-by-side at
   50 % width each. Used for: Logging+Retention, Window+Recovery,
   Integrity+Compliance — the smaller sections where the regular 4-col
   wide fieldsets wasted horizontal real estate.

   2026-05-27 — Operator follow-up: stop wasting a SECOND row inside
   each paired fieldset. Every field in Logging / Retention /
   Integrity / Compliance must sit on a single line. Switched the
   inner layout from grid to flexbox-with-wrap. Each label flows
   naturally; numeric inputs cap at 90 px, selects at 140 px, the
   one growable text input (Logging Directory) uses .ovr-flex-grow
   to take whatever space is left on its row. */
.ovr-form-grid .ovr-pair-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}
/* 2026-05-27 — Three-fieldset row variant. Used for the
   Window+Recovery+Integrity collapse. Inherits everything else
   from .ovr-pair-row (the flex layout inside each fieldset, the
   input-width caps, etc.).
   2026-05-27 (operator follow-up) — 1fr 1fr 2fr split so Window
   and Recovery get a quarter each and Integrity / Transfer (the
   widest section with 4 fields) gets the remaining half. */
.ovr-form-grid .ovr-pair-row.ovr-trio-row {
  grid-template-columns: 1fr 1fr 2fr;
}
/* Inside a paired Scope cell the cb-grid's default min-width 140px
   would force at most 3-4 chips per row; trim to a tighter minmax so
   all 8 Scope checkboxes wrap into roughly 2 rows (4 per row) in the
   50 %-wide column. */
.ovr-pair-row > fieldset > .cb-grid {
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 4px 10px;
  flex: 1 0 100%;
}
.ovr-pair-row > fieldset {
  /* override the outer .ovr-form-grid fieldset rule's grid layout */
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 8px 14px;
}
.ovr-pair-row > fieldset > .ovr-section-label {
  flex: 1 0 100%;   /* header occupies its own line */
  margin-bottom: 4px;
}
/* Each field block gets natural width; checkbox labels sit inline. */
.ovr-pair-row > fieldset > label {
  flex: 0 0 auto;
  min-width: 0;
  margin: 0;
}
/* The one entry per row that should consume the remaining space
   (Logging Directory). Apply <label class="ovr-flex-grow">. */
.ovr-pair-row > fieldset > .ovr-flex-grow {
  flex: 1 1 120px;
}
.ovr-pair-row > fieldset > .ovr-flex-grow input[type="text"] {
  max-width: 100%;
}
/* Numeric + select widths inside paired rows. Operator: "reduce input
   fields by 70% — they do not need that width". 2-4 digit numbers
   live happily in 90 px; selects in 140 px. */
.ovr-pair-row input[type="number"] { max-width: 90px; }
.ovr-pair-row select               { max-width: 140px; }
.ovr-pair-row input[type="text"]   { max-width: 200px; }

/* 2026-05-27 — Equal-width siblings inside a paired fieldset. Used
   for Logging (Level / Retain days / Directory all share width). The
   max-width overrides on number / select / text below give them
   100 % of their flex column so the three columns stay even. */
.ovr-pair-row > fieldset > .ovr-eq {
  flex: 1 1 0;
  min-width: 0;
}
.ovr-pair-row > fieldset > .ovr-eq input[type="number"],
.ovr-pair-row > fieldset > .ovr-eq input[type="text"],
.ovr-pair-row > fieldset > .ovr-eq select {
  max-width: 100%;
  width: 100%;
}
/* 2026-05-27 — Operator: Custom keep days input is too narrow at
   the 90 px default; double it. Scoped via .ovr-wide-input class on
   the label so only that field grows, leaving the other numeric
   inputs at the compact 90 px. */
.ovr-pair-row > fieldset > .ovr-wide-input input[type="number"] {
  max-width: 180px;
  width: 180px;
}

/* Inline confirm popup anchored at Close. */
/* 2026-05-18 — Inline confirm popover anchored to the Close button. */
.ovr-confirm-pop {
  position: fixed;            /* JS sets top/left at the anchor */
  z-index: 10000;
  background: var(--bg-2);
  border: 1px solid var(--border-1);
  border-radius: 8px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45),
              0 2px 6px  rgba(0, 0, 0, 0.30);
  padding: 14px 16px;
  min-width: 320px;
  max-width: 380px;
  font-size: 13px;
  color: var(--fg-1);
}
/* Small notch on top-right pointing at the Close button. */
.ovr-confirm-pop::before {
  content: "";
  position: absolute;
  top: -7px;
  right: 22px;
  width: 12px; height: 12px;
  background: var(--bg-2);
  border-top: 1px solid var(--border-1);
  border-left: 1px solid var(--border-1);
  transform: rotate(45deg);
}
.ovr-confirm-pop .ovr-confirm-msg {
  font-weight: 600;
  font-size: 13px;
  color: var(--fg-1);
  margin-bottom: 12px;
  line-height: 1.35;
}
.ovr-confirm-pop .ovr-confirm-actions {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
}
/* Buttons inside the popover follow the standard labelled-button
   dimensions so they don't pop out of place. */
.ovr-confirm-pop .ovr-confirm-actions button {
  min-width: 84px;
  padding: 6px 12px;
  border-radius: 5px;
  font-size: 13px;
  font-weight: 600;
  border: 1px solid transparent;
  cursor: pointer;
}
.ovr-confirm-pop .ovr-confirm-actions .btn-ghost {
  background: transparent;
  color: var(--fg-2);
  border-color: var(--border-1);
}
.ovr-confirm-pop .ovr-confirm-actions .btn-ghost:hover {
  background: var(--bg-1);
  color: var(--fg-1);
}

/* 2026-05-17 — Policy editor textarea: monospace, full-width, generous
   line height so YAML is readable. */
.policy-textarea {
  width: 100%;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 13px;
  line-height: 1.5;
  background: var(--bg-2);
  color: var(--fg-0);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 10px 12px;
  resize: vertical;
  min-height: 360px;
  tab-size: 2;
}

/* 2026-05-17 — Backup history page filter strip. */
.hist-filter-row {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  margin: 0 0 12px 0;
}
.hist-filter-row label { font-size: 12px; color: var(--fg-2); display: inline-flex; align-items: center; gap: 6px; }
.hist-filter-row input[type="search"],
.hist-filter-row input[type="date"],
.hist-filter-row select {
  background: var(--bg-1); color: var(--fg-0);
  border: 1px solid var(--border); border-radius: 6px;
  padding: 6px 9px; font-size: 13px; min-width: 160px;
}
.hist-filter-row input[type="search"] { min-width: 260px; flex: 1; }

/* Mobile */
@media (max-width: 800px) {
  .shell-side { display: none; }
  .shell-main { left: 0; }   /* 2026-05-27 — was margin-left:0; now position:fixed → use left */
}

/* 2026-05-18 — Backups > Browse: Windows-Explorer-style two-pane.
   Left tree lazy-loads servers → accounts → folders; right grid shows
   selected folder contents as small-icon tiles. Pure config-driven
   colours (theme tokens, no hardcoded hex except inside the SVG
   icons that ship a literal Windows-yellow folder + per-extension
   badges by design). All measurements in px per the project rule. */
.browse-shell {
  display: grid;
  grid-template-columns: 440px 1fr;
  gap: 12px;
  /* 2026-05-18 — Stretch the explorer to the bottom of the viewport.
     Offset ≈ shell-top + page-head + main padding (~190 px). */
  height: calc(100vh - 190px);
  min-height: 480px;
  margin-top: 12px;
}
.browse-tree {
  background: var(--bg-1);
  border: 1px solid var(--bg-2);
  border-radius: 6px;
  padding: 10px 8px;
  overflow-y: auto;
  /* Tree fills the full shell height — grid parent caps it. */
  height: 100%;
  font-size: 13px;
}
.browse-tree-loading,
.tree-loading {
  padding: 14px 8px;
  color: var(--fg-2);
  font-size: 12px;
}
.browse-tree-ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
.tree-children {
  list-style: none;
  padding-left: 18px;
  margin: 2px 0;
}
.tree-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px;
  border-radius: 4px;
  cursor: pointer;
  user-select: none;
  line-height: 18px;
}
.tree-row:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.tree-row.selected {
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  font-weight: 600;
}
.tree-caret {
  width: 12px;
  text-align: center;
  font-size: 9px;
  color: var(--fg-2);
  flex: 0 0 12px;
}
.tree-icon {
  width: 16px;
  height: 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 16px;
}
.tree-icon svg { width: 16px; height: 16px; }
/* 2026-05-18 — Server icon sized up so the top-of-tree node reads as
   a distinct higher-order kind versus the per-account / per-folder
   yellow folders. Folder icons stay at the compact 16 px. */
.tree-icon-server { width: 22px; height: 22px; flex: 0 0 22px; color: var(--accent); }
.tree-icon-server svg { width: 22px; height: 22px; }
.tree-label {
  flex: 1 1 auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 14px;
}
/* 2026-05-24 — Secondary metadata text rendered inline next to a
   tree label (e.g. "Baseline  (Full Backup taken: 2026-05-24 12:11)"
   on the Browse page). Same-row, smaller + dimmed so the label
   itself stays the dominant element. */
.tree-row .tree-label-meta {
  color: var(--fg-2);
  font-size: 12px;
  margin-left: 6px;
  white-space: nowrap;
  flex: 0 0 auto;
}
/* For rows that carry a meta sibling, the label shouldn't take all
   remaining flex space — otherwise the meta gets pushed to the far
   right of the tree column. Constrain to its natural width so meta
   sits immediately after. */
.tree-li-baseline .tree-row .tree-label {
  flex: 0 0 auto;
}
.tree-meta {
  color: var(--fg-2);
  font-size: 11px;
  flex: 0 0 auto;
}

.browse-content {
  background: var(--bg-1);
  border: 1px solid var(--bg-2);
  border-radius: 6px;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  /* Fills the shell height; the grid (.browse-grid) inside scrolls. */
  height: 100%;
  min-height: 0;
}
.browse-breadcrumb {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  font-size: 13px;
  padding-bottom: 10px;
  margin-bottom: 10px;
  border-bottom: 1px solid var(--bg-2);
}
.crumb {
  color: var(--fg-1);
  text-decoration: none;
  padding: 2px 6px;
  border-radius: 3px;
}
.crumb-link { color: var(--accent); cursor: pointer; }
.crumb-link:hover { text-decoration: underline; }
.crumb-placeholder { color: var(--fg-2); font-style: italic; }
.crumb-sep { color: var(--fg-2); font-size: 12px; }

.browse-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: 6px;
  align-content: start;
  flex: 1 1 auto;
  overflow-y: auto;
  min-height: 0;
  padding-right: 4px;
}
.browse-grid-loading,
.browse-grid-empty {
  grid-column: 1 / -1;
  text-align: center;
  color: var(--fg-2);
  padding: 28px 0;
  font-size: 13px;
}
/* 2026-05-18 — Browse-page tile rules. All scoped under .browse-grid
   so they CANNOT bleed into the .tile class used by session_detail
   and dashboard <section class="tile"> wrappers. Earlier these rules
   lived unscoped at the file level and cascaded into every page's
   .tile, centering all the section h3 headers. */
.browse-grid .tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;   /* 2026-05-23 — anchor content to top of tile */
  align-self: start;              /* 2026-05-23 — anchor tile to top of grid cell */
  text-align: center;
  padding: 8px 4px 6px;
  border: 1px solid transparent;
  border-radius: 5px;
  cursor: default;
  user-select: none;
  transition: background 0.08s, border-color 0.08s;
  position: relative;
}

/* 2026-05-23 — Per-snapshot file tile action chip. Sits top-right
   on the tile so the operator sees at a glance whether the file
   was added, modified, or deleted in this session. Light pastel
   tint matches the .bs-status snapshot status pill convention. */
.browse-grid .tile-chip {
  position: absolute;
  top: 4px;
  right: 4px;
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  padding: 1px 5px;
  border-radius: 3px;
  border: 1px solid transparent;
  line-height: 1.2;
}
.browse-grid .tile-chip-added {
  background:    color-mix(in srgb, var(--ok) 12%, transparent);
  color:         var(--ok);
  border-color:  color-mix(in srgb, var(--ok) 35%, transparent);
}
.browse-grid .tile-chip-modified {
  background:    color-mix(in srgb, var(--warn) 12%, transparent);
  color:         var(--warn);
  border-color:  color-mix(in srgb, var(--warn) 35%, transparent);
}
.browse-grid .tile-chip-deleted {
  background:    color-mix(in srgb, var(--err) 12%, transparent);
  color:         var(--err);
  border-color:  color-mix(in srgb, var(--err) 35%, transparent);
}
/* Deleted tile is greyed + strikethrough to make absence obvious. */
.browse-grid .tile-snap-deleted .tile-name {
  text-decoration: line-through;
  color: var(--fg-3);
}
.browse-grid .tile-snap-deleted .tile-icon { opacity: 0.55; }
.browse-grid .tile-folder { cursor: pointer; }
.browse-grid .tile:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.browse-grid .tile:focus { outline: none; border-color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.browse-grid .tile.selected,
.browse-grid .tile:active { background: color-mix(in srgb, var(--accent) 22%, transparent); border-color: var(--accent); }
.browse-grid .tile-icon {
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 4px;
}
.browse-grid .tile-icon svg { width: 44px; height: 44px; }
.browse-grid .tile-folder .tile-icon svg { width: 48px; height: 40px; }
.browse-grid .tile-name {
  width: 100%;
  font-size: 12px;
  color: var(--fg-1);
  word-break: break-all;
  max-height: 32px;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  line-height: 16px;
}
.browse-grid .tile-meta {
  font-size: 10px;
  color: var(--fg-2);
  margin-top: 2px;
}

.browse-loadmore {
  display: flex;
  align-items: center;
  gap: 12px;
  padding-top: 10px;
  margin-top: 10px;
  border-top: 1px solid var(--bg-2);
}
.browse-counts { color: var(--fg-2); font-size: 12px; }
/* 2026-05-18 — Force HTML5 `hidden` to override any flex/inline-block
   defaults that creep in from button base styles. Affects the Load
   more button (and any other intentionally-hidden node on this page). */
.browse-loadmore [hidden] { display: none !important; }

/* 2026-05-18 — Redesigned dashboard: 4 KPI cards + 3 widget cards.
   All colour decisions live on theme tokens; per-intent variants are
   purely class-modifiers on the .kpi-card and .widget-card bases.
   All measurements in px per the project rule. */

.grid.cards-3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
@media (max-width: 1000px) {
  .grid.cards-3 { grid-template-columns: 1fr; }
}

.widget-row { margin: 14px 0; }
.widget-card {
  padding: 14px 16px 16px;
  min-height: 240px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.widget-card .card-h { margin: 0 0 6px; }

/* 2026-06-03 — Dashboard "Currently running sessions" row now shares its
   line with a right-aligned Quick Launch card. Quick Launch is the SAME
   width as a .widget-card (1/3 of a cards-3 row → calc((100% - 24px)/3),
   matching the Hourly Data card); the running table flexes to fill the
   rest (squeezed) per operator spec. Stacks vertically below 1000px. */
.dash-run-row {
  display: flex;
  gap: 12px;
  align-items: stretch;
  margin: 14px 0;
}
.dash-run-row .dash-run-main {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
}
.dash-run-row .dash-quick {
  flex: 0 0 calc((100% - 24px) / 3);
  /* 2026-06-03 — height MATCHES the Currently Running Sessions card: the row's
     align-items: stretch makes both cards the same height; the buttons fill it.
     margin:0 cancels .card's default margin-top:16px (which the running card
     also resets) — otherwise this card sits 16px lower AND stretches 16px
     shorter, so the two cards never lined up. */
  margin: 0;
}
/* Quick Launch buttons: 2×2 grid that FILLS the card. The card is a
   .widget-card (flex column); the actions grid flexes to take all the height
   left under the header, with two equal rows, so the four buttons fill the
   card with comfortable gaps. Each button stretches to its cell (tall) and
   centres its label. */
.dash-quick-actions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  gap: 20px;
  flex: 1 1 auto;
  margin-top: 4px;
}
.dash-quick-btn {
  /* 2026-06-03 — buttons fill their grid cell so the ONLY spacing between them
     is the uniform .dash-quick-actions gap — identical vertically (between
     rows) and horizontally (between columns). (Proportional 80% sizing made
     the vertical gap larger because the cells are taller than they are wide.) */
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 12px;
}
/* Each button is an <a> (the ACTION is the link). Kill the hyperlink look —
   no underline — in EVERY state so the label reads as a plain button.
   2026-06-03 — operator: Quick Launch buttons are OUTLINED at rest (transparent
   fill, accent border + accent-coloured label) and flip to the FILLED accent
   look on hover/focus. This overrides the inherited .btn-svc-primary fill
   (which the buttons keep ONLY for their canonical .btn-svc dimensions); the
   override is scoped to .dash-quick-btn so the global .btn-svc-primary used
   elsewhere (Servers/Manage top-bar, inline editors) is untouched. Equal
   specificity to .btn-svc-primary but later in source order, so it wins. */
.dash-quick-btn,
.dash-quick-btn:link,
.dash-quick-btn:visited {
  background: transparent;
  border: 1px solid var(--accent);
  color: var(--accent);
  text-decoration: none;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.dash-quick-btn:hover,
.dash-quick-btn:focus,
.dash-quick-btn:active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-fg, #ffffff);
  text-decoration: none;
}
@media (max-width: 1000px) {
  .dash-run-row { flex-direction: column; }
  .dash-run-row .dash-quick { flex-basis: auto; }
}

/* 2026-06-03 — Administrators page "Create administrator" card: half width,
   centered (operator). Replaces a former inline style="margin-top:1.25rem"
   (no-inline-CSS + px-only HARD RULEs). Full width on narrow screens. */
.admin-create {
  margin: 20px auto 0;
  max-width: 50%;
}
@media (max-width: 900px) {
  .admin-create { max-width: 100%; }
}
/* 2026-06-03 — password row: readonly generated field + copy icon + Generate
   button + hint, on one line. The input grows to fill the row. */
.admin-pwgen {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
}
.admin-pwgen input { flex: 1 1 auto; min-width: 0; }
.admin-pwgen .hint { margin: 0; white-space: nowrap; }
.admin-pw-copy { flex: 0 0 auto; }
/* copy-icon has no disabled state of its own (it's never disabled elsewhere);
   dim it until a password exists. */
.copy-icon:disabled { opacity: 0.4; cursor: not-allowed; }
/* Save / Cancel row for the reveal-on-toggle Create form. */
.admin-create-actions { display: flex; gap: 8px; }

/* KPI card intent variants (icon stripe colour). The card body is the
   shared .kpi-card class already in dash.css; we only override the
   icon stripe per intent here. */
.kpi-card.kpi-primary .kpi-icon { color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.kpi-card.kpi-ok      .kpi-icon { color: var(--ok);     background: color-mix(in srgb, var(--ok)     14%, transparent); }
.kpi-card.kpi-warn    .kpi-icon { color: var(--warn);   background: color-mix(in srgb, var(--warn)   14%, transparent); }
.kpi-card.kpi-running .kpi-icon { color: var(--running);background: color-mix(in srgb, var(--running)14%, transparent); }
.kpi-card .kpi-icon svg { width: 22px; height: 22px; }
.kpi-foot {
  font-size: 11px;
  color: var(--fg-2);
  margin-top: 2px;
}

/* Volume donut */
.volume-donut {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
.volume-donut svg { width: 160px; height: 160px; }
.donut-track {
  fill: none;
  stroke: color-mix(in srgb, var(--fg-2) 28%, transparent);
  stroke-width: 12;
}
.donut-arc {
  fill: none;
  stroke: var(--accent);
  stroke-width: 12;
  stroke-linecap: round;
  transform: rotate(-90deg);
  transform-origin: 60px 60px;
  transition: stroke-dasharray 0.4s ease;
}
.donut-pct {
  fill: var(--fg-1);
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 18px;
  font-weight: 600;
  text-anchor: middle;
}
.donut-sub {
  fill: var(--fg-2);
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 10px;
  text-anchor: middle;
}
.volume-foot {
  color: var(--fg-2);
  font-size: 12px;
  text-align: center;
}
.volume-foot [data-cell-id] { color: var(--fg-1); font-weight: 600; }

/* Top-contributors bar list */
.bar-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 4px;
}
.bar-row {
  display: grid;
  grid-template-columns: 48px 1fr 110px;
  align-items: center;
  gap: 10px;
}
.bar-pct {
  font-size: 12px;
  font-weight: 600;
  color: var(--fg-1);
  text-align: right;
}
.bar-trackwrap { width: 100%; }
.bar-track {
  width: 100%;
  height: 8px;
  background: color-mix(in srgb, var(--fg-2) 22%, transparent);
  border-radius: 4px;
  overflow: hidden;
}
.bar-fill {
  height: 100%;
  background: var(--accent);
  border-radius: 4px;
  transition: width 0.4s ease;
}
.bar-row:nth-child(1) .bar-fill { background: var(--accent); }
.bar-row:nth-child(2) .bar-fill { background: var(--warn); }
.bar-row:nth-child(3) .bar-fill { background: var(--err); }
.bar-row:nth-child(4) .bar-fill { background: var(--ok); }
.bar-row:nth-child(5) .bar-fill { background: var(--running); }
.bar-label {
  font-size: 12px;
  color: var(--fg-1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.bar-empty {
  color: var(--fg-2);
  font-size: 12px;
  font-style: italic;
  text-align: center;
  padding: 20px 0;
}

/* Hourly bars */
.hourly-chart { width: 100%; }
.hourly-svg { width: 100%; height: 140px; display: block; }
.hourly-bar { fill: var(--accent); }
.axis-line { stroke: var(--fg-2); stroke-width: 1; opacity: 0.6; }
.axis-grid { stroke: var(--fg-2); stroke-width: 1; stroke-dasharray: 2 3; opacity: 0.25; }
.axis-tick { fill: var(--fg-2); font-family: 'Inter', system-ui, sans-serif; font-size: 10px; }

/* 2026-05-25 — nikshep-statsd dashboard tiles.
   Two new sections below "Currently Running Sessions":
     1. System & MongoDB Metrics — 2×2 grid of mini SVG line charts.
     2. Account Analytics — five top-N horizontal bar lists from the
        hourly batch in db.account_analytics. */
.tile-h-sub { color: var(--fg-2); font-weight: 400; font-size: 13px; margin-left: 8px; }

.metrics-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}
.metrics-cell {
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 10px 12px 8px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.metrics-cell-title {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 12px;
  color: var(--fg-2);
  font-weight: 500;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  margin: 0;
}
.metrics-chart {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* 2026-05-25 — modern line-chart styling for monitoring tiles.
   Header row: KPI value + delta arrow on the left, pill legend on
   the right. Below: white card-in-card holding a smoothed Catmull-
   Rom SVG path with gradient area fill. Lines are smoothed AND
   down-sampled to ~48 points so 10 s-cadence samples (~360 over 1 h)
   no longer crowd the path. Fonts: 11-13 px Inter, aligned with
   .tile-h-sub. */
.metric-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.metric-head-left {
  display: flex;
  align-items: baseline;
  gap: 6px;
}
.metric-kpi-label {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 11px;
  color: var(--fg-2);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-right: 4px;
}
.metric-kpi-val {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 18px;
  color: var(--fg-0);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.metric-kpi-unit {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 12px;
  color: var(--fg-2);
  font-weight: 500;
}
.metric-kpi-delta {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 11px;
  margin-left: 4px;
  line-height: 1;
}
.metric-kpi-delta.delta-up { color: var(--ok); }
.metric-kpi-delta.delta-down { color: var(--err); }

.metric-legend-row {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}
.metric-legend-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 12px;
  color: var(--fg-1);
  font-weight: 500;
  background: transparent;
  border: none;
  border-radius: 0;
  padding: 0;
}
.metric-legend-pill .metric-legend-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  display: inline-block;
}

.metric-chart-wrap {
  background: transparent;
  border: none;
  border-radius: 0;
  padding: 0;
}
.metric-svg { max-width: 100%; width: 100%; height: 170px; display: block; }
.metric-line {
  stroke-linejoin: round;
  stroke-linecap: round;
  stroke-width: 0.75;
  vector-effect: non-scaling-stroke;
}
.metric-area { pointer-events: none; }
.metric-dot {
  r: 3.5;
  stroke-width: 2;
  filter: drop-shadow(0 0 4px rgba(0,0,0,0.25));
}
.metric-grid {
  stroke: var(--fg-2);
  stroke-width: 1;
  stroke-dasharray: 2 4;
  opacity: 0.18;
  vector-effect: non-scaling-stroke;
}
.metric-tick {
  fill: var(--fg-2);
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 12px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.metric-tick-time {
  fill: var(--fg-2);
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 11px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.3px;
}

.metrics-coll {
  width: 100%;
}
/* Collection Sizes — vertical bars. 2026-05-25.
   yMax = max(size) * 1.1 so the tallest bar always has ~10% headroom
   above it. Bars are ~55% of their column slot wide (capped at 56 px)
   for a broad-but-still-spaced look. Hover surfaces a tooltip with
   size + doc count anchored to the column. */
.coll-vbar {
  transition: opacity 0.15s;
}
.coll-vbar-hit:hover ~ .coll-vbar { opacity: 0.7; }
.coll-vbar-val {
  fill: var(--fg-0);
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.coll-vbar-name {
  fill: var(--fg-1);
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 12px;
  font-weight: 500;
}
.coll-vbar-cap {
  fill: var(--fg-2);
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
.coll-vbar-hit { cursor: pointer; }

/* API Statistics — endpoint window selector + tile name styling.
   2026-05-25. */
.api-stats-window {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 13px;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: var(--bg-2);
  color: var(--fg-0);
}
.page-h-actions { margin-left: auto; }
.metric-kpi-rate {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 12px;
  font-weight: 500;
  color: var(--fg-2);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.2px;
}
[data-cell-id="api-stats-grid"] .metrics-cell-title {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 12px;
  font-weight: 500;
  color: var(--fg-0);
  text-transform: none;
  letter-spacing: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Hover tooltip + guideline shared by all line charts. */
.metric-guide {
  stroke: var(--fg-1);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  pointer-events: none;
  transition: opacity 0.1s;
}
.metric-hover-dot {
  pointer-events: none;
  transition: opacity 0.1s;
  filter: drop-shadow(0 0 4px rgba(0,0,0,0.3));
}
.metric-capture { cursor: crosshair; }
.metric-tooltip {
  position: absolute;
  pointer-events: none;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 10px;
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 12px;
  color: var(--fg-0);
  box-shadow: 0 4px 14px rgba(0,0,0,0.18);
  min-width: 140px;
  transition: opacity 0.1s, left 0.05s;
  z-index: 5;
}
.metric-tooltip-time {
  font-size: 11px;
  font-weight: 600;
  color: var(--fg-2);
  letter-spacing: 0.3px;
  margin-bottom: 6px;
  font-variant-numeric: tabular-nums;
  text-transform: uppercase;
}
.metric-tooltip-row {
  display: grid;
  grid-template-columns: 10px 1fr auto;
  gap: 6px;
  align-items: center;
  padding: 2px 0;
}
.metric-tooltip-dot {
  width: 8px; height: 8px; border-radius: 50%;
  display: inline-block;
}
.metric-tooltip-name { color: var(--fg-1); font-weight: 500; }
.metric-tooltip-val  {
  color: var(--fg-0);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.coll-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.coll-row {
  display: grid;
  grid-template-columns: 150px 110px 1fr;
  align-items: center;
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 13px;
  color: var(--fg-1);
}
.coll-name  { color: var(--fg-0); font-weight: 500; }
.coll-count { color: var(--fg-2); font-variant-numeric: tabular-nums; }
.coll-size  { color: var(--fg-2); text-align: right; font-variant-numeric: tabular-nums; }

/* Account Analytics tile — five horizontal bar lists in one tile.
   Each category gets its own card; ordered as compressed → images →
   small files → total files → smallest accounts. */
.analytics-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
.analytics-cell {
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 10px 12px;
}
.analytics-cell-title {
  font-size: 13px;
  color: var(--fg-2);
  font-weight: 500;
  margin-bottom: 6px;
}
.analytics-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.analytics-row {
  display: grid;
  grid-template-columns: 28px 110px 1fr 80px;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--fg-1);
  padding: 2px 0;
}
.analytics-rank    { color: var(--fg-2); font-family: monospace; }
.analytics-account { color: var(--fg-0); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.analytics-bar-wrap {
  background: var(--bg-1);
  border-radius: 2px;
  height: 6px;
  overflow: hidden;
}
.analytics-bar {
  display: block;
  background: var(--accent);
  height: 100%;
}
.analytics-value { color: var(--fg-2); text-align: right; }

/* 2026-05-18 — Restores page: form layout + restore-job state badges. */
.restore-form { display: flex; flex-direction: column; gap: 12px; }
.restore-row { display: flex; flex-wrap: wrap; gap: 16px; }
.restore-row label { display: flex; flex-direction: column; gap: 4px; font-size: 14px; color: var(--fg-2); min-width: 180px; flex: 1 1 180px; }
.restore-row label input[type="text"] {
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 5px 8px;
  font-size: 13px;
  color: var(--fg-0);
}
.restore-fieldset {
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 14px 14px 12px;
  margin: 0;
  background: var(--bg-1);
  flex: 1 1 320px;
}
.restore-fieldset label { display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--fg-1); padding: 3px 0; min-width: auto; flex: none; flex-direction: row; }
.restore-fieldset label .sub { font-size: 13px; color: var(--fg-2); }
/* 2026-05-22 — Combobox (typeahead + dropdown) for the New Restore form.
   Three cascading instances: Server → Account → Snapshot. The visible
   input filters; a hidden sibling holds the resolved value. Menu shows
   up to 200 matches; max-height caps visible rows at ~20 then scrolls. */
.rf-combo-label { position: relative; }
.rf-combo {
  position: relative;
  display: flex;
  align-items: stretch;
  background: var(--bg-2);
  border: 1px solid var(--bg-2);
  border-radius: 4px;
}
.rf-combo:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}
.rf-combo-disabled { opacity: 0.55; pointer-events: none; }
.rf-combo-input {
  flex: 1 1 auto;
  background: transparent;
  border: none;
  outline: none;
  padding: 6px 10px;
  font-size: 13px;
  color: var(--fg-0);
  min-width: 0;
}
.rf-combo-caret {
  flex: 0 0 28px;
  background: transparent;
  border: none;
  color: var(--fg-2);
  font-size: 12px;
  cursor: pointer;
  padding: 0;
}
.rf-combo-caret:hover { color: var(--fg-0); }
.rf-combo-menu {
  position: absolute;
  top: calc(100% + 2px);
  left: 0;
  right: 0;
  z-index: 100;
  list-style: none;
  margin: 0;
  padding: 4px 0;
  background: var(--bg-1);
  border: 1px solid var(--border-strong);
  border-radius: 5px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
  max-height: 440px;   /* ~20 rows at 22 px each + padding */
  overflow-y: auto;
}
.rf-combo-menu[hidden] { display: none; }
.rf-combo-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 5px 12px;
  font-size: 13px;
  color: var(--fg-0);
  cursor: pointer;
}
.rf-combo-item:hover { background: var(--bg-2); color: var(--accent); }
.rf-combo-item-label { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rf-combo-item-hint  { flex: 0 0 auto; font-size: 11px; color: var(--fg-2); }
.rf-combo-none { padding: 6px 12px; font-size: 12px; color: var(--fg-2); font-style: italic; }

.rf-fieldset-disabled { opacity: 0.55; pointer-events: none; }
.restore-fieldset[disabled] { opacity: 0.55; pointer-events: none; }
.restore-fieldset label.rf-disabled { color: var(--fg-3); }
.restore-fieldset .rf-remote-row {
  display: flex; align-items: center; gap: 8px; flex-direction: row; padding: 4px 0;
}
.restore-fieldset .rf-remote-row select,
.restore-fieldset .rf-remote-row input[type="text"] {
  background: var(--bg-2);
  border: 1px solid var(--bg-2);
  border-radius: 4px;
  padding: 6px 10px;
  font-size: 13px;
  color: var(--fg-0);
  min-width: 160px;
}
.restore-actions { display: flex; align-items: center; gap: 16px; width: 100%; }
.restore-actions > button[type="submit"] { margin-left: auto; }
.restore-msg { font-size: 12px; color: var(--fg-2); }
.restore-msg.ok { color: var(--ok); }
.restore-msg.err { color: var(--err); }

/* Restore-job state badge tints. */
.badge.status-pending,
.badge.status-assembling,
.badge.status-delivering { color: var(--running); }
.badge.status-ready                                     { color: var(--ok); }
.badge.status-delivered                                 { color: var(--accent); }
.badge.status-failed                                    { color: var(--err); }
/* 2026-05-19 — Scheduling & retry bundle: retry-chain breadcrumb. */
.badge.status-retry                                     { color: var(--accent); }
.retry-chain-bar {
  margin: 12px 0 0 0;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-2);
  font-size: 13px;
  color: var(--ink-soft);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.retry-chain-bar code { font-size: 12px; }

/* ─────────────────────────────────────────────────────────────────
   2026-05-21 — Phase 3 of the restore-redesign plan.
   Restores page tabs (.rj-) + reusable pagination widget (.np-).
   All units px per [[px-only-css]]. .rj- and .np- prefixes scope
   to this page so they don't bleed elsewhere ([[css-class-namespacing]]).
   ───────────────────────────────────────────────────────────────── */

/* Tabs */
.rj-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin: 0 0 12px 0; }
.rj-tab {
  background: transparent;
  border: 1px solid transparent;
  border-bottom: none;
  border-radius: 4px 4px 0 0;
  padding: 6px 14px;
  font-size: 13px;
  font-weight: 600;
  color: var(--fg-2);
  cursor: pointer;
}
.rj-tab:hover { color: var(--fg-1); background: var(--bg-2); }
.rj-tab-active {
  color: var(--fg-1);
  background: var(--bg-1);
  border-color: var(--border);
  border-bottom-color: var(--bg-1);   /* overlap the panel border-top */
  margin-bottom: -1px;
}
.rj-panel { padding: 8px 0 0 0; }

/* Pager row — bottom-right alignment */
.rj-pager-row { display: flex; justify-content: flex-end; padding: 12px 0 0 0; }

/* Shared pagination widget — .np- namespace */
.np-pager {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
}
.np-pager .np-pages { display: inline-flex; list-style: none; padding: 0; margin: 0; gap: 2px; }
.np-pager .np-pages li { display: inline-flex; }
.np-pager .np-btn,
.np-pager .np-page {
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--fg-1);
  font-size: 13px;
  font-weight: 600;
  padding: 4px 10px;
  min-width: 32px;
  cursor: pointer;
}
.np-pager .np-btn:hover:not([disabled]),
.np-pager .np-page:hover:not(.np-current) {
  background: var(--bg-2);
  border-color: var(--accent);
}
.np-pager .np-btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}
.np-pager .np-page.np-current {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  cursor: default;
}
.np-pager .np-summary {
  margin-left: 12px;
  color: var(--fg-2);
  font-size: 12px;
}

/* ─────────────────────────────────────────────────────────────────
   2026-05-21 — Phase 1 of the restore-redesign plan.
   Browse → Snapshots tree (.bs- namespace) — sibling of the existing
   live-baseline Files tree. The .browse-aside flex container stacks
   the two sections vertically so both share the 320 px sidebar.
   PX-only per [[px-only-css]]. Scoped to .bs- per [[css-class-namespacing]].
   ───────────────────────────────────────────────────────────────── */

/* Sidebar layout: stack the two tree sections vertically and let
   each scroll independently. */
.browse-aside {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
  /* match .browse-tree height behaviour — fill the .browse-shell grid */
  height: 100%;
}
.browse-tree-section {
  display: flex;
  flex-direction: column;
  min-height: 120px;
  flex: 1 1 auto;
  min-width: 0;
}
.browse-tree-section .browse-tree {
  flex: 1 1 auto;
  height: auto;
  min-height: 100px;
}
.browse-tree-h {
  margin: 0 0 6px 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--fg-2);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

/* Snapshot tree — nodes are nested divs (NOT <ul>) so the indent
   logic is simpler than the existing .browse-tree-ul tree. */
.bs-section .bs-hint {
  margin: 6px 0 0 0;
  padding: 0 4px;
  font-size: 11px;
  color: var(--fg-2);
}
.bs-tree { font-size: 13px; }
.bs-node { margin: 1px 0; }
.bs-node-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 4px;
  border-radius: 4px;
  cursor: pointer;
  color: var(--fg-1);
  text-decoration: none;
}
.bs-node-head:hover { background: var(--bg-2); }
.bs-caret {
  display: inline-block;
  width: 10px;
  font-size: 10px;
  color: var(--fg-2);
}
.bs-label { font-size: 13px; }
.bs-meta {
  font-size: 11px;
  color: var(--fg-2);
  padding: 0 4px;
}
.bs-meta-row {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
}
.bs-children {
  padding-left: 16px;
  border-left: 1px solid var(--bg-2);
  margin-left: 6px;
}
.bs-server > .bs-node-head { font-weight: 600; }
.bs-kind > .bs-node-head { color: var(--fg-1); }
.bs-date > .bs-node-head { font-family: var(--mono, monospace); }
.bs-acct .bs-node-head {
  padding-left: 18px;
  color: var(--accent);
}
.bs-acct .bs-node-head:hover { text-decoration: underline; }
.bs-acct-icon {
  display: inline-block;
  width: 10px;
  text-align: center;
  color: var(--fg-2);
}

/* Tags + status chips at the date row */
.bs-tags { display: inline-flex; gap: 3px; }
.bs-tag {
  display: inline-block;
  padding: 1px 5px;
  border-radius: 3px;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.3px;
}
.bs-tag-weekly  { background: var(--accent); color: #fff; }
.bs-tag-monthly { background: var(--ok); color: #fff; }
/* 2026-05-22 — Light pastel pill: 12% tint of the hue for the BG, the
   hue itself for the text, 1 px border of the same hue. All variants
   share min-width: 72 px (≈ CANCELED's natural width) so every chip
   in the tree aligns. inline-flex + center keeps the text optical
   centre even when the label is shorter than the pill. */
.bs-status {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 72px;
  padding: 1px 6px;
  border: 1px solid transparent;
  border-radius: 3px;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.3px;
}
.bs-status-success {
  background: color-mix(in srgb, var(--ok) 12%, transparent);
  color: var(--ok);
  border-color: color-mix(in srgb, var(--ok) 35%, transparent);
}
.bs-status-partial,
.bs-status-completed_with_errors {
  background: color-mix(in srgb, var(--warn) 12%, transparent);
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 35%, transparent);
}
.bs-status-failed {
  background: color-mix(in srgb, var(--err) 12%, transparent);
  color: var(--err);
  border-color: color-mix(in srgb, var(--err) 35%, transparent);
}
.bs-status-canceled {
  background: color-mix(in srgb, var(--fg-2) 12%, transparent);
  color: var(--fg-2);
  border-color: color-mix(in srgb, var(--fg-2) 35%, transparent);
}
.bs-status-running {
  background: color-mix(in srgb, var(--running, var(--accent)) 12%, transparent);
  color: var(--running, var(--accent));
  border-color: color-mix(in srgb, var(--running, var(--accent)) 35%, transparent);
}

/* Empty/error/loading lines inside the tree */
.bs-loading, .bs-empty, .bs-err {
  padding: 8px 4px;
  font-size: 12px;
  color: var(--fg-2);
}
.bs-err { color: var(--err); }

/* Load more button — uses .btn-svc inherited dimensions so it
   matches every other labelled button per [[standard-button-size]]. */
.bs-loadmore {
  margin: 8px 0 6px 4px;
}

/* ─────────────────────────────────────────────────────────────────
   2026-05-21 — Phase 2 of the restore-redesign plan.
   Right-click restore destination modal (.bs-modal-*) + History tab
   destination column tweaks (.rj-dest, .rj-dest-path).
   PX-only per [[px-only-css]]; .bs- + .rj- prefixes per [[css-class-namespacing]].
   ───────────────────────────────────────────────────────────────── */

.bs-modal {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: flex;
  align-items: center;
  justify-content: center;
}
.bs-modal[hidden] { display: none; }
.bs-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
}
.bs-modal-card {
  position: relative;
  z-index: 1;
  background: var(--bg-1);
  color: var(--fg-0);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 18px 22px;
  width: min(720px, 92vw);
  max-height: 92vh;
  overflow-y: auto;
  scrollbar-gutter: auto;
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.18);
}
.bs-fieldset[disabled] {
  opacity: 0.55;
  pointer-events: none;
}
.bs-fieldset[disabled] > legend { color: var(--fg-3); }
.bs-modal-h {
  margin: 0 0 4px 0;
  font-size: 16px;
  font-weight: 600;
  color: var(--fg-0);
  padding-right: 32px;
}
.bs-modal-close {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--fg-2);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}
.bs-modal-close:hover {
  background: var(--err);
  border-color: var(--err);
  color: #fff;
}
.bs-modal-close:active {
  background: color-mix(in srgb, var(--err) 85%, #000);
  border-color: color-mix(in srgb, var(--err) 85%, #000);
  color: #fff;
}
.bs-fieldset-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  align-items: start;
}
.bs-fieldset-row > .bs-fieldset { margin: 0; }
@media (max-width: 560px) {
  .bs-fieldset-row { grid-template-columns: 1fr; }
}
.bs-modal-sub {
  margin: 0 0 14px 0;
  font-size: 12px;
  color: var(--fg-2);
}
.bs-modal-sub code {
  font-size: 11px;
  background: var(--bg-2);
  color: var(--fg-0);
  padding: 1px 4px;
  border-radius: 3px;
}
.bs-modal-form { display: flex; flex-direction: column; gap: 12px; }

.bs-fieldset {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 10px 12px 8px 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  background: var(--bg-0);
}
.bs-fieldset > legend {
  padding: 0 6px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--fg-2);
  font-weight: 600;
}
.bs-fieldset label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--fg-0);
  cursor: pointer;
}
.bs-fieldset label:has(input:disabled) {
  color: var(--fg-3);
  cursor: not-allowed;
}
.bs-fieldset input[type="radio"] { accent-color: var(--accent); margin: 0; }
.bs-fieldset input[type="text"],
.bs-fieldset select {
  padding: 6px 10px;
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  background: var(--bg-1);
  color: var(--fg-0);
  font-size: 13px;
  margin-top: 4px;
}
.bs-fieldset input[type="text"]:focus,
.bs-fieldset select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}
.bs-hint-inline {
  font-size: 11px;
  color: var(--fg-2);
}
.bs-modal-warn {
  margin: 4px 0 0 0;
  font-size: 11px;
  color: var(--warn);
}
.bs-modal-msg { font-size: 12px; min-height: 16px; color: var(--fg-2); }
.bs-modal-msg.ok  { color: var(--ok); }
.bs-modal-msg.err { color: var(--err); }
/* 2026-05-26 — "View queue" link inside the restore-modal success
   state. Same accent colour the rest of the dashboard uses for action
   links, with a subtle underline so it's unmistakably clickable. */
.bs-modal-msg-link {
  color: var(--accent); text-decoration: underline;
  font-weight: 600; margin-left: 2px;
}
.bs-modal-msg-link:hover { color: var(--accent-hover); }
.bs-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
/* 2026-06-03 — operator: the plain .btn-svc Cancel/Create-restore in this
   modal fell back to the native browser-grey (no background on base .btn-svc),
   which reads as DISABLED in dark theme. Create restore now carries
   .btn-svc-primary (accent). Cancel gets an explicit themed SECONDARY fill —
   clearly enabled, distinct from the 0.45-opacity disabled style — driven by
   theme vars so light + dark both render correctly. Same trick for any other
   plain .btn-svc that needs a neutral-but-enabled look. */
.bs-cancel {
  background: var(--bg-3);
  color: var(--fg-0);
  border-color: var(--border-strong);
}
.bs-cancel:hover { background: var(--border-strong); }

/* 2026-05-21 — Right-click context menu (browse_snapshots.js) +
   lightweight toast for the "Download" action. Theme-variable based
   so light + dark both render correctly. */
.bs-ctxmenu {
  position: fixed;
  z-index: 9500;
  min-width: 160px;
  padding: 4px 0;
  background: var(--bg-1);
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
  font-size: 13px;
}
.bs-ctxmenu-item {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  padding: 7px 14px;
  color: var(--fg-0);
  cursor: pointer;
  font-size: 13px;
}
.bs-ctxmenu-item:hover {
  background: var(--bg-2);
  color: var(--accent);
}
.bs-ctxmenu-item:focus { outline: none; background: var(--bg-2); }

.bs-toast {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 9700;
  padding: 10px 14px;
  border-radius: 6px;
  background: var(--bg-1);
  color: var(--fg-0);
  border: 1px solid var(--border-strong);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  font-size: 13px;
  max-width: 380px;
}
.bs-toast-ok   { border-color: var(--ok); }
.bs-toast-err  { border-color: var(--err); }
.bs-toast-info { border-color: var(--accent); }

/* History tab — Destination column treatment */
.rj-dest {
  display: inline-block;
  padding: 2px 6px;
  background: var(--bg-2);
  color: var(--fg-1);
  border-radius: 3px;
  font-size: 11px;
  font-weight: 600;
}
.rj-dest-path {
  margin-top: 2px;
  font-size: 11px;
  color: var(--fg-2);
  font-family: var(--mono, monospace);
  word-break: break-all;
}

/* ─────────────────────────────────────────────────────────────────
   2026-05-21 — Integration of the snapshot subtree under each server
   in the existing Browse tree (replaces the parallel sidebar
   section). Reuses .tree-* classes so the snapshot rows look
   identical to the account-folder rows; only adds a horizontal
   divider between the live-baseline accounts and the snapshot
   categories.  PX-only ([[px-only-css]]); scoped to .tree-* /.bs-*.
   ───────────────────────────────────────────────────────────────── */
.tree-li-divider { padding: 4px 0; }
.tree-divider {
  height: 1px;
  background: var(--border);
  margin: 4px 8px;
  opacity: 0.7;
}
.tree-row-kind,
.tree-row-date,
.tree-row-snap-acct {
  padding: 2px 4px;
}
.tree-row-kind .tree-label,
.tree-row-date .tree-label {
  font-weight: 500;
}
.tree-row-snap-acct .tree-label-acct {
  color: var(--accent);
  text-decoration: none;
}
.tree-row-snap-acct .tree-label-acct:hover { text-decoration: underline; }
.tree-caret-leaf {
  color: var(--fg-2);
  cursor: default;
}
.tree-loadmore-btn { margin: 4px 0 6px 22px; }

/* 2026-05-25 — ITEM-043 (Scheduler) page styles. Two tiles: Global
   Schedule (top) and Per-Host Overrides (bottom). Each cadence card
   inside Global / drawer is a flexible vertical stack. */
.sched-tile { margin-bottom: 16px; }
.sched-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
  margin-top: 10px;
}
.sched-card {
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 12px;
}
/* 2026-05-26 — header row groups "Hourly / Daily / Weekly / Monthly"
   title + Enabled checkbox INLINE on one line, eliminating the gap
   that used to live between the title and the first input. */
.sched-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 4px;
}
.sched-card-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--fg-0);
  margin: 0;
}
.sched-enabled-inline {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  cursor: pointer;
  font-size: 12px;
  color: var(--fg-2);
}
.sched-enabled-inline input[type="checkbox"] { margin: 0; }
.sched-fields { display: flex; flex-direction: column; gap: 8px; margin-top: 0; }
/* Legacy class kept for backward compat with any callers that still
   produce the old "sched-enabled-row" wrapper. */
.sched-enabled-row {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 8px; cursor: pointer;
}
/* 2026-05-26 — live preview line under each cadence card's inputs.
   Operator-readable summary like "Runs every hour at the 15th minute.
   e.g. 10:15, 11:15, 12:15 …" — updates as the operator types. */
.sched-card-preview {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px dashed var(--border);
  color: var(--fg-2);
  font-size: 13px;
  line-height: 1.4;
  font-style: italic;
  word-break: break-word;
}
.sched-row {
  display: flex; flex-direction: column; gap: 3px;
  font-size: 12px; color: var(--fg-2);
}
.sched-row input, .sched-row select {
  background: var(--bg-1); border: 1px solid var(--border);
  border-radius: 4px; padding: 4px 6px;
  font-size: 13px; color: var(--fg-0);
}
/* 2026-05-28 — ITEM-073 Surgery 1: weekday chip-row used by the Weekly
   cadence card to pick multiple days at once. Each chip is a small
   toggle button (outline at rest, filled-accent when selected). The
   palette deliberately matches the existing icon-button state machine
   (--accent + #fff) so the page reads as one consistent UI.
   Preset row below the chips ("Weekdays / Weekends / Every day / Clear")
   uses the same outline-only style as the Backup History button so it
   doesn't compete with the primary Save action. */
.sched-day-row {
  display: flex; flex-wrap: wrap; gap: 6px;
}
.sched-day-chip {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 36px; padding: 4px 8px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 4px;
  font-size: 13px; font-weight: 600; color: var(--fg-1);
  cursor: pointer;
  user-select: none;
}
.sched-day-chip:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.sched-day-chip.selected {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-fg);
}
.sched-day-chip.selected:hover {
  background: color-mix(in srgb, var(--accent) 85%, #000);
  border-color: color-mix(in srgb, var(--accent) 85%, #000);
}
.sched-day-presets {
  display: flex; gap: 6px; flex-wrap: wrap;
  margin-top: 6px;
}
.sched-day-preset {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--fg-2);
  border-radius: 4px;
  padding: 3px 8px;
  font-size: 12px;
  cursor: pointer;
}
.sched-day-preset:hover {
  border-color: var(--accent);
  color: var(--accent);
}
/* 2026-05-26 — actions row: Save/Close buttons RIGHT-aligned, extra
   bottom padding so the drawer "feels" like a finished panel. */
.sched-actions {
  display: flex; gap: 8px; align-items: center;
  justify-content: flex-end;
  margin-top: 14px;
  padding-bottom: 6px;
}
.sched-status { color: var(--fg-2); font-size: 12px; margin-right: auto; }
.sched-host-table { width: 100%; }
/* 2026-05-25 — host cells inherit table.t typography (14 px, single
   weight) so the row reads like the Sessions → Recent Sessions table.
   No per-cell font overrides — operator asked for uniform fonts. */
.sched-host-toggle { transform: scale(1.2); }
.sched-host-drawer-row.hidden { display: none; }
.sched-host-drawer-row > td {
  background: var(--bg-1); padding: 14px 16px 18px 16px;
  border-top: 1px solid var(--border);
}
/* 2026-05-26 — editing-state highlight. When a host's drawer is open
   the host row + its drawer get a left-side accent border and a
   subtle accent-tinted background so the operator can clearly see
   which row the editor belongs to. The host-row's accent border is
   set on the first cell so it doesn't fight the row's normal table
   border-collapse behaviour. */
/* 2026-05-26 — selected-row left rail uses the same MUTED blue as the
   bottom border (color-mix accent 35%) and the same 2 px weight, so
   the rail + bottom border form one consistent thin frame instead of
   a heavy dark left edge competing with the bottom.
   2026-05-26 (later) — backgrounds switched from accent-mixed-with-
   bg-1 (which produced a muddy grey-blue) to a CLEAN light blue tint
   for both light and dark themes. Cards inside the drawer get an even
   lighter blue so they sit on top of the drawer with visible
   separation. Borders unchanged (still the muted-blue 35% mix). */
.sched-host-row.editing > td {
  background: var(--editing-bg);
}
.sched-host-row.editing > td:first-child {
  box-shadow: inset 2px 0 0 0 color-mix(in srgb, var(--accent) 35%, var(--border));
  font-weight: 600;
}
.sched-host-drawer-row:not(.hidden) > td {
  background: var(--editing-bg);
  box-shadow: inset 2px 0 0 0 color-mix(in srgb, var(--accent) 35%, var(--border));
  border-bottom: 2px solid color-mix(in srgb, var(--accent) 35%, var(--border));
}
.sched-host-drawer-row:not(.hidden) .sched-card {
  background: var(--editing-card-bg);
}
.sched-host-edit {
  background: transparent; border: 1px solid var(--accent);
  color: var(--accent); border-radius: 4px;
  width: 28px; height: 28px; cursor: pointer; font-size: 14px;
}
.sched-host-edit:hover { background: var(--accent); color: #fff; }

/* 2026-05-28 — Deep-link auto-open highlight. When the Manage Backups
   Action column's calendar icon brings the operator here with
   ?host=<aid>, scheduler.js scrolls the row into view and adds this
   class for ~2.2 s so the operator's eye lands on it. Animation cycles
   the row background through a soft amber pulse and back. */
.sched-host-highlight > td {
  animation: schedHostPulse 1.1s ease-in-out 0s 2;
}
@keyframes schedHostPulse {
  0%, 100% { background: transparent; }
  50%      { background: color-mix(in srgb, var(--info) 22%, transparent); }
}

/* 2026-05-25 — Global Schedule accordion + schedule-level timezone row. */
.sched-acc-head {
  display: flex; align-items: center; justify-content: space-between;
  cursor: pointer; user-select: none;
}
.sched-acc-head .tile-h { margin: 0; }
.sched-acc-caret {
  font-size: 14px; color: var(--fg-2);
  margin-left: 8px; min-width: 14px; text-align: center;
}
.sched-acc-body { margin-top: 12px; }
.sched-acc-body.hidden { display: none; }
.sched-tz-row {
  margin-bottom: 10px; padding-bottom: 8px;
  border-bottom: 1px dashed var(--border);
}
/* 2026-05-26 — label + dropdown on ONE row to reclaim vertical space.
   The .sched-row default lays them as a column (label above input);
   override here for the tz row only. */
.sched-tz-label {
  display: flex !important;
  flex-direction: row !important;
  align-items: center;
  gap: 12px;
  max-width: none;
  margin: 0;
}
.sched-tz-label > span {
  flex: 0 0 auto;
  white-space: nowrap;
  font-size: 12px;
  color: var(--fg-2);
}
.sched-tz-label select {
  flex: 0 1 360px;
  min-width: 220px;
  background: var(--bg-1); border: 1px solid var(--border);
  border-radius: 4px; padding: 4px 6px;
  font-size: 13px; color: var(--fg-0);
}

/* 2026-05-25 — Scheduler Logs page (Logs → Scheduler Logs).
   Toolbar = search input + dropdowns + Reload, on one wrap-friendly
   row. Table uses table.t typography + level badges with intent
   colours; details column wraps freely so long messages stay readable. */
.sl-toolbar {
  display: flex; flex-wrap: nowrap; gap: 8px; align-items: center;
  padding: 8px 0 12px 0;
  white-space: nowrap;
}
.sl-search {
  /* 2026-05-25 — operator asked for ~50% of the previous width. The
     row was nowrap with flex:1 1 auto for the search; on a 1280 px
     viewport that was ~720 px. Half ≈ 360 px. Fixed width so the
     status pill next to it gets a stable home. */
  flex: 0 0 360px;
  min-width: 0;
  background: var(--bg-1); border: 1px solid var(--border);
  border-radius: 4px; padding: 6px 10px;
  font-size: 13px; color: var(--fg-0);
  height: 32px; box-sizing: border-box;
}
.sl-select {
  flex: 0 0 auto;
  background: var(--bg-1); border: 1px solid var(--border);
  border-radius: 4px; padding: 5px 8px;
  font-size: 13px; color: var(--fg-0);
  height: 32px; box-sizing: border-box;
  width: auto;
}
/* 2026-05-25 — fixed widths for the three short dropdowns so the
   toolbar fits on one row even at 1280 px. Search expands to fill. */
.sl-toolbar [data-cell-id="sl-level"]    { width: 96px;  }
.sl-toolbar [data-cell-id="sl-window"]   { width: 110px; }
.sl-toolbar [data-cell-id="sl-pagesize"] { width: 96px;  }
.sl-toolbar [data-cell-id="sl-reload"]   { flex: 0 0 auto; height: 32px; box-sizing: border-box; padding: 0 14px; min-width: 0; }
.sl-status {
  /* 2026-05-25 — now sits IMMEDIATELY after the search input. Fixed
     width so the controls right of it (level / window / pagesize /
     reload) stay anchored regardless of result-count digits. */
  color: var(--fg-2); font-size: 12px;
  flex: 0 0 260px; width: 260px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* 2026-05-26 — match the typography of other log/audit pages: drop
   the monospace + 12 px overrides on details + ev <code> so every
   cell inherits table.t's 14 px text. The only retained per-column
   property is the width hint (layout) and word-break (long detail
   strings) — both layout, not type. */
.sl-table .sl-col-ts      { width: 160px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.sl-table .sl-col-level   { width: 64px;  }
.sl-table .sl-col-ev      { width: 200px; }
.sl-table .sl-col-host    { width: 160px; }
.sl-table .sl-col-cadence { width: 80px;  }
.sl-table .sl-details     { word-break: break-word; }
.sl-table .sl-col-ev code { background: transparent; padding: 0; }
/* 2026-05-26 — ITEM-051: clickable session / cmd links inline in the
   scheduler-log Details column. */
.sl-session-link, .sl-cmd-link {
  color: var(--accent);
  text-decoration: none;
  font-variant-numeric: tabular-nums;
}
.sl-session-link:hover, .sl-cmd-link:hover { text-decoration: underline; }
.sl-cmd-link { color: var(--fg-2); }

.sl-badge-info  { background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--accent); }
.sl-badge-warn  { background: color-mix(in srgb, #f97316 22%, transparent);       color: #f97316; }
.sl-badge-error { background: color-mix(in srgb, #dc2626 22%, transparent);       color: #dc2626; }
.sl-badge-debug { background: color-mix(in srgb, var(--fg-2) 16%, transparent);   color: var(--fg-2); }

.sl-pager {
  display: flex; gap: 10px; align-items: center; justify-content: center;
  padding: 12px 0 4px 0;
}
.sl-pager-info { color: var(--fg-2); font-size: 12px; min-width: 120px; text-align: center; }

/* ---------------- 2026-05-26 — Phase 6 Create-Backup wizard ----------------
   Restyled to mirror the Restore popup (.bs-modal-card + .bs-fieldset)
   per operator directive. Each step is rendered as a fieldset-style
   container with bordered outline + uppercase mini-legend; inputs use
   the same border-strong / accent-ring focus chrome the restore form
   uses. Tab strip kept (the wizard's discriminator vs the single-card
   restore modal) but coloured to match the rest of the modal. */
.wizard-form { max-width: 100%; width: 100%; margin: 0; display: flex; flex-direction: column; gap: 14px; }
.wizard-tabs {
  display: flex; gap: 6px;
  margin: 18px 0 0 0;
  /* 2026-05-26 — Operator: keep a decent gap between the modal title
     and the form content. Title sits in .cb-modal-card h2 with
     margin-bottom 12px; the tab strip adds another 18px on top so the
     visual separation reads cleanly. */
  border-bottom: 1px solid var(--border-strong);
}
.wiz-tab {
  background: transparent; color: var(--fg-2);
  border: 1px solid transparent; border-bottom: 0;
  padding: 6px 12px; border-radius: 5px 5px 0 0;
  font-size: 13px; font-weight: 600; cursor: pointer;
}
.wiz-tab:hover { color: var(--fg-0); background: var(--bg-2); }
.wiz-tab.active {
  color: var(--fg-0); background: var(--bg-1);
  border-color: var(--border-strong);
  border-bottom-color: var(--bg-1);
  margin-bottom: -1px;
}
/* Step container styled like a .bs-fieldset card. Hidden when not
   active; min-height pins all six steps at the same footprint so the
   modal doesn't reflow on Next / Previous. */
.wiz-step {
  display: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 14px 16px 12px 16px;
  background: var(--bg-0);
  flex-direction: column; gap: 10px;
}
/* 2026-05-26 — Operator complaint: within step 1 the modal collapsed
   when toggling NEW agent (5 inputs) → EXISTING (1 select). Pin the
   step container to a HEIGHT (not min-height) so every step + every
   within-step variant renders at the same footprint. 460 px fits the
   tallest natural content (step 1 NEW + step 2 override with all
   knobs visible) without scroll; smaller variants render with
   padded whitespace inside the fieldset frame, which reads
   intentional and stops the modal from jumping. */
.wiz-step.active { display: flex; height: 460px; overflow-y: auto; }
/* Step's h2 acts as the fieldset legend — uppercase, muted, small. */
.wiz-step h2 {
  font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--fg-2);
  margin: 0 0 6px 0;
}
.wiz-step label {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 13px; color: var(--fg-0); cursor: pointer;
  margin: 0;
}
.wiz-step .radio-row {
  flex-direction: row; align-items: center; gap: 8px;
}
.wiz-step .radio-row input { margin: 0; }
.wiz-step input[type=text],
.wiz-step input[type=number],
.wiz-step select,
.wiz-step textarea {
  padding: 6px 10px; font-size: 13px;
  background: var(--bg-1); color: var(--fg-0);
  border: 1px solid var(--border-strong); border-radius: 4px;
}
.wiz-step input[type=text]:focus,
.wiz-step input[type=number]:focus,
.wiz-step select:focus,
.wiz-step textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}
.wiz-step input[type="radio"] { accent-color: var(--accent); margin: 0; }
.wiz-sub {
  margin: 4px 0 0 20px; padding-left: 10px;
  border-left: 2px solid var(--border);
  display: flex; flex-direction: column; gap: 8px;
}
.wiz-note { color: var(--fg-2); font-size: 12px; margin: 0; }
.wiz-controls {
  display: flex; gap: 8px; justify-content: flex-end;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.form-errors {
  padding: 8px 12px; margin: 0;
  background: color-mix(in srgb, var(--err) 12%, transparent);
  color: var(--err); border-radius: 5px;
}
.form-err { font-size: 13px; }
.token-once {
  display: inline-block; padding: 6px 12px;
  background: var(--bg-2);
  border: 1px dashed var(--accent); border-radius: 4px;
  font-size: 13px;
}
.token-warn { color: var(--warn); font-size: 12px; margin-top: 4px; }
.next-steps { display: flex; gap: 8px; margin-top: 16px; }

/* 2026-05-26 — Path-scoped restore: greys-out the Scope fieldset
   when the modal was opened from a folder/file right-click. The
   legend explainer is added by JS; this rule provides the visual
   "this is not for you" cue. */
.bs-fieldset.bs-fieldset-disabled {
  opacity: 0.55;
  pointer-events: none;
}
.bs-fieldset.bs-fieldset-disabled legend .bs-hint-inline {
  color: var(--fg-2); font-style: italic; font-size: 11px;
}

/* 2026-05-26 — Queue History (history-table) RESULT column reuses the
   audit-log Detail expand pattern: CSS-driven ellipsis when
   collapsed, full-text inline when the row carries .expanded. The
   rules below mirror the audit-table block but scope to
   table.t.history-table so the two pages stay independently styled.
   Operator complaint: long result_summary blobs were hidden in a
   drawer row beneath; the audit-log inline style is what they want
   here too. */
table.t.history-table td.audit-detail-cell {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  position: relative; max-width: 480px;
}
table.t.history-table tr.audit-row td.audit-detail-cell:has(.audit-detail-text) {
  cursor: pointer;
}
table.t.history-table tr.audit-row.expanded td.audit-detail-cell {
  white-space: pre-wrap !important;
  word-break: break-word !important;
  overflow: visible !important;
  text-overflow: clip !important;
  max-width: none !important;
}
table.t.history-table tr.audit-row.expanded td.audit-detail-cell .audit-detail-text {
  white-space: pre-wrap !important;
  display: inline-block;
  max-width: 100%;
  font-family: monospace;
  font-size: 12px;
}
table.t.history-table tr.audit-row.expanded .audit-detail-caret::after { content: "▴"; }
section.tile:has(table.t.history-table) { overflow-x: auto; }

/* ---------------- 2026-05-26 — Create-Backup popup modal ----------------
   Visual reference: the existing Restore popup (.bs-modal-card +
   .bs-fieldset family). Operator wanted the wizard to MATCH the
   restore popup's frame, fieldset outlines, legend chrome and
   centered screen position — earlier styling read as transparent /
   top-anchored / inconsistent with the rest of the dashboard.
*/
.cb-modal-backdrop {
  position: fixed; inset: 0; z-index: 9000;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0.55);
}
.cb-modal-backdrop.hidden { display: none; }
.cb-modal-card {
  position: relative;
  background: var(--bg-1);
  color: var(--fg-0);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  width: min(720px, 92vw);
  max-height: 92vh;
  overflow-y: auto;
  padding: 18px 22px;
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.18);
}
.cb-modal-card h2 {
  margin: 0 0 12px 0;
  font-size: 16px; font-weight: 600;
  color: var(--fg-0);
  padding-right: 32px;
}
.cb-modal-close {
  position: absolute; top: 10px; right: 10px;
  width: 28px; height: 28px;
  display: flex; align-items: center; justify-content: center;
  background: transparent; color: var(--fg-2);
  border: 1px solid transparent; border-radius: 4px;
  font-size: 20px; line-height: 1; cursor: pointer;
  padding: 0;
}
.cb-modal-close:hover {
  background: var(--err); border-color: var(--err); color: #fff;
}
.cb-modal-close:active {
  background: color-mix(in srgb, var(--err) 85%, #000);
  border-color: color-mix(in srgb, var(--err) 85%, #000);
  color: #fff;
}
.cb-modal-card .wizard-form { max-width: 100%; width: 100%; }
.wiz-done .kv { margin: 12px 0; }
.wiz-done .kv dt { color: var(--fg-2); font-size: 12px; margin-top: 8px; }
.wiz-done .kv dd { margin: 0; font-size: 14px; }

/* 2026-05-26 — Policy override knobs: compact 2-column grid + checkbox-chip
   rows for category / compliance selection. The grid lives inside a
   .wiz-sub (already styled as a left-rule indented block); chips are
   small inline-block pills so several categories fit in one line. */
.wiz-grid-2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px 16px;
  border-left: none !important;     /* drop the .wiz-sub rule for grid layout */
  margin: 4px 0 0 0;
  padding-left: 0;
}
.wiz-grid-2 > label { margin: 0; gap: 2px; font-size: 12px; }
.wiz-grid-2 > label > input,
.wiz-grid-2 > label > select { margin-top: 2px; }
.wiz-grid-span2 { grid-column: 1 / span 2; }
.wiz-mini-legend {
  font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--fg-2);
  margin: 4px 0 4px 0;
}
.wiz-checkbox-row {
  display: flex; flex-wrap: wrap;
  gap: 4px 16px;   /* tight vertical, generous horizontal for flat-label layout */
}
/* 2026-05-26 — Operator: drop the pill enclosure on category /
   compliance checkboxes. Flat label + checkbox, no border, no
   rounded background. Spacing kept tight so the row still wraps
   cleanly inside the 460 px step. */
.checkbox-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 0;
  border: none;
  border-radius: 0;
  background: transparent;
  font-size: 12px; cursor: pointer;
  user-select: none;
  color: var(--fg-0);
}
.checkbox-chip input { accent-color: var(--accent); margin: 0; }
.checkbox-chip:has(input:checked) {
  color: var(--fg-0);
  font-weight: 500;
}

/* 2026-05-26 — Wizard Step 3 accounts table. Compact checkbox grid
   that fits inside the 460px-tall step area; long lists scroll. */
.wiz-accts-live { width: 100%; }
.wiz-accts-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.wiz-accts-table thead th {
  text-align: left; padding: 6px 8px;
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--fg-2);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; background: var(--bg-0);
}
.wiz-accts-table thead th.col-check { width: 28px; }
.wiz-accts-table tbody td {
  padding: 5px 8px; border-bottom: 1px solid var(--border);
  color: var(--fg-0);
}
.wiz-accts-table tbody tr:hover td { background: color-mix(in srgb, var(--accent) 6%, transparent); }
.wiz-accts-table input[type="checkbox"] { accent-color: var(--accent); margin: 0; }
.wiz-accts-fallback { margin-top: 8px; }

/* 2026-05-26 — Per-account progress table: fix column widths so values
   changing (— → counts → larger counts → committing) don't shift
   neighbouring columns. table-layout:fixed locks the cell widths to
   the <colgroup> values; word-break ensures long account names wrap
   inside the Account column rather than pushing everything sideways. */
table.t.t-per-account {
  table-layout: fixed;
  width: 100%;
}
table.t.t-per-account .col-acct     { width: 14%; }
table.t.t-per-account .col-progress { width: 22%; }
table.t.t-per-account .col-files    { width: 16%; }
table.t.t-per-account .col-bytes    { width: 16%; }
table.t.t-per-account .col-phase    { width: 16%; }
table.t.t-per-account .col-through  { width: 16%; }
/* 2026-05-26 — Operator: 30 px breathing room between the Progress
   bar's right edge and the Files count. Applied as padding-left on
   the Files column (3rd col); the visible text content of Files
   shifts right by 30 px inside its cell, and Bytes / Phase /
   Throughput follow naturally since each subsequent column starts
   where the previous one's cell ends. Table-layout: fixed means
   the column-WIDTHS don't change, but the visible offset reads as
   if the right block of columns moved 30 px right. */
table.t.t-per-account th:nth-child(3),
table.t.t-per-account td:nth-child(3) { padding-left: 30px; }
table.t.t-per-account td,
table.t.t-per-account th {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Account name can wrap so a long username doesn't get truncated. */
table.t.t-per-account td:first-child { white-space: normal; word-break: break-word; }
/* Progress bar wrapper fills the cell. */
table.t.t-per-account .progress-row { width: 100%; }
table.t.t-per-account .progress-row progress { flex: 1; min-width: 0; }

/* 2026-05-27 — Inbox / Run 3-column layout. Operator-specified
   grouping (see screenshot 2026-05-27 01:18). Three vertical columns
   separated by 1 px borders:
     col 1 — basic run knobs (Poll interval, Max parallel hashes,
             Max parallel uploads, Manifest chunk size)
     col 2 — Dynamic Uploading group (checkbox header + 3 numerics)
     col 3 — Compression + Bandwidth group (checkbox header + select
             + custom Mbps)
   The fieldset.ovr-run-fieldset opts out of the default 4-col grid
   the regular .ovr-form-grid fieldset sets, switching to a single
   stacked column (label header on top, then the 3-col grid below). */
.ovr-form-grid fieldset.ovr-run-fieldset {
  display: block;        /* override 4-col grid from .ovr-form-grid fieldset */
  padding: 14px 0 12px 0;
}
.ovr-run-fieldset > .ovr-section-label {
  padding: 0 18px;
  margin-bottom: 14px;
}
.ovr-run-3col {
  display: grid;
  grid-template-columns: 1fr 1.25fr 1fr;
  align-items: start;
}
.ovr-run-col {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 4px 22px;
  min-width: 0;
}
/* Vertical dividers between columns. */
.ovr-run-col + .ovr-run-col {
  border-left: 1px solid var(--border);
}
/* Each label-input pair flows label-left, input-right with the label
   text taking the remaining horizontal space. !important on
   flex-direction overrides .ovr-form-grid label's flex-direction:
   column at the same specificity, which would otherwise stack the
   label text above the input vertically. */
.ovr-form-grid .ovr-run-row {
  display: flex !important;
  flex-direction: row !important;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 0;
  padding: 0;
  font-size: 13px;
  color: var(--fg-1);
}
.ovr-form-grid .ovr-run-row > input[type="number"],
.ovr-form-grid .ovr-run-row > input[type="text"],
.ovr-form-grid .ovr-run-row > select {
  margin-left: 0;
  margin-top: 0;
  max-width: 110px;
  width: 110px;
  flex: 0 0 110px;
}
.ovr-form-grid .ovr-run-row > select {
  max-width: 140px;
  width: 140px;
  flex: 0 0 140px;
}

/* Feature-flag checkbox at the top of cols 2 and 3 acts as a group
   header — slightly bolder, with the sub-text on a second line. */
.ovr-run-feature-header {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 0 0 6px 0;
  font-size: 13px;
  color: var(--fg-0);
  font-weight: 600;
  cursor: pointer;
}
.ovr-run-feature-header > input {
  margin: 3px 0 0 0;
  accent-color: var(--accent);
}
.ovr-run-feature-title {
  display: flex;
  flex-direction: column;
  gap: 2px;
  color: var(--fg-0);
}
.ovr-run-feature-sub {
  font-weight: 400;
  font-size: 12px;
  color: var(--fg-2);
}
/* Italic helper sub-text under threshold / range fields. Negative
   margin pulls it closer to the input above so the pair reads as a
   single visual unit. */
.ovr-run-hint {
  font-size: 11px;
  font-style: italic;
  color: var(--fg-2);
  margin: -4px 0 0 0;
  padding-left: 0;
  line-height: 1.35;
}

/* ─────────── 2026-05-27 — session-detail UI tweaks ─────────── */

/* Files Changed collapse: chevron button-row replaces the static
   h3 + subtitle header so the operator can hide the changeset table
   (which is rarely interesting once a session is running) and keep
   the Agent log scrollable in the viewport. */
.card-h-collapse {
  display: flex; align-items: center; gap: 10px;
  background: transparent; border: 0; padding: 0; margin: 0;
  cursor: pointer; text-align: left;
  color: inherit; width: 100%;
}
.card-h-collapse h3      { margin: 0; }
.card-h-collapse:hover h3 { color: var(--accent); }
.card-h-chevron {
  display: inline-block;
  font-size: 11px; color: var(--fg-2);
  width: 14px; text-align: center;
  transition: color 0.15s ease;
}
.card-h-collapse:hover .card-h-chevron { color: var(--accent); }
.card-collapsible.hidden { display: none; }

/* Agent log toolbar — single row, dropdown 15 % / search 70 % / tail
   marker right. Overrides the older flex-wrap layout for this row only. */
.log-toolbar.log-toolbar-row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: nowrap;
  margin: 8px 0 12px 0;
  padding: 0 4px;
}
.log-toolbar.log-toolbar-row > .log-level {
  flex: 0 0 15%;
  min-width: 100px;
  background: var(--bg-2); color: var(--fg-0);
  border: 1px solid var(--border-strong); border-radius: 4px;
  padding: 6px 10px; font-size: 13px;
}
.log-toolbar.log-toolbar-row > .log-filter {
  flex: 0 0 70%;
  background: var(--bg-2); color: var(--fg-0);
  border: 1px solid var(--border-strong); border-radius: 4px;
  padding: 6px 12px; font-size: 13px;
}
.log-toolbar.log-toolbar-row > .log-filter:focus,
.log-toolbar.log-toolbar-row > .log-level:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}
.log-toolbar.log-toolbar-row > .log-tail-marker {
  flex: 1 1 auto;
  margin-left: 0;
  text-align: right;
}

/* Level filter — CSS hides log lines whose level class doesn't satisfy
   the current floor. `log-level-info` viewer state keeps all visible;
   `log-level-warn` hides INFO; `log-level-error` hides INFO + WARN. */
.log-viewer.log-level-warn  .log-line:has(.log-lvl-info)  { display: none; }
.log-viewer.log-level-error .log-line:has(.log-lvl-info),
.log-viewer.log-level-error .log-line:has(.log-lvl-warn),
.log-viewer.log-level-error .log-line:has(.log-lvl-debug) { display: none; }

/* Search filter — JS toggles .log-line-hidden per line; the rule
   wins over the level-filter `display: none` only when active. */
.log-line.log-line-hidden { display: none; }

/* 2026-05-27 — "Accounts not considered" section on session-detail.
   Informational (not an error). Subtle outline + accent tint so the
   operator notices it's a disclosure callout, not a status problem. */
.anc-card {
  border-left: 3px solid var(--accent);
}
.anc-card .card-h h3 { color: var(--fg-0); }
.anc-blurb {
  margin: 6px 0 12px 0;
  font-size: 12px;
  color: var(--fg-2);
  line-height: 1.45;
}
.anc-table { width: 100%; }
.anc-table thead th {
  text-align: left;
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--fg-2);
}
.anc-table tbody td { font-size: 13px; color: var(--fg-0); }
.anc-table tbody td:nth-child(2) { color: var(--fg-2); font-style: italic; }

/* 2026-05-27 — Coming-soon placeholder stub pages (Storage / SaaS
   App / Customers / Billing / Notifications). Reuses the standard
   .tile + .tile-h + .sub conventions so the look + feel matches
   every other page in the dashboard — no custom typography, no
   custom border / background / fonts. Only addition: a small inline
   phase badge to the right of the tile heading so the operator sees
   at a glance what release window this page is targeted for. */
.cs-phase-badge {
  display: inline-block;
  margin-left: 10px; padding: 2px 8px;
  font-size: 10px; font-weight: 600; letter-spacing: 0.4px;
  text-transform: uppercase; vertical-align: middle;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-radius: 4px;
}
.cs-desc { color: var(--fg-1); }
.cs-desc p { margin: 0 0 10px 0; }
.cs-desc p:last-child { margin-bottom: 0; }
.cs-desc ul { margin: 0 0 10px 18px; padding: 0; }

/* ─────────── 2026-05-27 — Customers + Billing form chrome ───────────
   Minimal additions on top of the existing .tile / .t / .btn-svc /
   .badge / .dl conventions. No custom typography. */

/* Form-error list inside a .tile (used by org/user/role/apikey forms). */
.form-errors {
  margin: 0; padding: 0 0 0 18px;
  color: var(--err); font-size: 13px;
}
.form-errors li { margin: 2px 0; }

/* Action row at the bottom of a form (Submit + Cancel). */
.form-actions {
  display: flex; gap: 8px;
  justify-content: flex-end;        /* 2026-05-27 — bottom buttons right-aligned */
  margin-top: 14px; padding-top: 12px;
  border-top: 1px solid var(--border);
}

/* 2026-05-27 — Required-asterisk on form labels (Identity section). */
.dl-pair .req { color: #dc2626; font-weight: 700; margin-left: 2px; }

/* 2026-05-27 — ITEM-058: muted-tint rows for archived Orgs / soft-deleted agents. */
table.t tr.row-archived td { color: var(--fg-2); opacity: 0.75; }
table.t tr.row-archived td strong { font-weight: 500; }

/* 2026-05-27 — Form info list inside termination-confirm tile. */
.form-info { margin: 0; padding-left: 18px; }
.form-info li { margin: 6px 0; color: var(--fg-1); }
.form-info li strong { color: var(--fg-0); }

/* 2026-05-27 — Add-row at the bottom of inline-editable tables (Plan detail).
   Visual divider so the "+ Add tier" / "+ Add class" row reads as separate
   from existing rows. */
table.t tr.add-row td {
  background: color-mix(in srgb, var(--bg-1) 60%, transparent);
  border-top: 1px dashed var(--border);
}
table.t tr.add-row input,
table.t tr.add-row select {
  width: 100%;
  box-sizing: border-box;
}
table.t td.action-col {
  display: flex; gap: 6px; align-items: center;
  white-space: nowrap;
}

/* Filter bar on list pages (Org filter, action filter, etc.). */
.filter-bar {
  display: flex; flex-wrap: wrap;
  gap: 14px; align-items: center;
  font-size: 13px; color: var(--fg-2);
}
.filter-bar label { display: flex; align-items: center; gap: 6px; }
.filter-bar select,
.filter-bar input[type="text"] {
  padding: 4px 10px; font-size: 13px;
  background: var(--bg-2); color: var(--fg-0);
  border: 1px solid var(--border-strong); border-radius: 4px;
}

/* Permission-flag matrix on the role form. Each group is its own
   fieldset with a small legend, then a list of checkbox labels. */
.perm-group {
  border: 1px solid var(--border); border-radius: 5px;
  padding: 10px 14px; margin: 10px 0;
}
.perm-group > legend {
  font-size: 12px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.4px; color: var(--fg-2);
  padding: 0 6px;
}
.perm-group label.cb {
  display: flex; align-items: center; gap: 8px;
  margin: 4px 0; font-size: 13px;
}
.perm-group label.cb code {
  font-size: 12px; color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  padding: 1px 5px; border-radius: 3px;
}

/* dl / dl-pair layout used by the form pages. Existing .overall-meta-4col
   targets a 4-col session-detail grid; for forms we want a simpler
   stacked label / input layout. */
.tile > form.dl,
.tile > .dl {
  display: flex; flex-direction: column; gap: 10px;
}
.tile > form.dl .dl-pair,
form .tile > .dl .dl-pair {
  display: grid !important;
  grid-template-columns: 120px 1fr !important;
  gap: 12px; align-items: center;
  /* 2026-05-27 — Indent halved from 250 → 100 px on the row container.
     !important on display+grid because the same .dl-pair class is also used
     by display-only overall-meta-4col which defines its own grid; without
     !important the source-order winner can flip. */
  padding-left: 100px;
}

/* 2026-05-28 — Two-column layout for sections like Identity on the
   Org form. Container .dl.dl-2col holds TWO child .dl-2col-col flex
   columns, each carrying its own .dl-pair items. Two independent
   columns avoid the row-height sharing of CSS Grid (a tall textarea
   in col 2 row 3 was bleeding extra space into col 1 row 3). */
form .tile > .dl.dl-2col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 24px;
  padding-left: 100px;
}
.dl-2col-col {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
form .tile > .dl.dl-2col .dl-pair {
  padding-left: 0 !important;   /* indent already on the outer container */
}
.tile > form.dl .dl-pair dt,
form .tile > .dl .dl-pair dt {
  margin: 0;
  font-size: 14px; font-weight: 500; color: var(--fg-2);   /* 2026-05-27 — bumped 13→14 px */
}
.tile > form.dl .dl-pair dd,
form .tile > .dl .dl-pair dd { margin: 0; }
.tile > form.dl input[type="text"],
.tile > form.dl input[type="email"],
.tile > form.dl input[type="number"],
.tile > form.dl select,
.tile > form.dl textarea,
form .tile > .dl input[type="text"],
form .tile > .dl input[type="email"],
form .tile > .dl input[type="number"],
form .tile > .dl select,
form .tile > .dl textarea {
  /* 2026-05-27 — Inputs capped at 400 px (was 600 px). */
  width: 100%; max-width: 400px;
  padding: 6px 10px; font-size: 14px;   /* 2026-05-27 — bumped 13→14 px (so placeholders read at 14 too) */
  background: var(--bg-2); color: var(--fg-0);
  border: 1px solid var(--border-strong); border-radius: 4px;
  box-sizing: border-box;
}
.tile > form.dl textarea,
form .tile > .dl textarea { font-family: inherit; max-width: 400px; }   /* 2026-05-27 — matched input width 400 px */

/* 2026-05-27 — .form-card: single outer container that wraps multiple
   <section class="tile"> blocks belonging to one form (e.g. New
   Organization: Identity / Compliance / Address / 3 contacts). Inner
   tiles lose their individual background/border and become divided
   blocks inside the single card. */
.form-card {
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 6px;
  margin-bottom: 14px;
  overflow: hidden;
}
.form-card > section.tile {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  border-radius: 0 !important;
  border-bottom: 1px solid var(--border) !important;
  margin: 0 !important;
  padding: 16px 18px;
}
.form-card > section.tile:last-child {
  border-bottom: none !important;
}
.form-card > section.tile > .tile-h {
  margin-top: 0;
}

/* 2026-05-27 — Back link: 15 px font, opted-in via .btn-back. The canonical
   .btn-svc font-size (13 px) is governed by the HARD RULE on labelled
   buttons; .btn-back is the explicit exception for navigation back-links. */
.btn-svc.btn-back { font-size: 15px; }
.tile > form.dl input:focus,
.tile > form.dl select:focus,
.tile > form.dl textarea:focus,
form .tile > .dl input:focus,
form .tile > .dl select:focus,
form .tile > .dl textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}

/* 2026-05-28 — Per-host phase spinner. Lives in the THROUGHPUT cell of
   the in-flight accounts table, 25 px to the right of the rate text.
   Border-spinner: a partial ring rotates at 0.9 s/turn so an operator
   sees ACTIVITY even when files_done/bytes_done plateau (long single-
   file uploads, slow remote API calls, etc.). Colour reflects the
   current_phase — green = uploading, orange = walking/diffing, blue =
   committing, gray for unmapped phases. Failed / done rows do not
   render the spinner. */
.phase-dot {
  display: inline-block;
  width: 14px;
  height: 14px;
  margin-left: 25px;
  border-radius: 50%;
  border: 2px solid color-mix(in srgb, currentColor 22%, transparent);
  border-top-color: currentColor;
  animation: phaseSpin 0.9s linear infinite;
  vertical-align: middle;
  flex-shrink: 0;
}
.phase-dot.phase-starting    { color: #8b5cf6;       } /* violet — bridges commit→walk */
.phase-dot.phase-dumping_db  { color: #06b6d4;       } /* cyan — Plesk DB dump (mysql+mssql) */
.phase-dot.phase-uploading   { color: var(--ok);     }
.phase-dot.phase-walking     { color: var(--warn);   }
.phase-dot.phase-hashing     { color: var(--warn);   }
.phase-dot.phase-diffing     { color: var(--warn);   }
.phase-dot.phase-no_baseline { color: var(--warn);   }
.phase-dot.phase-full_backup { color: var(--ok);     }
.phase-dot.phase-committing  { color: var(--accent); }
.phase-dot.phase-unknown     { color: var(--fg-2);   }
@keyframes phaseSpin {
  to { transform: rotate(360deg); }
}
/* Throughput cell needs flex so the 25 px gap is honoured even when the
   rate text is empty ("—") — without flex the spinner collapses against
   the cell's right edge in narrow tables. */
.bytes-cell.with-phase-dot {
  display: inline-flex;
  align-items: center;
}
/* 2026-05-28 — Fixed-width wrapper for the throughput value so the
   revolving phase-dot stays put when digit count changes (e.g. "5.12 MB/s"
   vs "523 KB/s" vs "1.23 GB/s"). Width is sized for the longest expected
   value ("888.88 MB/s") + a few px breathing room. tabular-nums keeps
   the decimal point from sliding within each width too. text-align right
   so the rightmost digit sits flush against the 25 px gap. */
.throughput-val {
  display: inline-block;
  min-width: 96px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* 2026-06-03 — ITEM-130 P1: Backup Nodes editor (Org detail page). All
   classes namespaced .org-nodes-* so generic table styling is untouched. */
.org-nodes-table th.org-nodes-act,
.org-nodes-table td.org-nodes-act { width: 64px; text-align: center; }
.org-nodes-dialog { min-width: 420px; }
.org-nodes-picklist {
  display: flex; flex-direction: column; gap: 6px;
  max-height: 320px; overflow-y: auto;
  margin: 4px 0 14px 0; padding: 4px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-0);
}
.org-nodes-pick {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 10px; border-radius: var(--radius);
  cursor: pointer; font-size: 13px; color: var(--fg-0);
}
.org-nodes-pick:hover { background: var(--bg-2); }
.org-nodes-pick-host { font-weight: 600; }
.org-nodes-pick-meta { margin-left: auto; color: var(--fg-2); font-size: 12px; }

/* 2026-06-03 — ITEM-130 P3: Add User modal. Namespaced .adduser-* so nothing
   bleeds into other pages. Reuses .confirm-dialog / .btn-svc / .icon-btn. */
.adduser-dialog { min-width: 560px; max-width: 96vw; }
.adduser-form { gap: 12px; }
.adduser-grid2 {
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px 16px;
}
.adduser-field { display: flex; flex-direction: column; gap: 4px; }
.adduser-lbl { font-size: 12px; color: var(--fg-2); font-weight: 600; }
.adduser-field input,
.adduser-field select { width: 100%; }
.adduser-pwrow { display: flex; align-items: center; gap: 8px; }
.adduser-pwrow input { flex: 1 1 auto; }
.adduser-orgadmin {
  display: flex; align-items: flex-start; gap: 8px;
  font-size: 13px; color: var(--fg-0); padding: 8px 10px;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-0);
}
.adduser-orgadmin input { margin-top: 2px; }
.adduser-gridwrap { display: flex; flex-direction: column; gap: 6px; }
.adduser-gridwrap.adduser-disabled { opacity: 0.4; pointer-events: none; }
.adduser-nodegrid { font-size: 13px; }
.adduser-nodegrid th:not(:first-child),
.adduser-nodegrid td.adduser-radio-cell { width: 64px; text-align: center; }
.adduser-node-host { font-weight: 600; }
.adduser-node-meta { color: var(--fg-2); font-size: 12px; }
.adduser-legend { font-size: 11px; color: var(--fg-2); }

/* 2026-06-03 — ITEM-130 P3: one-time secret reveal (admin password reset). */
.onetime-secret-lbl { margin-top: 14px; }
.onetime-secret {
  display: block; padding: 10px 14px; margin-top: 4px;
  background: var(--bg-2); border: 1px dashed var(--accent); border-radius: 4px;
  word-break: break-all; font-size: 14px; font-weight: 600;
}
.onetime-secret-note { margin-top: 10px; }

/* 2026-06-03 — ITEM-130 P5: customer-portal auth/MFA styles were MOVED OUT of
   this admin stylesheet into static/css/portal.css (operator decision) — keeping
   the two realms' CSS fully separate so a portal class can never collide with an
   admin class again (the earlier .auth-card collision regressed /admin/login/). */

/* 2026-06-03 — Modal field typography (operator): labels 13px, inputs 14px,
   set on the shared MODAL CONTAINER bases (.confirm-dialog themed dialogs incl.
   Add User/Add Node; .am-modal; .bs-modal-card Restore; .cb-modal-card wizards)
   so page-level forms are unaffected and every modal inherits it from one place. */
.confirm-dialog label, .am-modal label, .bs-modal-card label, .cb-modal-card label,
.confirm-dialog dt,    .am-modal dt,    .bs-modal-card dt,    .cb-modal-card dt,
.adduser-lbl {
  font-size: 13px;
}
.confirm-dialog input,  .confirm-dialog select,  .confirm-dialog textarea,
.am-modal input,        .am-modal select,        .am-modal textarea,
.bs-modal-card input,   .bs-modal-card select,   .bs-modal-card textarea,
.cb-modal-card input,   .cb-modal-card select,   .cb-modal-card textarea {
  font-size: 14px;
}
