/* ============================================================================
 * lotek styling contract — the PUBLIC styling API (core + extensions). See #840.
 *
 * Extensions render into core's base.html and already load this sheet, so they
 * inherit everything below for free. The contract they build against:
 *
 *   1. DESIGN TOKENS (the `:root` block). Reference `var(--…)` instead of
 *      hardcoded hex so markup inherits all 3 runtime themes (dark/light/h4xor
 *      via `data-theme`) with no rebuild. Public token groups:
 *        - color surfaces: --bg --bg2 --card --ink --muted --line --line-strong
 *        - accent/intent:  --accent --accent-strong --danger
 *        - controls:       --btn-* --field-* --pill-*
 *        - severity:       --sev-*     KPI: --kpi-*
 *      (Themes override these under `[data-theme=…]` further down.)
 *
 *   2. SEMANTIC COMPONENT CLASSES — .btn .panel-card .kv .status-* .row .list …
 *      Prefer a component class; reach for a utility only for a one-off repeat.
 *
 *   3. BOUNDED UTILITY VOCABULARY (search "Utility vocabulary" below). A small,
 *      named set for the handful of patterns that actually repeat across
 *      templates — "Tailwind-lite" without Tailwind, no build step. Do NOT grow
 *      this into a full utility framework; new entries earn their place by
 *      replacing many inline `style=` repeats. The inline-style count is
 *      ratcheted down by tests/test_inline_style_ratchet.py.
 * ========================================================================== */
:root {
  --content-max: 1500px;
  --bg: #060b12;
  --bg2: #0f1620;
  --card: #131f2c;
  --ink: #dce7f3;
  --muted: #8fa4b8;
  --accent: #22c55e;
  --accent-strong: #16a34a;
  --danger: #ff6b6b;
  --line: #2a3b4d;
  --line-strong: #3a4f66;
  --topbar: #0f1b28;
  --sidebar-bg: #0c1622;
  --sidebar-ink: #b8c8db;
  --sidebar-active-bg: #15263a;
  --sidebar-active-ink: #ffffff;
  --sidebar-active-bar: #22c55e;
  --appbar-bg: #0f1b28;
  --btn-bg: #132435;
  --btn-border: #2f4f6b;
  --btn-ink: #dce7f3;
  --btn-primary-bg: #15803d;  /* green-700: white text hits WCAG AA 4.5:1 (was #16a34a → 3.29:1) */
  --btn-primary-ink: #ffffff;
  --field-bg: #0d1722;
  --field-border: #2a3b4d;
  --pill-bg: #102338;
  --pill-border: #2e516d;
  --pill-ink: #8ad2ff;
  --shadow: rgba(0, 0, 0, 0.35);
  --progress-track: #1b2a39;
  --progress-fill: linear-gradient(90deg, #22c55e, #1ec8a5);
  /* Severity / status palette (Nessus-style) */
  --sev-critical: #b91c1c;
  --sev-high: #dc2626;
  --sev-medium: #ea580c;
  --sev-low: #ca8a04;
  --sev-info: #0284c7;
  --sev-ok: #16a34a;
  --kpi-running: #0284c7;
  --kpi-queued: #ca8a04;
  --kpi-completed: #16a34a;
  --kpi-failed: #dc2626;
  --kpi-runners: #7c3aed;
}

html[data-theme="light"] {
  --bg: #f3f5f7;
  --bg2: #e6f7ef;
  --card: #ffffff;
  --ink: #0f1720;
  --muted: #586474;
  --accent: #0f766e;
  --accent-strong: #0a5e58;
  --danger: #b42318;
  --line: #d8dee6;
  --line-strong: #b9c2cd;
  --topbar: #ffffff;
  --sidebar-bg: #1b2735;
  --sidebar-ink: #c8d4e3;
  --sidebar-active-bg: #243348;
  --sidebar-active-ink: #ffffff;
  --sidebar-active-bar: #0f766e;
  --appbar-bg: #ffffff;
  --btn-bg: #ffffff;
  --btn-border: #c8d2dd;
  --btn-ink: #0f1720;
  --btn-primary-bg: #0f766e;
  --btn-primary-ink: #ffffff;
  --field-bg: #ffffff;
  --field-border: #d8dee6;
  --pill-bg: #ecfbf8;
  --pill-border: #b7d8d4;
  --pill-ink: #0f513f;
  --shadow: rgba(15, 23, 32, 0.04);
  --progress-track: #e7edf2;
  --progress-fill: linear-gradient(90deg, #0f766e, #22a57f);
  --sev-critical: #991b1b;
  --sev-high: #dc2626;
  --sev-medium: #ea580c;
  --sev-low: #ca8a04;
  --sev-info: #0284c7;
  --sev-ok: #15803d;
  --kpi-running: #0284c7;
  --kpi-queued: #ca8a04;
  --kpi-completed: #15803d;
  --kpi-failed: #dc2626;
  --kpi-runners: #7c3aed;
}

html[data-theme="h4xor"] {
  --bg: #000000;
  --bg2: #000022;
  --card: #050514;
  --ink: #00ffff;
  --muted: #ccff00;
  --accent: #ff0099;
  --accent-strong: #ff33aa;
  --danger: #ff6600;
  --line: #0000cc;
  --line-strong: #2222ee;
  --topbar: #020218;
  --sidebar-bg: #020218;
  --sidebar-ink: #00ffff;
  --sidebar-active-bg: #050530;
  --sidebar-active-ink: #ccff00;
  --sidebar-active-bar: #ff0099;
  --appbar-bg: #020218;
  --btn-bg: #050514;
  --btn-border: #ff0099;
  --btn-ink: #00ffff;
  --btn-primary-bg: #ff0099;
  --btn-primary-ink: #000022;
  --field-bg: #000000;
  --field-border: #0000cc;
  --pill-bg: #050514;
  --pill-border: #0000cc;
  --pill-ink: #ccff00;
  --shadow: rgba(0, 0, 0, 0.8);
  --progress-track: #000033;
  --progress-fill: linear-gradient(90deg, #00ffff 0%, #ff0099 45%, #ff6600 70%, #ccff00 100%);
  --sev-critical: #ff0099;
  --sev-high: #ff6600;
  --sev-medium: #ccff00;
  --sev-low: #00ffff;
  --sev-info: #00ffff;
  --sev-ok: #ccff00;
  --kpi-running: #00ffff;
  --kpi-queued: #ccff00;
  --kpi-completed: #ccff00;
  --kpi-failed: #ff0099;
  --kpi-runners: #ff6600;
}

* { box-sizing: border-box; }
body {
  margin: 0;
  font-family: "Inter", "IBM Plex Sans", "Segoe UI", sans-serif;
  font-size: 15px;
  line-height: 1.5;
  background: var(--bg);
  color: var(--ink);
}
body.app-shell {
  display: grid;
  grid-template-columns: 232px 1fr;
  min-height: 100vh;
}
a, a:visited {
  color: var(--accent);
  text-decoration: none;
  transition: color 120ms ease, text-decoration-color 120ms ease;
}
a:hover {
  color: color-mix(in srgb, var(--accent) 80%, white 20%);
  text-decoration: underline;
}
a:active {
  color: color-mix(in srgb, var(--accent) 70%, black 30%);
}
/* Links embedded in help/body text must be distinguishable without relying on color alone
   (WCAG 1.4.1 / axe link-in-text-block). Standalone nav/button links keep text-decoration:none
   via their own component rules. */
.muted a { text-decoration: underline; }
.mono { font-family: "JetBrains Mono", "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace; }

/* Utility vocabulary (#840) is defined at the END of this sheet so utilities win
 * over component classes by source order (equal specificity). See there. */

/* Sidebar */
/* Sidebar scrim is mobile-only (shown by the .sidebar.open ~ rule in the ≤860px block); never on desktop. */
.sidebar-backdrop { display: none; }
.sidebar {
  position: sticky;
  top: 0;
  height: 100vh;
  /* When many extensions are mounted the nav + foot exceed 100vh; without this the margin-top:auto
     footer (and the .sidebar-version string) is pushed below the fixed-height panel with no way to
     reach it. Same fix the mobile query already applies — desktop was simply missing it. */
  overflow-y: auto;
  background: var(--sidebar-bg);
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  padding: 16px 0;
  z-index: 50;
  /* Thin, theme-matched scrollbar — the default OS bar is chunky/bright on the dark sidebar. Only shows
     when the nav overflows (overflow-y:auto above); vars adapt per theme. */
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}
.sidebar::-webkit-scrollbar { width: 8px; }
.sidebar::-webkit-scrollbar-track { background: transparent; }
.sidebar::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; }
.sidebar::-webkit-scrollbar-thumb:hover { background: var(--line-strong); }
.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 18px 18px 18px;
  color: var(--sidebar-active-ink);
  font-weight: 700;
  font-size: 17px;
  letter-spacing: 0.5px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 10px;
}
.sidebar-brand:hover { text-decoration: none; color: var(--sidebar-active-ink); }
.brand-mark { color: var(--accent); font-size: 18px; }
img.brand-mark { width: 24px; height: 24px; display: block; border-radius: 5px; }
.brand-text { font-family: ui-monospace, "Cascadia Mono", "JetBrains Mono", "Fira Code", Menlo, Consolas, monospace; text-transform: lowercase; letter-spacing: 1px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar-nav { display: flex; flex-direction: column; gap: 2px; padding: 4px 8px; }
.sidebar-nav a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  color: var(--sidebar-ink);
  border-radius: 8px;
  font-size: 14px;
  font-weight: 500;
  position: relative;
}
.sidebar-nav a:hover {
  background: color-mix(in srgb, var(--sidebar-active-bg) 60%, transparent);
  color: var(--sidebar-active-ink);
  text-decoration: none;
}
.sidebar-nav a.active {
  background: var(--sidebar-active-bg);
  color: var(--sidebar-active-ink);
}
.sidebar-nav a.active::before {
  content: "";
  position: absolute;
  left: -8px;
  top: 8px;
  bottom: 8px;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--sidebar-active-bar);
}
.nav-icon { width: 18px; text-align: center; color: var(--muted); font-size: 14px; }
.sidebar-nav a.active .nav-icon { color: var(--sidebar-active-bar); }
.nav-label { flex: 1; }
/* Extension sub-pages nested under their extension's group-header link (see base.html nav). */
.sidebar-nav a.nav-ext-child { padding-left: 30px; font-size: 13px; }
.sidebar-nav a.nav-ext-child .nav-icon { font-size: 12px; }
/* Section divider in the sidebar nav — groups the admin/config items (Settings, Admin) below the
   operational items. Colors come from per-theme vars so it adapts to dark/light/h4xor. */
.nav-section { margin: 10px 4px 2px; padding: 6px 10px 4px; border-top: 1px solid var(--line);
  font-size: 10px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.sidebar-foot {
  margin-top: auto;
  padding: 12px;
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.sidebar-version {
  font-size: 11px;
  text-align: center;
  letter-spacing: 0.04em;
  opacity: 0.9;  /* 0.7 dimmed muted text to 4.22:1 on --bg; 0.9 clears WCAG AA 4.5:1 */
}
.sidebar-user {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--sidebar-ink);
  font-size: 13px;
}
.user-avatar {
  width: 30px;
  height: 30px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--btn-primary-ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 13px;
}
.user-name { flex: 1; }
.btn-block { width: 100%; text-align: center; }

/* App main column */
.app-main { min-width: 0; }
.appbar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 20px;
  /* Cap the bar's inner content to the same width as <main>, left-aligned, while the bar
     background/border stay full-bleed. No-op below the cap (max() floors at the base 20px). */
  padding-right: max(20px, calc(100% - var(--content-max) - 20px));
  background: var(--appbar-bg);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 40;
}
.appbar-title {
  font-size: 18px;
  font-weight: 600;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.appbar-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.sidebar-toggle {
  display: none;
  background: transparent;
  border: 1px solid var(--line);
  color: var(--ink);
  border-radius: 8px;
  padding: 4px 10px;
  font-size: 16px;
  cursor: pointer;
}
.breadcrumb { display: flex; align-items: center; gap: 8px; min-width: 0; }
.breadcrumb a { color: var(--muted); }
.crumb-sep { color: var(--line-strong); }
.crumb-current { color: var(--ink); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The topbar title IS the page's <h1> (base.html). Reset UA h1 size/margin so it renders like the
   surrounding appbar text — covers the default title (.appbar-h1) and a breadcrumb's current-page
   crumb rendered as <h1 class="crumb-current">. Heading-tag change only, no layout change. */
.appbar-h1 { font-size: inherit; font-weight: inherit; line-height: inherit; margin: 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
h1.crumb-current { font-size: inherit; line-height: inherit; margin: 0; }

main { padding: 18px 20px 32px 20px; max-width: var(--content-max, 1500px); width: 100%; box-sizing: border-box; }

/* Wide-screen dashboard: use the reclaimed width as layout, not margin. The dashboard gets a
   wider cap and splits into two columns at ≥1800px; below that it's a single column (unchanged). */
.page-dashboard { --content-max: 1920px; }
/* minmax(0, 1fr), not bare 1fr: 1fr is minmax(auto, 1fr), so the single column grew to its widest
   child's min-content and .dash-col/.card overflowed a 390px phone by 528px (#1196). Same fix as the
   .form-grid mobile rule and the two-column rule below. */
.dashboard-grid { display: grid; gap: 14px; grid-template-columns: minmax(0, 1fr); align-items: start; }
@media (min-width: 1800px) {
  .dashboard-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

/* Canvas / console pages want the full width, not a reading cap: the Vector diagram editor
   (.ved) and the runner console (.page-wide). Setting --content-max:none uncaps <main>, and the
   appbar's calc() padding-right becomes invalid and drops out, restoring its full-bleed actions —
   so main and appbar stay aligned. :has() fails safe: no match → the page stays capped. */
.page-wide, body:has(.ved) { --content-max: none; }

/* Legacy topbar (kept for any non-extending pages) */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid var(--line);
  background: var(--topbar);
  position: sticky;
  top: 0;
}
.brand { font-weight: 700; color: var(--ink); text-decoration: none; }
.topnav a { margin-left: 12px; color: var(--muted); text-decoration: none; }
.topnav a:hover { text-decoration: underline; }
.card {
  background: var(--card);
  border: 1px solid var(--line-strong);
  border-radius: 14px;
  padding: 12px;
  margin-bottom: 14px;
  box-shadow: 0 8px 16px var(--shadow);
}
.row { display: flex; gap: 8px; align-items: center; }
.row h2 { flex: 1; margin: 0; }
.row form { margin: 0; }
.pill {
  border: 1px solid var(--pill-border);
  background: var(--pill-bg);
  color: var(--pill-ink);
  border-radius: 999px;
  padding: 4px 10px;
  font-size: 12px;
}
.btn {
  border: 1px solid var(--btn-border);
  background: var(--btn-bg);
  color: var(--btn-ink);
  border-radius: 8px;
  padding: 6px 12px;
  cursor: pointer;
  text-decoration: none;
  font-size: 13px;
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  line-height: 1.4;
}
.btn:hover { filter: brightness(1.05); text-decoration: none; }
.btn:disabled { opacity: 0.55; cursor: not-allowed; }
.btn.btn-primary {
  background: var(--btn-primary-bg);
  color: var(--btn-primary-ink);
  border-color: var(--btn-primary-bg);
}
.btn.btn-primary:hover { filter: brightness(1.08); }
.btn.btn-icon { padding: 6px 10px; font-size: 13px; }
.btn.btn-sm { padding: 4px 8px; font-size: 12px; }
/* Inline on/off toggle chip — matches the theme-switcher pill style */
.chip-btn {
  border: 1px solid var(--line);
  background: transparent;
  color: var(--muted);
  border-radius: 8px;
  padding: 2px 8px;
  font-size: 0.8em;
  font-family: inherit;
  cursor: pointer;
  line-height: 1.7;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.chip-btn.active {
  border-color: var(--accent, #1e90ff);
  background: color-mix(in srgb, var(--accent, #1e90ff) 12%, transparent);
  color: var(--accent, #1e90ff);
}
.btn.danger {
  border-color: color-mix(in srgb, var(--danger) 40%, transparent);
  background: color-mix(in srgb, var(--danger) 15%, transparent);
  color: var(--danger);
}
.form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}
.form-grid .full { grid-column: 1 / -1; }
/* Short single-line fields shouldn't run the full column on wide screens (line length ≫ 80ch).
   Textareas and .full rows stay fluid. */
.form-grid label:not(.full) .input:not(textarea) { max-width: 640px; }
.input {
  width: 100%;
  border: 1px solid var(--field-border);
  border-radius: 10px;
  padding: 8px;
  font: inherit;
  background: var(--field-bg);
  color: var(--ink);
}

/* Native controls follow the active theme, so date/number spinners, native <select> popups,
   scrollbars and the file-picker render dark instead of the light-on-dark browser default
   (this is what makes a native date input's calendar icon visible on the dark theme). */
html[data-theme="dark"],
html[data-theme="h4xor"] { color-scheme: dark; }
html[data-theme="light"] { color-scheme: light; }

/* File inputs: the "Choose File" button is a light OS default that clashes with the dark theme.
   The button is only styleable via ::file-selector-button, so theme the control + its button to
   match .input / .btn. */
input[type="file"] {
  border: 1px solid var(--field-border);
  border-radius: 10px;
  padding: 6px;
  font: inherit;
  background: var(--field-bg);
  color: var(--ink);
}
input[type="file"]::file-selector-button {
  margin-right: 10px;
  padding: 6px 12px;
  border: 1px solid var(--btn-border);
  border-radius: 8px;
  background: var(--btn-bg);
  color: var(--btn-ink);
  font: inherit;
  cursor: pointer;
}
input[type="file"]::file-selector-button:hover { filter: brightness(1.05); }

/* Bare form controls (no .input class) — theme them like .input so a native <input>/<select>/<textarea>
   that forgot the class (common on extension pages, which reach this stylesheet through the shared layout)
   still renders dark + sized to match instead of the light browser default. :where() keeps specificity at
   0 so .input, .combobox-input and every component class always win; checkboxes/radios/file/buttons/etc.
   keep their own rendering. */
:where(
  input:not([type="checkbox"], [type="radio"], [type="file"], [type="range"], [type="color"],
              [type="submit"], [type="button"], [type="image"], [type="reset"]),
  select,
  textarea
) {
  border: 1px solid var(--field-border);
  border-radius: 10px;
  padding: 8px;
  font: inherit;
  background: var(--field-bg);
  color: var(--ink);
}

/* Fleet Ops HUD form (runners page): stack each label above its control so the four fields read
   as one uniform row instead of one styled .input beside raw native select/number controls. */
#fleet-form label { display: flex; flex-direction: column; gap: 4px; }

/* Field-help popovers (module editor): a small "?" button reveals per-field help on click,
   keeping the form uncluttered. The popover anchors to its field label, so labels are relative.
   Scoped to the module-editor form (class on /modules/new|edit) so it can't shift positioned
   children of labels on other .form-grid pages (dashboard, assessments, runners, run types). */
.module-editor label { position: relative; }
.help-info {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  margin-left: 6px;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  vertical-align: middle;
}
.help-info:hover,
.help-info[aria-expanded="true"] {
  color: var(--accent);
  border-color: var(--accent);
}
.help-info:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.help-pop {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 1500;
  max-width: 480px;
  width: max-content;
  margin-top: 4px;
  padding: 10px 12px;
  background: var(--card);
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  box-shadow: 0 10px 28px var(--shadow);
  color: var(--ink);
  font-size: 13px;
  font-weight: 400;
  line-height: 1.5;
  white-space: normal;
}
.help-pop[hidden] { display: none; }
.help-pop code {
  background: var(--field-bg);
  border: 1px solid var(--field-border);
  border-radius: 4px;
  padding: 0 3px;
  font-size: 12px;
}
.help-pop .help-dyn { display: block; margin-top: 6px; color: var(--muted); }
.help-pop .help-dyn:empty { display: none; }
@media (max-width: 640px) { .help-pop { max-width: min(480px, 90vw); } }

.tag-source-hidden {
  display: none;
}
.tag-input-shell {
  width: 100%;
  min-height: 40px;
  border: 1px solid var(--field-border);
  border-radius: 10px;
  padding: 6px;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  background: var(--field-bg);
}
.tag-input-shell:focus-within {
  border-color: var(--accent);
}
.tag-input-entry {
  border: none;
  outline: none;
  background: transparent;
  color: var(--ink);
  min-width: 150px;
  flex: 1 1 150px;
  font: inherit;
}
.tag-input-entry::placeholder {
  color: var(--muted);
}
.tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--pill-border);
  background: var(--pill-bg);
  color: var(--pill-ink);
  border-radius: 999px;
  padding: 3px 8px;
  font-size: 12px;
}
.tag-chip-remove {
  border: none;
  background: transparent;
  color: inherit;
  cursor: pointer;
  font: inherit;
  line-height: 1;
  padding: 0;
}
.tag-chip-remove:hover {
  filter: brightness(1.2);
}
.twocol {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
}
.filter-grid {
  display: grid;
  grid-template-columns: 1.1fr 1fr 1.5fr 1.2fr 0.8fr auto;
  gap: 10px;
  align-items: end;
}
/* 3-col variant (Search + Tags + Apply button) — targets and runners pages */
.filter-grid-3col { grid-template-columns: 1fr 1fr auto; margin-bottom: 12px; }
.stats-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
}
.stat {
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 10px;
  background: color-mix(in srgb, var(--card) 90%, black 10%);
}
.input-with-btn {
  display: flex;
  gap: 6px;
  align-items: center;
}
.input-with-btn .btn.subtle {
  padding: 8px 10px;
  border-color: var(--line);
  background: color-mix(in srgb, var(--card) 80%, transparent 20%);
}
.input-with-btn .btn.subtle:hover {
  filter: brightness(1.05);
}
.stat .label { font-size: 12px; color: var(--muted); }
.stat .value { font-size: 20px; font-weight: 700; margin-top: 4px; }
.table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
.table th, .table td {
  border-bottom: 1px solid var(--line);
  padding: 8px 6px;
  text-align: left;
  vertical-align: top;
}
.table th { color: var(--muted); font-weight: 600; }
.table tr:hover { background: color-mix(in srgb, var(--card) 88%, white 12%); }
.table.sortable th { cursor: pointer; user-select: none; }
.table-scroll {
  overflow-x: auto;
}
.status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  border: 1px solid transparent;
}
.status::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: currentColor;
}
.status-running   { color: color-mix(in srgb, var(--kpi-running), #ffffff 38%);   background: color-mix(in srgb, var(--kpi-running) 18%, transparent); border-color: color-mix(in srgb, var(--kpi-running) 45%, transparent); }
.status-completed { color: color-mix(in srgb, var(--kpi-completed), #ffffff 38%); background: color-mix(in srgb, var(--kpi-completed) 18%, transparent); border-color: color-mix(in srgb, var(--kpi-completed) 45%, transparent); }  /* lighten green text: raw #16a34a on the tinted bg was 3.93:1 */
.status-failed    { color: color-mix(in srgb, var(--kpi-failed), #ffffff 38%);    background: color-mix(in srgb, var(--kpi-failed) 18%, transparent); border-color: color-mix(in srgb, var(--kpi-failed) 45%, transparent); }
.status-canceled  { color: color-mix(in srgb, var(--sev-medium), #ffffff 38%);    background: color-mix(in srgb, var(--sev-medium) 18%, transparent); border-color: color-mix(in srgb, var(--sev-medium) 45%, transparent); }
/* `error` = orchestration/environment fault, outcome INDETERMINATE (lost contact) — amber "attention",
   deliberately NOT the red of `failed` (a known scan failure). It is recoverable (a returning runner
   resurrects it), so it reads as a warning, not a dead end. */
.status-error     { color: color-mix(in srgb, var(--sev-high), #ffffff 38%);      background: color-mix(in srgb, var(--sev-high) 18%, transparent); border-color: color-mix(in srgb, var(--sev-high) 45%, transparent); }
.status-stopping  { color: color-mix(in srgb, var(--sev-medium), #ffffff 38%);    background: color-mix(in srgb, var(--sev-medium) 18%, transparent); border-color: color-mix(in srgb, var(--sev-medium) 45%, transparent); }
.status-queued, .status-scheduled, .status-stopped {
  color: var(--muted);
  background: color-mix(in srgb, var(--muted) 14%, transparent);
  border-color: color-mix(in srgb, var(--muted) 35%, transparent);
}
.pager {
  margin-top: 12px;
  display: flex;
  gap: 8px;
  align-items: center;
}
.threecol {
  display: grid;
  grid-template-columns: 1.2fr 1fr 1fr;
  gap: 14px;
}
.meta-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
  margin-top: 10px;
}
.muted { color: var(--muted); font-size: 13px; }
/* collection_preview macro badge — "… +N more" (see _macros.html) */
.cp-more { font-size: 0.85em; color: var(--muted); text-decoration: none; }
.cp-more:hover { text-decoration: underline; }
.list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.list li {
  border-bottom: 1px solid var(--line);
  padding: 7px 4px;
}
.list.compact li { padding: 5px 4px; font-size: 13px; }
.score {
  display: inline-block;
  min-width: 32px;
  text-align: center;
  padding: 2px 6px;
  border-radius: 999px;
  background: #ffe5b0;
  color: #643f00;
  font-weight: 700;
  font-size: 12px;
}
.mini {
  background: color-mix(in srgb, var(--card) 88%, black 12%);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 8px;
  white-space: pre-wrap;
  overflow: auto;
  max-height: 160px;
}
.mini.tall { max-height: 300px; }

/* Runner packaging / signing config fieldsets (#1634). Shared by settings_runner_signing.html
   and the runners.html packaging card, which previously carried identical per-page <style> blocks
   (folded here per #840). */
.signing-cfg { border: 1px solid var(--border, #333); border-radius: 6px; padding: .5rem .8rem; margin: .6rem 0; }
.signing-cfg legend { padding: 0 .3rem; }
.signing-cfg + .signing-cfg { margin-top: .8rem; }
.signing-radio { display: flex; align-items: flex-start; gap: .4rem; margin: .5rem 0; cursor: pointer; }
.signing-radio input[type=radio] { margin-top: .2rem; flex-shrink: 0; }
.signing-advanced { margin: .3rem 0; }
.signing-advanced summary { cursor: pointer; list-style: none; }
.signing-advanced summary::-webkit-details-marker { display: none; }
.signing-advanced-body { padding: .4rem 0 .2rem 1.4rem; }
.signing-advanced-body .input { width: 100%; max-width: 28rem; margin-top: .3rem; display: block; }
.signing-disabled { opacity: .55; }
.badge { font-size: 11px; padding: 1px 5px; border-radius: 3px; vertical-align: middle; }
.badge-default { background: var(--accent-muted, #334); color: var(--accent, #7af); }
.badge-soon { background: var(--border, #333); color: var(--muted, #888); }

.runner-commands summary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  padding: 4px 0;
}
.runner-commands summary::-webkit-details-marker { display: none; }
.runner-commands .chevron {
  transition: transform 140ms ease;
  font-size: 14px;
}
.runner-commands[open] .chevron { transform: rotate(90deg); }
.console {
  background: color-mix(in srgb, var(--card) 70%, black 30%);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 10px;
  white-space: pre-wrap;
  overflow: auto;
  height: 420px;
}
.console.resizable {
  resize: vertical;
  min-height: 260px;
}
.file { cursor: pointer; }
.file:hover { background: #f2f8f7; }
.progress-list { margin-top: 12px; display: grid; gap: 10px; }
.progress-item { border: 1px solid var(--line); border-radius: 10px; padding: 8px; background: color-mix(in srgb, var(--card) 92%, black 8%); }
.progress-head { display: flex; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
.progress-track { height: 8px; border-radius: 999px; background: var(--progress-track); overflow: hidden; }
.progress-fill { height: 100%; background: var(--progress-fill); }
/* Runners registry CPU meter thresholds (token-driven; replaces the old hardcoded hex in the poller). */
.progress-fill.warn { background: var(--sev-medium); }
.progress-fill.crit { background: var(--danger); }
/* Runners registry table: dim deactivated runners, faintly tint the awaiting-authorization group. */
tbody.runners-disabled tr { opacity: 0.72; }
tbody[data-pending-runners] td { background: color-mix(in srgb, var(--danger) 6%, transparent); }
.runner-fleet-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 10px;
}
.runner-fleet-card {
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 10px;
  background: color-mix(in srgb, var(--card) 92%, black 8%);
}
.runner-fleet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
}
.runner-fleet-link {
  font-weight: 700;
}
.runner-fleet-actions {
  margin-top: 8px;
}
.runner-detail-grid {
  display: grid;
  grid-template-columns: minmax(260px, 360px) minmax(0, 1fr);
  gap: 12px;
}
.runner-info-card {
  border: 1px solid var(--line);
  border-radius: 10px;
  background: color-mix(in srgb, var(--card) 90%, black 10%);
  padding: 10px;
}
.runner-info-row {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  border-bottom: 1px solid var(--line);
  padding: 7px 0;
}
.runner-info-row:last-child {
  border-bottom: none;
}
.runner-info-notes {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--line);
  white-space: pre-wrap;
}
.runner-reachability-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 2px 10px;
  font-size: 12px;
  font-weight: 600;
}
.runner-reachability-pill::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: currentColor;
}
.runner-reachability-pill.is-online {
  color: #22c55e;
  border-color: color-mix(in srgb, #22c55e 50%, var(--line) 50%);
  background: color-mix(in srgb, #22c55e 16%, transparent);
}
.runner-reachability-pill.is-offline {
  color: #ef4444;
  border-color: color-mix(in srgb, #ef4444 50%, var(--line) 50%);
  background: color-mix(in srgb, #ef4444 14%, transparent);
}
.runner-reachability-pill.is-unknown {
  color: var(--muted);
  border-color: color-mix(in srgb, var(--muted) 45%, var(--line) 55%);
  background: color-mix(in srgb, var(--muted) 10%, transparent);
}
.runner-terminal-wrap {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.runner-terminal-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
#terminal-terminate-runner {
  border-color: color-mix(in srgb, var(--danger) 65%, var(--line) 35%);
  background: color-mix(in srgb, var(--danger) 20%, transparent);
  color: color-mix(in srgb, var(--danger) 75%, var(--ink) 25%);
}
#terminal-terminate-runner:hover {
  filter: brightness(1.08);
}
/* Used by the standalone /shell page's connection indicator (shell.html). The runner-detail
   terminal deliberately has no such indicator — the page loading already proves the dashboard link,
   and the Reachability pill is the meaningful runner-liveness signal there. */
#terminal-link-status.state-ok {
  color: #22c55e;
}
#terminal-link-status.state-bad {
  color: #ef4444;
}
#terminal-link-status.state-neutral {
  color: var(--muted);
}
#terminal-input-owner {
  padding: 2px 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: color-mix(in srgb, var(--card) 86%, black 14%);
}
#terminal-input-owner.is-owner {
  border-color: color-mix(in srgb, #22c55e 55%, var(--line) 45%);
  background: color-mix(in srgb, #22c55e 18%, var(--card) 82%);
  color: color-mix(in srgb, #22c55e 65%, var(--ink) 35%);
}
#terminal-input-owner.is-viewer {
  border-color: color-mix(in srgb, #f59e0b 55%, var(--line) 45%);
  background: color-mix(in srgb, #f59e0b 18%, var(--card) 82%);
  color: color-mix(in srgb, #f59e0b 70%, var(--ink) 30%);
}
#terminal-input-owner.is-unclaimed {
  border-color: var(--line);
  background: color-mix(in srgb, var(--card) 86%, black 14%);
  color: var(--muted);
}
.runner-terminal-tabs {
  display: inline-flex;
  border: 1px solid var(--line);
  border-radius: 9px;
  overflow: hidden;
}
.runner-tab {
  border: none;
  border-right: 1px solid var(--line);
  background: color-mix(in srgb, var(--card) 88%, black 12%);
  color: var(--muted);
  padding: 6px 12px;
  padding-left: 24px;
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  position: relative;
}
.runner-tab::before {
  content: "";
  position: absolute;
  left: 9px;
  top: 50%;
  transform: translateY(-50%);
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--muted) 65%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--line) 80%, transparent);
}
.runner-tab.state-running::before {
  background: #22c55e;
  box-shadow: 0 0 0 1px color-mix(in srgb, #22c55e 50%, transparent);
}
.runner-tab.state-starting::before,
.runner-tab.state-stopping::before {
  background: #f59e0b;
  box-shadow: 0 0 0 1px color-mix(in srgb, #f59e0b 50%, transparent);
}
.runner-tab.state-stopped::before,
.runner-tab.state-idle::before {
  background: color-mix(in srgb, var(--muted) 65%, transparent);
}
.runner-tab.state-error::before {
  background: #ef4444;
  box-shadow: 0 0 0 1px color-mix(in srgb, #ef4444 50%, transparent);
}
.runner-tab:last-child {
  border-right: none;
}
.runner-tab.active {
  color: var(--ink);
  background: color-mix(in srgb, var(--accent) 18%, transparent);
}
.runner-tab.has-unread::after {
  content: "";
  position: absolute;
  top: 5px;
  right: 6px;
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: var(--accent);
}
.runner-terminal-shell {
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #0a1018;
  position: relative;
  min-height: 420px;
  height: 560px;
  resize: vertical;
  overflow: hidden;
  padding: 4px;
  box-sizing: border-box;
  display: flex;
}
.runner-terminal-shell.read-only {
  border-color: color-mix(in srgb, #f59e0b 45%, var(--line) 55%);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, #f59e0b 55%, transparent);
}
.runner-terminal-shell.read-only::after {
  content: "READ-ONLY VIEW";
  position: absolute;
  top: 10px;
  right: 12px;
  font-size: 11px;
  letter-spacing: 0.06em;
  color: color-mix(in srgb, #f59e0b 75%, white 25%);
  background: color-mix(in srgb, #0a1018 70%, black 30%);
  border: 1px solid color-mix(in srgb, #f59e0b 55%, #0a1018 45%);
  border-radius: 999px;
  padding: 2px 8px;
  pointer-events: none;
  z-index: 3;
}
.runner-terminal-shell > #runner-terminal {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  width: 100%;
  height: 100%;
}
.runner-terminal-shell .xterm,
.runner-terminal-shell .xterm-viewport,
.runner-terminal-shell .xterm-screen {
  width: 100% !important;
  height: 100% !important;
}

body.terminal-popout-mode {
  background: #0a1018;
}
body.app-shell.terminal-popout-mode {
  grid-template-columns: 1fr;
}
body.terminal-popout-mode .sidebar,
body.terminal-popout-mode .appbar,
body.terminal-popout-mode .theme-switcher,
body.terminal-popout-mode .runner-info-card,
body.terminal-popout-mode section.card:not(.terminal-card-popout),
body.terminal-popout-mode .terminal-card-popout > .row {
  display: none !important;
}
body.terminal-popout-mode .app-main {
  grid-column: 1 / -1;
  margin-left: 0;
  width: 100%;
  min-width: 0;
}
body.terminal-popout-mode main {
  max-width: none;
  width: 100%;
  padding: 0;
}
body.terminal-popout-mode .terminal-card-popout {
  margin: 0;
  padding: 0;
  border: none;
  border-radius: 0;
  box-shadow: none;
  background: #0a1018;
}
body.terminal-popout-mode .runner-detail-grid,
body.terminal-popout-mode .runner-terminal-wrap {
  display: flex;
  flex-direction: column;
  gap: 0;
}
body.terminal-popout-mode .runner-terminal-toolbar {
  display: flex;
  padding: 8px 10px;
  border-bottom: 1px solid var(--line);
  background: #0a1018;
}
body.terminal-popout-mode .runner-terminal-shell {
  border: none;
  border-radius: 0;
  padding: 0;
  min-height: 0;
  height: calc(100vh - 52px);
  flex: 1 1 auto;
  resize: none;
}

@media (max-width: 980px) {
  .runner-detail-grid {
    grid-template-columns: 1fr;
  }
}
.theme-switcher {
  position: fixed;
  right: 14px;
  bottom: 14px;
  z-index: 1000;
  display: inline-flex;
  gap: 4px;
  border: 1px solid var(--line);
  background: var(--card);
  border-radius: 12px;
  padding: 4px;
  box-shadow: 0 6px 16px var(--shadow);
}
.theme-btn {
  border: 1px solid transparent;
  background: transparent;
  color: var(--muted);
  border-radius: 8px;
  padding: 6px 8px;
  font-size: 11px;
  text-transform: lowercase;
  cursor: pointer;
}
.theme-btn.active {
  border-color: var(--btn-border);
  background: var(--btn-bg);
  color: var(--btn-ink);
}
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000;
  padding: 12px;
}
.modal-backdrop[hidden] { display: none !important; }
.modal-card {
  width: min(480px, 100%);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 12px 28px var(--shadow);
  padding: 14px;
}
.modal-card h3 {
  margin-top: 0;
  margin-bottom: 8px;
}
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 14px;
}

/* Flatpickr theming */
.flatpickr-calendar {
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: 0 10px 24px var(--shadow);
  color: var(--ink);
}
.flatpickr-months .flatpickr-month,
.flatpickr-current-month .flatpickr-monthDropdown-months,
.flatpickr-current-month input.cur-year {
  color: var(--ink);
}
.flatpickr-weekday {
  color: var(--muted);
}
.flatpickr-day {
  color: var(--ink);
  border-radius: 8px;
}
.flatpickr-day:hover {
  background: color-mix(in srgb, var(--accent) 20%, transparent);
  border-color: color-mix(in srgb, var(--accent) 35%, transparent);
}
.flatpickr-day.today {
  border-color: var(--accent);
}
.flatpickr-day.selected,
.flatpickr-day.startRange,
.flatpickr-day.endRange,
.flatpickr-day.selected:hover,
.flatpickr-day.startRange:hover,
.flatpickr-day.endRange:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: #00120f;
}
.flatpickr-time input,
.flatpickr-time .flatpickr-time-separator {
  color: var(--ink);
}
.flatpickr-time input:hover,
.flatpickr-time input:focus {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
}

html[data-theme="h4xor"] .flatpickr-calendar {
  background: #000000;
  border: 1px solid #0000cc;
  color: #00ffff;
  box-shadow: 0 0 10px rgba(255, 0, 153, 0.38), 0 0 22px rgba(0, 255, 255, 0.24), 0 8px 20px rgba(0, 0, 0, 0.8);
}
html[data-theme="h4xor"] .flatpickr-months .flatpickr-month,
html[data-theme="h4xor"] .flatpickr-current-month .flatpickr-monthDropdown-months,
html[data-theme="h4xor"] .flatpickr-current-month input.cur-year,
html[data-theme="h4xor"] .flatpickr-weekday,
html[data-theme="h4xor"] .flatpickr-time input,
html[data-theme="h4xor"] .flatpickr-time .flatpickr-time-separator {
  color: #00ffff;
}
html[data-theme="h4xor"] .flatpickr-day {
  color: #00ffff;
}
html[data-theme="h4xor"] .flatpickr-day:hover {
  background: rgba(255, 0, 153, 0.24);
  border-color: #ff0099;
}
html[data-theme="h4xor"] .flatpickr-day.today {
  border-color: #ccff00;
}
html[data-theme="h4xor"] .flatpickr-day.selected,
html[data-theme="h4xor"] .flatpickr-day.startRange,
html[data-theme="h4xor"] .flatpickr-day.endRange,
html[data-theme="h4xor"] .flatpickr-day.selected:hover,
html[data-theme="h4xor"] .flatpickr-day.startRange:hover,
html[data-theme="h4xor"] .flatpickr-day.endRange:hover {
  background: #ff0099;
  border-color: #ff0099;
  color: #00ffff;
  text-shadow: -1px 0 rgba(255, 0, 153, 0.5), 1px 0 rgba(0, 255, 255, 0.4), 0 0 6px rgba(0, 255, 255, 0.3);
}
html[data-theme="h4xor"] .flatpickr-time input:hover,
html[data-theme="h4xor"] .flatpickr-time input:focus {
  background: rgba(0, 0, 204, 0.26);
}

html[data-theme="h4xor"] body {
  background:
    radial-gradient(circle at 8% 0%, rgba(255, 0, 153, 0.33) 0, transparent 28%),
    radial-gradient(circle at 91% 10%, rgba(0, 255, 255, 0.28) 0, transparent 30%),
    linear-gradient(180deg, #000011 0%, #000000 52%, #020235 100%);
  font-family: "Press Start 2P", "VT323", "IBM Plex Sans", monospace;
  text-rendering: geometricPrecision;
  animation: h4xor-crt-flicker 0.18s infinite;
}

html[data-theme="h4xor"] body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background: repeating-linear-gradient(
    to bottom,
    rgba(255, 255, 255, 0.06) 0px,
    rgba(255, 255, 255, 0.06) 1px,
    transparent 2px,
    transparent 4px
  );
  mix-blend-mode: screen;
  animation: h4xor-scanlines 4s linear infinite;
}

html[data-theme="h4xor"] body::after {
  content:
    ">> ACCESS GRANTED <<\A"
    "01001101 01001111 01000100 01000101 01001101 5A\A"
    "[SKL] [DISK] [MONITOR] [MODEM] [NET]\A"
    "root@megacorp:/vault # ./dump_all.sh\A"
    ":::: DATA STREAM ACTIVE ::::\A"
    "0x1337 0xC0DE 0xBEEF 0xF00D\A"
    "+++ TRACKING PACKETS +++\A"
    "LOGIN OVERRIDE ACCEPTED\A"
    "BREACH VECTOR: SATELLITE UPLINK\A"
    "R00T R00T R00T R00T R00T\A"
    ">> ACCESS GRANTED <<\A";
  position: fixed;
  left: 0;
  right: 0;
  top: 100%;
  pointer-events: none;
  z-index: 0;
  color: rgba(204, 255, 0, 0.2);
  font-family: "Courier New", monospace;
  font-size: 12px;
  line-height: 1.45;
  letter-spacing: 1px;
  white-space: pre;
  padding: 20px 8px;
  mix-blend-mode: screen;
  text-shadow: 0 0 8px rgba(204, 255, 0, 0.34), 0 0 14px rgba(0, 255, 255, 0.2);
  animation: h4xor-ascii-scroll 18s linear infinite;
}

html[data-theme="h4xor"] main,
html[data-theme="h4xor"] .topbar {
  position: relative;
  z-index: 1;
}

/* NB: no h4xor override of .theme-switcher positioning here. The switcher now lives inline in the
   sidebar footer (.theme-switcher.inline-themes, position:static). A leftover fixed/bottom-right rule
   used to win on specificity in this theme and fling the switcher into the page corner. */

html[data-theme="h4xor"] .brand,
html[data-theme="h4xor"] h1,
html[data-theme="h4xor"] h2,
html[data-theme="h4xor"] h3 {
  font-family: "Press Start 2P", "VT323", "IBM Plex Sans", monospace;
  text-transform: uppercase;
  letter-spacing: 1px;
  text-shadow: 0 0 8px rgba(0, 255, 255, 0.5), 0 0 18px rgba(255, 0, 153, 0.35);
  animation: h4xor-glitch 2s steps(1) infinite, h4xor-headroom-stutter 3.2s steps(1) infinite;
}

html[data-theme="h4xor"] .brand::before {
  content: "[MONITOR]";
  display: inline-block;
  margin-right: 8px;
  color: #ff6600;
  animation: h4xor-icon-spin 3s linear infinite;
}

html[data-theme="h4xor"] .topbar {
  position: sticky;
  overflow: hidden;
}

html[data-theme="h4xor"] .topbar::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    repeating-linear-gradient(
      to bottom,
      rgba(255, 255, 255, 0) 0px,
      rgba(255, 255, 255, 0) 14px,
      rgba(0, 255, 255, 0.16) 15px,
      rgba(255, 0, 153, 0.12) 16px,
      rgba(255, 255, 255, 0) 17px
    );
  mix-blend-mode: screen;
  animation: h4xor-headroom-bars 1.15s linear infinite;
}

html[data-theme="h4xor"] .card {
  position: relative;
  border-color: #0000cc;
  background:
    linear-gradient(180deg, rgba(0, 0, 204, 0.08) 0%, rgba(0, 0, 0, 0.8) 100%),
    repeating-linear-gradient(90deg, rgba(255, 0, 153, 0.08) 0, rgba(255, 0, 153, 0.08) 1px, transparent 1px, transparent 8px),
    var(--card);
  box-shadow:
    0 0 0 1px rgba(0, 0, 204, 0.45),
    0 0 10px rgba(255, 0, 153, 0.28),
    0 0 24px rgba(0, 255, 255, 0.2),
    0 8px 24px var(--shadow);
  animation: h4xor-neon-border 1.6s ease-in-out infinite alternate;
}

html[data-theme="h4xor"] .card .row h2::before {
  content: "[h4x0r]";
  display: inline-block;
  margin-right: 8px;
  color: #ccff00;
  transform-origin: center;
  animation: h4xor-icon-spin 2.8s linear infinite;
}

html[data-theme="h4xor"] .btn {
  font-family: "Press Start 2P", "VT323", "IBM Plex Sans", monospace;
  text-transform: uppercase;
  letter-spacing: 1px;
  border-width: 2px;
  box-shadow:
    inset 0 0 0 1px rgba(0, 255, 255, 0.2),
    0 0 8px rgba(255, 0, 153, 0.45),
    0 0 18px rgba(0, 255, 255, 0.24);
  position: relative;
  overflow: hidden;
  animation: h4xor-button-pulse 1.2s steps(2) infinite;
}

html[data-theme="h4xor"] .btn::before {
  content: "";
  position: absolute;
  top: 0;
  left: -130%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 102, 0, 0.55), transparent);
  animation: h4xor-button-scan 1s linear infinite;
}

html[data-theme="h4xor"] .brand,
html[data-theme="h4xor"] .btn,
html[data-theme="h4xor"] .status,
html[data-theme="h4xor"] .pill {
  text-shadow:
    -1px 0 rgba(255, 0, 153, 0.55),
    1px 0 rgba(0, 255, 255, 0.5),
    0 0 8px rgba(0, 255, 255, 0.25);
}

html[data-theme="h4xor"] .console,
html[data-theme="h4xor"] .mini {
  border-color: #0000cc;
  background: #000000;
  color: #00ffff;
  text-shadow: 0 0 6px rgba(0, 255, 255, 0.35);
}
html[data-theme="h4xor"] .tag-input-shell {
  border-color: #0000cc;
  box-shadow: inset 0 0 8px rgba(0, 255, 255, 0.12);
}
html[data-theme="h4xor"] .tag-chip {
  border-color: #ff0099;
  background: rgba(255, 0, 153, 0.2);
  color: #00ffff;
  text-shadow: -1px 0 rgba(255, 0, 153, 0.45), 1px 0 rgba(0, 255, 255, 0.35);
}

html[data-theme="h4xor"] .status-running { border-color: #00ffff; background: rgba(0, 255, 255, 0.2); color: #00ffff; }
html[data-theme="h4xor"] .status-completed { border-color: #ccff00; background: rgba(204, 255, 0, 0.18); color: #ccff00; }
html[data-theme="h4xor"] .status-failed { border-color: #ff0099; background: rgba(255, 0, 153, 0.2); color: #ff0099; }
html[data-theme="h4xor"] .status-canceled { border-color: #ff6600; background: rgba(255, 102, 0, 0.2); color: #ff6600; }
html[data-theme="h4xor"] .status-error { border-color: #ffb000; background: rgba(255, 176, 0, 0.2); color: #ffb000; }
html[data-theme="h4xor"] .status-stopping { border-color: #ff6600; background: rgba(255, 102, 0, 0.2); color: #ff6600; }
html[data-theme="h4xor"] .status-queued,
html[data-theme="h4xor"] .status-scheduled,
html[data-theme="h4xor"] .status-stopped {
  border-color: #0000cc;
  background: rgba(0, 0, 204, 0.25);
  color: #00ffff;
}
html[data-theme="h4xor"] .status { text-transform: uppercase; }

@keyframes h4xor-scanlines {
  0% { transform: translateY(0); }
  100% { transform: translateY(8px); }
}

@keyframes h4xor-crt-flicker {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.95; }
  76% { opacity: 0.9; }
}

@keyframes h4xor-glitch {
  0%, 82%, 100% { transform: translateX(0); color: var(--ink); }
  83% { transform: translateX(-2px); color: #ff0099; }
  86% { transform: translateX(2px); color: #00ffff; }
  88% { transform: translateX(-1px); color: #ccff00; }
  91% { transform: translateX(1px); color: #ff6600; }
}

@keyframes h4xor-neon-border {
  0% {
    box-shadow:
      0 0 0 1px rgba(0, 0, 204, 0.42),
      0 0 10px rgba(255, 0, 153, 0.28),
      0 0 20px rgba(0, 255, 255, 0.18),
      0 8px 24px var(--shadow);
  }
  100% {
    box-shadow:
      0 0 0 1px rgba(0, 0, 204, 0.85),
      0 0 20px rgba(255, 0, 153, 0.42),
      0 0 36px rgba(0, 255, 255, 0.3),
      0 8px 24px var(--shadow);
  }
}

@keyframes h4xor-button-pulse {
  0% {
    box-shadow:
      inset 0 0 0 1px rgba(0, 255, 255, 0.22),
      0 0 10px rgba(255, 0, 153, 0.4),
      0 0 22px rgba(0, 255, 255, 0.22);
  }
  100% {
    box-shadow:
      inset 0 0 0 1px rgba(204, 255, 0, 0.3),
      0 0 18px rgba(255, 102, 0, 0.45),
      0 0 28px rgba(204, 255, 0, 0.32);
  }
}

@keyframes h4xor-button-scan {
  0% { left: -130%; }
  100% { left: 130%; }
}

@keyframes h4xor-icon-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@keyframes h4xor-ascii-scroll {
  from { transform: translateY(0); }
  to { transform: translateY(-240%); }
}

@keyframes h4xor-headroom-bars {
  0% { transform: translateY(-8px); opacity: 0.55; }
  100% { transform: translateY(8px); opacity: 0.9; }
}

@keyframes h4xor-headroom-stutter {
  0%, 72%, 100% { transform: translate(0, 0) skewX(0deg); }
  73% { transform: translate(-2px, 0) skewX(-6deg); }
  75% { transform: translate(2px, 0) skewX(7deg); }
  77% { transform: translate(-1px, 1px) skewX(-3deg); }
  79% { transform: translate(1px, -1px) skewX(2deg); }
}

/* ============================================================
   Nessus-style components: KPI tiles, tabs, panels, port pills
   ============================================================ */

/* Card refinements */
.card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line);
}
.card-head .card-title { flex: 1; margin: 0; font-size: 16px; font-weight: 600; }
.card-title { margin: 0; font-size: 16px; font-weight: 600; }
.panel-card { padding: 14px; }
.panel-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line);
}
.panel-title { font-size: 15px; font-weight: 600; flex: 1; }
.panel-meta { display: flex; gap: 10px; align-items: center; }

/* KPI tiles */
.kpi-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 16px;
}
.kpi {
  background: var(--card);
  border: 1px solid var(--line);
  border-left: 3px solid var(--muted);
  border-radius: 10px;
  padding: 12px 14px;
  display: flex;
  align-items: center;
  gap: 12px;
  box-shadow: 0 1px 2px var(--shadow);
}
.kpi-icon {
  width: 36px;
  height: 36px;
  border-radius: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  background: color-mix(in srgb, var(--muted) 16%, transparent);
  color: var(--muted);
}
.kpi-body { display: flex; flex-direction: column; min-width: 0; }
.kpi-value { font-size: 24px; font-weight: 700; line-height: 1.1; }
.kpi-value .kpi-sub { font-size: 14px; color: var(--muted); font-weight: 500; }
.kpi-label { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
.kpi-running   { border-left-color: var(--kpi-running); }
.kpi-running   .kpi-icon { color: var(--kpi-running);   background: color-mix(in srgb, var(--kpi-running) 14%, transparent); }
.kpi-queued    { border-left-color: var(--kpi-queued); }
.kpi-queued    .kpi-icon { color: var(--kpi-queued);    background: color-mix(in srgb, var(--kpi-queued) 14%, transparent); }
.kpi-completed { border-left-color: var(--kpi-completed); }
.kpi-completed .kpi-icon { color: var(--kpi-completed); background: color-mix(in srgb, var(--kpi-completed) 14%, transparent); }
.kpi-failed    { border-left-color: var(--kpi-failed); }
.kpi-failed    .kpi-icon { color: var(--kpi-failed);    background: color-mix(in srgb, var(--kpi-failed) 14%, transparent); }
.kpi-runners   { border-left-color: var(--kpi-runners); }
.kpi-runners   .kpi-icon { color: var(--kpi-runners);   background: color-mix(in srgb, var(--kpi-runners) 14%, transparent); }

/* Tabs */
.tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 14px;
  overflow-x: auto;
}
.tab {
  border: none;
  background: transparent;
  color: var(--muted);
  padding: 10px 16px;
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  border-bottom: 2px solid transparent;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  position: relative;
  white-space: nowrap;
}
.tab:hover { color: var(--ink); }
.tab.active {
  color: var(--ink);
  border-bottom-color: var(--accent);
}
.tab-count {
  font-size: 11px;
  background: color-mix(in srgb, var(--muted) 18%, transparent);
  color: var(--muted);
  border-radius: 999px;
  padding: 1px 8px;
  font-weight: 600;
}
.tab.active .tab-count { background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--accent); }
.tab-dot { width: 7px; height: 7px; border-radius: 999px; background: var(--kpi-running); display: inline-block; animation: pulse 1.4s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }

.tab-panel { display: none; }
.tab-panel.active { display: block; }

/* Job strip (sticky-feeling status bar above tabs) */
.job-strip { padding: 10px 14px; margin-bottom: 12px; }
.job-strip-status { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
.kv { display: inline-flex; flex-direction: column; gap: 1px; font-size: 13px; }
.kv-k { font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--muted); }
.kv-v { color: var(--ink); font-weight: 500; }

/* Console panel */
.console-panel .cmdline-label { font-size: 12px; margin-bottom: 4px; }
.console-panel .cmdline-box { max-height: 110px; overflow: auto; margin: 0 0 10px 0; font-size: 12px; }
.job-console { height: 560px; min-height: 360px; }

/* Files panel is the shared File Browser widget (#1313) — its styling lives in static/file-browser.css. */
.input-sm { padding: 5px 8px; font-size: 12px; width: 220px; }

/* Reports list */
.reports-list .report-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 6px;
}
.report-link {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
}
.report-icon { font-size: 18px; }
.report-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.report-size { font-size: 12px; }

/* Summary panel */
.summary-grid {
  display: grid;
  grid-template-columns: 1.2fr 1fr 1fr;
  gap: 14px;
}
.summary-block h4 {
  margin: 0 0 8px 0;
  font-size: 12px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-weight: 600;
}

/* Details panel */
.detail-list {
  display: grid;
  grid-template-columns: 180px 1fr;
  gap: 6px 14px;
  margin: 0;
}
.detail-list dt { color: var(--muted); font-size: 12px; padding-top: 4px; }
.detail-list dd { margin: 0; padding-top: 4px; }
/* Cap long multi-entry lists (e.g. Targets) so the panel doesn't grow unbounded. */
.detail-list .targets-list { max-height: 200px; overflow-y: auto; }
.detail-list .targets-list:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Port state pills (used in services table & summary) */
.port-state {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  padding: 1px 6px;
  border-radius: 4px;
  letter-spacing: 0.5px;
}
.port-open     { color: var(--sev-ok);       background: color-mix(in srgb, var(--sev-ok) 18%, transparent); }
.port-closed   { color: var(--muted);        background: color-mix(in srgb, var(--muted) 14%, transparent); }
.port-filtered { color: var(--sev-medium);   background: color-mix(in srgb, var(--sev-medium) 16%, transparent); }

/* Service fingerprint text (product version [extrainfo]) */
.svc-fingerprint {
  font-family: var(--mono-font, monospace);
  font-size: 12px;
  color: var(--text-secondary, var(--muted));
  margin-right: 4px;
}

/* Technology stack chips in the Additional info column */
.tech-chip {
  display: inline-block;
  font-size: 10px;
  font-weight: 600;
  padding: 1px 6px;
  border-radius: 4px;
  margin: 0 2px 2px 0;
  color: var(--accent, #4e9ae0);
  background: color-mix(in srgb, var(--accent, #4e9ae0) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent, #4e9ae0) 30%, transparent);
}

/* Data tables — denser, hover highlight */
.data-table {
  font-size: 13px;
}
.data-table thead th {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--muted);
  background: color-mix(in srgb, var(--card) 88%, black 12%);
  padding: 8px 10px;
}
.data-table tbody td { padding: 8px 10px; }
.data-table th.sortable { cursor: pointer; user-select: none; }
.data-table th.sortable:hover { color: var(--ink); }
.data-table th .sort-ind { font-size: 10px; }
/* Reusable data-table toolbar + pager (static/data-table.js). */
.dt-toolbar { display: flex; gap: 10px; align-items: center; margin-bottom: 12px; flex-wrap: wrap; }
.dt-toolbar .dt-filter { flex: 1; min-width: 220px; }
.dt-pager { display: flex; gap: 10px; align-items: center; justify-content: flex-end; margin-top: 10px; }
.dt-pager .dt-info { font-size: 12px; }
.row-link { font-weight: 500; }

/* Runner mini detail strip on cards */
.runner-mini {
  display: grid;
  grid-template-columns: 50px 1fr;
  gap: 3px 10px;
  margin: 8px 0 0 0;
  font-size: 12px;
}
.runner-mini dt { color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; font-size: 10px; padding-top: 2px; }
.runner-mini dd { margin: 0; padding-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Banner / notice */
.banner {
  background: color-mix(in srgb, var(--accent) 12%, var(--card));
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  border-left: 3px solid var(--accent);
  color: var(--ink);
  padding: 10px 14px;
  border-radius: 8px;
  margin-bottom: 12px;
  font-size: 13px;
}
.banner-warn {
  background: color-mix(in srgb, var(--danger) 12%, var(--card));
  border-color: color-mix(in srgb, var(--danger) 40%, transparent);
  border-left-color: var(--danger);
}

/* Progress list — tighten spacing */
.progress-name { font-weight: 500; }
.progress-stats { font-size: 12px; }
.progress-pct { font-weight: 600; color: var(--ink); }

/* Dashboard collapsible */
.dash-collapse > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 0;
}
.dash-collapse > summary::-webkit-details-marker { display: none; }
.dash-collapse .chevron { color: var(--muted); width: 14px; display: inline-block; }
.dash-collapse-body { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); }

/* Inline theme switcher in sidebar */
.theme-switcher.inline-themes {
  position: static;
  display: flex;
  gap: 2px;
  border: 1px solid var(--line);
  background: color-mix(in srgb, var(--card) 88%, transparent);
  border-radius: 8px;
  padding: 3px;
  box-shadow: none;
}
.theme-switcher.inline-themes .theme-btn {
  font-size: 10px;
  padding: 4px 8px;
  flex: 1;
}
@media (max-width: 1100px) {
  .threecol { grid-template-columns: 1fr; }
  .twocol { grid-template-columns: 1fr; }
  .meta-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* minmax(0, …) like the desktop rule: bare `1fr` is minmax(auto, 1fr), so the one column grew to its
     widest child's min-content and the New Scan form overflowed a 390px phone by 21px. */
  .form-grid { grid-template-columns: minmax(0, 1fr); }
  .stats-grid { grid-template-columns: 1fr 1fr; }
  .kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .filter-grid { grid-template-columns: 1fr 1fr; }
  .table { font-size: 13px; }
  .summary-grid { grid-template-columns: 1fr; }
  .detail-list { grid-template-columns: 1fr; }
  .detail-list dt { padding-top: 8px; }
}
@media (max-width: 860px) {
  body.app-shell { grid-template-columns: 1fr; }
  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 232px;
    transform: translateX(-100%);
    transition: transform 180ms ease;
  }
  .sidebar.open { transform: translateX(0); }
  /* Scrim behind the open mobile sidebar: a tap on it closes the panel (#1178). The open fixed
     sidebar covers the hamburger, so this (plus Escape) is the reachable close path. Sits above the
     appbar (z 40) / content but below the sidebar (z 50), so the panel stays on top and tappable. */
  .sidebar.open ~ .sidebar-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 45;
    background: rgba(0, 0, 0, 0.4);
  }
  /* Hamburger: min 44×44 CSS px touch target per WCAG 2.5.5 */
  .sidebar-toggle { display: inline-flex; min-width: 44px; min-height: 44px; justify-content: center; align-items: center; }
  /* The fixed sidebar is viewport-tall; on a short phone viewport (or with many extensions) the
     nav overflows it and the footer (logout / theme) is unreachable. Scroll the panel instead.
     overflow-x:hidden because overflow-y:auto alone computes overflow-x to auto too, and a long
     nav label would then grow a horizontal scrollbar. */
  .sidebar { overflow-y: auto; overflow-x: hidden; }
  /* 44 px tap-target floor on every nav link (sidebar is 232 px wide, so width is fine). */
  .sidebar-nav a { min-height: 44px; }
  /* Version string is not needed on mobile — remove it to keep the footer compact. */
  .sidebar-version { display: none; }
  .kpi-grid { grid-template-columns: 1fr 1fr; }
  .data-table { display: block; overflow-x: auto; white-space: nowrap; }
  /* Reclaim horizontal space — 20px→12px sides saves 16px on a 390px screen (~4% content width). */
  main { padding: 12px 12px 24px; }
  .appbar { padding: 8px 12px; }
  /* Filter dropdowns are too cramped side-by-side on sub-860 viewports; stack them. */
  .filter-grid { grid-template-columns: 1fr; }
  .filter-grid-3col { grid-template-columns: 1fr; }
}

/* ── Scan composer (run-type pipeline builder) ───────────────────────────── */
.sc-toolbar { display:flex; gap:8px; align-items:flex-end; margin:6px 0 12px; flex-wrap:wrap; }
.sc-toolbar label { flex:1; min-width:220px; }
.sc-fold-ctrls { display:flex; gap:6px; align-items:flex-end; }
.sc-steps { display:flex; flex-direction:column; gap:10px; }
.sc-step { border:1px solid var(--line-strong); border-radius:10px;
           background: color-mix(in srgb, var(--card) 92%, black 8%); padding:0; overflow:hidden;
           transition: border-color .12s ease, box-shadow .12s ease;
           /* Keep natural height inside the flex-column, max-height:34rem step list. Without this, a
              flex item with overflow:hidden gets min-height:auto=0 and shrinks to fit the capped
              container — clipping its param grid, which then spills over the cards below (the
              multi-module "clobber"). flex-shrink:0 makes cards keep full height and the list scroll. */
           flex-shrink:0; }
.sc-step:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--line-strong));
                 box-shadow: 0 2px 10px var(--shadow); }
/* Expanded cards get an accent stripe + a header band divider so the params read as a distinct zone;
   collapsed cards stay a compact single row. */
.sc-step.sc-expanded { border-color: color-mix(in srgb, var(--accent) 38%, var(--line-strong)); }
.sc-step-head { display:flex; align-items:center; gap:10px; flex-wrap:wrap; padding:9px 11px; }
.sc-step.sc-expanded .sc-step-head { background: color-mix(in srgb, var(--accent) 7%, var(--card));
           border-bottom:1px solid var(--line); }
/* Collapse chevron — a borderless icon toggle, sized to line up with the drag handle. */
.sc-collapse { background:none; border:0; color:var(--muted); cursor:pointer; font-size:12px; line-height:1;
               padding:2px 4px; flex:0 0 auto; border-radius:4px; }
.sc-collapse:hover { color:var(--ink); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.sc-step-title { font-weight:600; flex:1; cursor:pointer; user-select:none; }
.sc-modified { color:var(--accent); font-size:9px; vertical-align:middle; margin-left:6px; }
.sc-step-cat { font-size:10px; text-transform:uppercase; letter-spacing:.5px; color:var(--muted);
               border:1px solid color-mix(in srgb, var(--muted) 35%, transparent); border-radius:999px; padding:1px 7px; }
.sc-step-modes { display:flex; gap:12px; font-size:12px; color:var(--muted); }
.sc-step-modes label { display:inline-flex; gap:5px; align-items:center; margin:0; }
.sc-step-ctrls { display:flex; gap:4px; }
.sc-step-params { margin:0; padding:11px; grid-template-columns: repeat(3, minmax(0,1fr)); gap:10px; }
/* Two-tier params: the advanced grid sits below the "More options" toggle, set off by a divider and
   a touch of shade so it reads as a secondary zone, not more of the essentials. */
.sc-step-advanced { padding-top:2px; }
.sc-more { margin:2px 11px 6px; color:var(--muted); }
.sc-more:hover { color:var(--ink); }
/* Extra args: a full-width quick-type box pinned at the card bottom, always visible when expanded. */
.sc-extra-args { display:block; padding:2px 11px 11px; border-top:1px solid var(--line); margin-top:4px; }
.sc-extra-args .input { width:100%; margin-top:4px; }
/* Collapsed-card summary: chips for only the params the operator changed from default. */
.sc-step-summary { display:flex; flex-wrap:wrap; gap:6px; padding:2px 11px 10px; }
.sc-step-note { padding:0 11px 11px; }
/* Reorder controls sized as comfortable touch targets (drag is desktop-only). */
.sc-step-ctrls .btn { min-width:30px; padding:5px 8px; line-height:1; }
.sc-step-ctrls .btn[disabled] { opacity:.35; cursor:not-allowed; }
.sc-chip { font-size:11px; color:var(--muted); border:1px solid color-mix(in srgb, var(--muted) 30%, transparent);
           border-radius:999px; padding:1px 8px; background: color-mix(in srgb, var(--card) 80%, black 20%); }
.sc-empty { color:var(--muted); font-style:italic; padding:8px 2px; }
/* Drag-to-reorder affordance (complements the ▲/▼ buttons, which remain for touch/keyboard). */
.sc-drag { cursor:grab; user-select:none; color:var(--muted); font-size:16px; line-height:1;
           padding:0 2px; flex:0 0 auto; }
.sc-drag:active { cursor:grabbing; }
.sc-step.sc-dragging { opacity:.45; }
.sc-step.sc-drop-before { box-shadow: inset 0 3px 0 0 var(--accent); }
.sc-step.sc-drop-after { box-shadow: inset 0 -3px 0 0 var(--accent); }
/* A step whose module is disabled/deleted: flag it (a run type can't be saved/run referencing one). */
.sc-step.sc-step-disabled, .sc-step.sc-step-unknown {
  border-color: color-mix(in srgb, var(--danger) 55%, var(--line) 45%);
  background: color-mix(in srgb, var(--danger) 8%, transparent); }
.sc-step-note { color: color-mix(in srgb, var(--danger) 70%, var(--ink) 30%); margin-top:8px; }

/* ── Job page: per-module status chips + module-tagged live feed ──────────── */
/* Pipeline flow graph (job-flow.js): module chips are node cards in stage columns; SVG edges between
   them march while a module runs — the vector extension's `vap-flow` dash idiom. Palette is the
   operator's ask: green done, gold running, red failed, blue other-terminal, dim not started.
   (--kpi-running is BLUE in the theme, so running deliberately maps to the gold --kpi-queued.) */
.job-flow, .flow-mini { --flow-ok:var(--kpi-completed); --flow-run:var(--kpi-queued); --flow-bad:var(--kpi-failed);
            --flow-other:var(--kpi-running); --flow-lost:#a855f7; --flow-warn:#f97316;
            --flow-idle:color-mix(in srgb, var(--muted) 45%, transparent); }
html[data-theme="light"] .job-flow, html[data-theme="light"] .flow-mini { --flow-lost:#7e22ce; --flow-warn:#c2410c; }
.job-flow { overflow-x:auto; overflow-y:hidden; margin:0 0 12px; padding:12px 14px; }
.job-flow-canvas { position:relative; display:inline-flex; flex-direction:column; gap:14px; min-width:100%; }
.job-flow-edges { position:absolute; inset:0; pointer-events:none; overflow:visible; }
.job-flow-main { display:flex; align-items:center; gap:56px; }
.job-flow-col { display:flex; flex-direction:column; gap:10px; }
.job-flow-bg { display:flex; align-items:center; gap:10px; margin-left:88px; }
.job-flow-bg[hidden] { display:none; }
.job-flow-bg .flow-node-state::after { content:" · background"; }
.job-flow [data-flow="ok"], .flow-edge[data-state="ok"]    { --flow-c:var(--flow-ok); }
.job-flow [data-flow="run"], .flow-edge[data-state="run"]   { --flow-c:var(--flow-run); }
.job-flow [data-flow="bad"], .flow-edge[data-state="bad"]   { --flow-c:var(--flow-bad); }
.job-flow [data-flow="other"], .flow-edge[data-state="other"] { --flow-c:var(--flow-other); }
.job-flow [data-flow="idle"], .flow-edge[data-state="idle"]  { --flow-c:var(--flow-idle); }
.job-flow [data-flow="lost"], .flow-edge[data-state="lost"]  { --flow-c:var(--flow-lost); }
.job-flow [data-flow="warn"] { --flow-c:var(--flow-warn); }  /* end node only: finished, but with issues */
.module-chip { position:relative; display:flex; flex-direction:column; gap:2px; min-width:128px; padding:7px 26px 7px 10px;
               border-radius:6px; border:1px solid var(--flow-c, var(--line)); border-left-width:3px;
               background:color-mix(in srgb, var(--flow-c, var(--muted)) 10%, var(--card)); cursor:default; }
.flow-node-label { font:600 12px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace; color:var(--ink); }
.flow-node-state { font-size:10px; letter-spacing:.06em; text-transform:uppercase;
                   color:color-mix(in srgb, var(--flow-c, var(--muted)), var(--ink) 30%); }
.module-chip[data-flow="idle"] .flow-node-label { color:var(--muted); }
.module-chip[data-flow="run"] { animation:flow-pulse 1.6s ease-in-out infinite; }
/* completed but wrote no evidence: done, yet assessed nothing — outline only, never a solid "clean" */
.module-chip[data-hollow="1"] { background:transparent; border-style:dashed; border-left-style:solid; }
.flow-node-time { font:10px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace; color:var(--muted); font-variant-numeric:tabular-nums; }
.flow-node-time:empty { display:none; }
.flow-terminal { min-width:44px; height:26px; padding:0 10px; border-radius:999px; display:grid; place-items:center; flex:none;
                 border:2px solid var(--flow-c); background:color-mix(in srgb, var(--flow-c) 12%, var(--card)); }
.flow-terminal .flow-node-state { font-size:9px; }
.flow-edge { fill:none; stroke:var(--flow-c); stroke-width:2; }
.flow-edge[data-state="idle"] { stroke-dasharray:3 5; }
.flow-edge[data-state="run"] { stroke-width:2.5; stroke-dasharray:8 4; animation:flow-march .8s linear infinite;
                               filter:drop-shadow(0 0 3px var(--flow-run)); }
.flow-edge[data-state="ok"] { opacity:.85; }
.flow-edge-bg { stroke-dasharray:2 6; }
.flow-edge-bg[data-state="run"] { stroke-dasharray:8 4; }
@keyframes flow-march { to { stroke-dashoffset:-24; } }
.flow-edge[data-state="lost"] { stroke-dasharray:2 4; }
.flow-edge-drawin { stroke-dasharray:1000; stroke-dashoffset:1000; animation:flow-drawin .7s ease-out forwards; }
@keyframes flow-drawin { to { stroke-dashoffset:0; } }
@keyframes flow-pulse { 0%, 100% { box-shadow:0 0 4px 0 color-mix(in srgb, var(--flow-run) 30%, transparent); }
                        50% { box-shadow:0 0 14px 3px color-mix(in srgb, var(--flow-run) 55%, transparent); } }
/* scans-list mini graph: one dot per module, same state palette */
.flow-mini { display:inline-block; vertical-align:middle; line-height:0; }
.flow-mini-edge { stroke:var(--flow-c, var(--flow-idle)); stroke-width:1.2; }
.flow-mini-dot { fill:var(--flow-c); stroke:var(--flow-c); stroke-width:1.2; }
.flow-mini-dot[data-hollow="1"] { fill:transparent; }
.flow-mini [data-flow="ok"], .flow-mini [data-state="ok"] { --flow-c:var(--flow-ok); }
.flow-mini [data-flow="run"], .flow-mini [data-state="run"] { --flow-c:var(--flow-run); }
.flow-mini [data-flow="bad"], .flow-mini [data-state="bad"] { --flow-c:var(--flow-bad); }
.flow-mini [data-flow="other"], .flow-mini [data-state="other"] { --flow-c:var(--flow-other); }
.flow-mini [data-flow="lost"], .flow-mini [data-state="lost"] { --flow-c:var(--flow-lost); }
.flow-mini [data-flow="idle"], .flow-mini [data-state="idle"] { --flow-c:var(--flow-idle); }
.flow-mini-dot[data-flow="run"] { animation:flow-mini-pulse 1.4s ease-in-out infinite; }
@keyframes flow-mini-pulse { 50% { opacity:.35; } }
@media (prefers-reduced-motion: reduce) {
  .flow-edge[data-state="run"], .module-chip[data-flow="run"], .flow-mini-dot[data-flow="run"] { animation:none; }
  .flow-edge-drawin { animation:none; stroke-dasharray:none; stroke-dashoffset:0; }
}
/* per-module stop affordance: a small ✕ inside an in-flight chip (per-module control feature) */
.module-chip-stop { position:absolute; top:5px; right:5px; padding:0 3px; font-size:11px; line-height:1; cursor:pointer;
               background:none; border:none; color:inherit; opacity:0.55; border-radius:4px; }
.module-chip-stop:hover { opacity:1; color:var(--kpi-failed); }
/* per-module "restart from here" affordance (#1414): a small ↻ on a terminal chip. Shares the stop
   button's top-right slot — the two never coexist (stop = running, restart = terminal). */
.module-chip-restart { position:absolute; top:5px; right:5px; padding:0 3px; font-size:12px; line-height:1; cursor:pointer;
               background:none; border:none; color:inherit; opacity:0.55; border-radius:4px; }
.module-chip-restart:hover { opacity:1; color:var(--flow-run, var(--accent)); }
.evt-mod { display:block; border-left:3px solid hsl(var(--mod-h, 210) 70% 55%); padding-left:6px; }
.evt-tag { color:hsl(var(--mod-h, 210) 70% 60%); font-weight:600; }

/* CodeMirror 5 (module editor code/DSL fields) — theme it like our .input controls instead of the
   library's default black-on-white/fixed-height look. The vendored codemirror.min.css loads first;
   these rules override its color/background/border/height. */
.CodeMirror.cm-module-field {
  height: auto;
  min-height: 90px;
  border: 1px solid var(--field-border);
  border-radius: 10px;
  background: var(--field-bg);
  color: var(--ink);
  font-family: "JetBrains Mono", "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  line-height: 1.5;
}
.CodeMirror.cm-module-field .CodeMirror-gutters {
  background: color-mix(in srgb, var(--field-bg) 82%, var(--bg) 18%);
  border-right: 1px solid var(--line);
}
.CodeMirror.cm-module-field .CodeMirror-linenumber { color: var(--muted); }
.CodeMirror.cm-module-field .CodeMirror-cursor { border-left-color: var(--accent); }
.CodeMirror.cm-module-field .CodeMirror-selected { background: color-mix(in srgb, var(--accent) 22%, transparent); }
.CodeMirror.cm-module-field.CodeMirror-focused { border-color: var(--accent); }
.CodeMirror.cm-module-field.cm-readonly { opacity: 0.85; }
/* Readable syntax colors across dark/light/h4xor (the default CM theme colors assume a light bg). */
.CodeMirror.cm-module-field .cm-string { color: var(--accent); }
.CodeMirror.cm-module-field .cm-number,
.CodeMirror.cm-module-field .cm-atom { color: #d19a66; }
.CodeMirror.cm-module-field .cm-property,
.CodeMirror.cm-module-field .cm-keyword { color: #61afef; }
.CodeMirror.cm-module-field .cm-variable-2,
.CodeMirror.cm-module-field .cm-def { color: var(--ink); }
.CodeMirror.cm-module-field .cm-comment { color: var(--muted); font-style: italic; }

/* ── Docs section (/docs): renders the repo's own markdown; see docs_view.py ───────────────────── */
.docs-jump { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.docs-jump a {
  padding: 3px 10px; border: 1px solid var(--line-strong); border-radius: 999px;
  color: var(--ink); text-decoration: none; font-size: 13px; background: var(--field-bg);
}
.docs-jump a:hover { border-color: var(--accent); color: var(--accent); }
.doc-section { scroll-margin-top: 12px; }
.doc-top { margin-top: 12px; text-align: right; }
.doc-top a { color: var(--muted); text-decoration: none; font-size: 12px; }
.doc-top a:hover { color: var(--accent); }
/* The DATAFLOW guided tour (static/tour-mount.js). The viewer fills its host (height:100%), so the
   wrapper sets the height; below 940px (the viewer's own breakpoint) it stacks its columns, so it gets taller. */
.docs-tour { height: 640px; margin-bottom: 16px; border: 1px solid var(--line-strong); border-radius: 8px; overflow: hidden; }
@media (max-width: 940px) { .docs-tour { height: 900px; } }

/* Rendered-markdown body. Scoped to .md-body so it never leaks to the rest of the app. */
.md-body { line-height: 1.6; overflow-wrap: anywhere; }
.md-body > :first-child { margin-top: 0; }
.md-body h1, .md-body h2, .md-body h3, .md-body h4 { line-height: 1.25; margin: 1.4em 0 0.5em; }
.md-body h1 { font-size: 1.7em; }
.md-body h2 { font-size: 1.35em; border-bottom: 1px solid var(--line); padding-bottom: 4px; }
.md-body h3 { font-size: 1.15em; }
.md-body p, .md-body ul, .md-body ol, .md-body blockquote, .md-body table, .md-body pre { margin: 0.6em 0; }
.md-body a { color: var(--accent); text-decoration: underline; }
.md-body ul, .md-body ol { padding-left: 1.5em; }
.md-body li { margin: 0.2em 0; }
.md-body img { max-width: 100%; height: auto; border-radius: 8px; border: 1px solid var(--line); }
.md-body code {
  background: var(--field-bg); border: 1px solid var(--field-border); border-radius: 4px;
  padding: 0 4px; font-size: 0.9em;
}
.md-body pre {
  background: var(--field-bg); border: 1px solid var(--field-border); border-radius: 10px;
  padding: 12px; overflow-x: auto;
}
.md-body pre code { background: none; border: 0; padding: 0; }
/* A ```mermaid block rendered by static/docs-mermaid.js; its source moves into a <details> below it. */
.md-body .mermaid-diagram { margin: 0.6em 0; overflow-x: auto; text-align: center; }
.md-body .mermaid-source > summary { color: var(--muted); font-size: 12px; cursor: pointer; }
.md-body blockquote {
  margin-left: 0; padding: 4px 14px; border-left: 3px solid var(--line-strong);
  color: var(--muted); background: var(--field-bg); border-radius: 0 8px 8px 0;
}
.md-body table { border-collapse: collapse; display: block; overflow-x: auto; max-width: 100%; }
.md-body th, .md-body td { border: 1px solid var(--line); padding: 6px 10px; text-align: left; }
.md-body th { background: var(--field-bg); }
.md-body kbd, kbd {
  background: var(--field-bg); border: 1px solid var(--line-strong); border-bottom-width: 2px;
  border-radius: 5px; padding: 0 5px; font-size: 0.85em; font-family: inherit;
}
.md-body hr { border: 0; border-top: 1px solid var(--line); margin: 1.4em 0; }
/* scan-composer pipeline preview (job-flow.js preview): a draft, so every node is "not started" —
   keep the labels at full ink so the plan stays readable rather than greyed out. */
.sc-flow-wrap { margin-top:10px; }
.sc-flow-title { font-size:10px; letter-spacing:.08em; text-transform:uppercase; margin-bottom:4px; }
.sc-flow[hidden] { display:none; }
.sc-flow .module-chip[data-flow="idle"] .flow-node-label { color:var(--ink); }
.sc-flow-wrap:has(> .sc-flow[hidden]) { display:none; }

/* In-flight HUD (#904): app-bar strip of mini pipeline graphs, one per job in flight. */
.inflight-hud { display:flex; align-items:center; gap:8px; margin-left:auto; margin-right:10px; overflow-x:auto;
                max-width:min(52vw, 640px); }
.inflight-hud[hidden] { display:none; }
.inflight-job { display:inline-flex; align-items:center; gap:6px; padding:3px 8px; border-radius:999px;
                border:1px solid var(--line); background:var(--pill-bg, transparent); color:var(--ink);
                text-decoration:none; white-space:nowrap; font-size:11px; }
.inflight-job:visited { color:var(--ink); }
.inflight-job:hover, .inflight-job:focus-visible { border-color:var(--accent); }
.inflight-name { max-width:14ch; overflow:hidden; text-overflow:ellipsis; }
.inflight-more { font-size:11px; color:var(--muted); white-space:nowrap; }

/* Engagement surface component classes (#840 — folded from the per-page <style> blocks in
 * engagement_detail.html and engagements.html; .eng-status-* was DUPLICATED across both, and
 * engagements.html referenced it without carrying the rule, so this also closes that gap). */
.scope-list { max-height: 8.5rem; overflow: auto; margin: .2rem 0 .5rem; padding-left: 1.1rem; }
.jobs-table { width:100%; border-collapse:collapse; font-size:.9rem; }
.jobs-table th { text-align:left; font-weight:600; color:var(--muted); font-size:.78rem; padding:.3rem .5rem; border-bottom:1px solid var(--line); }
.jobs-table td { padding:.4rem .5rem; border-bottom:1px solid var(--line); }
.jobs-table tr:last-child td { border-bottom:none; }
.eng-status-active{border-color:var(--accent); color:var(--accent);}
.eng-status-draft{border-color:var(--muted); color:var(--muted);}
.eng-status-closed{border-color:var(--danger); color:var(--danger);}
/* Exploit graph (#919): the engagement's findings posture as one segmented bar + chip legend,
 * coloured by the shared --sev-* vars so it can never drift from the report/landing palette.
 * Folded from engagement_detail.html's per-page <style> block (#840). */
.exploit-bar { display:flex; height:9px; border-radius:5px; overflow:hidden; background:var(--line); margin:.1rem 0 .5rem; }
.exploit-seg { min-width:2px; }
.exploit-legend { display:flex; gap:.35rem; flex-wrap:wrap; align-items:center; }
.sev-chip { display:inline-flex; align-items:center; gap:.25rem; font-size:.74rem; font-weight:600; line-height:1;
            padding:.15rem .4rem; border-radius:999px; color:#fff; white-space:nowrap; }
.sev-bg-critical, .sev-chip-critical { background:var(--sev-critical); }
.sev-bg-high,     .sev-chip-high     { background:var(--sev-high); }
.sev-bg-medium,   .sev-chip-medium   { background:var(--sev-medium); }
.sev-bg-low,      .sev-chip-low      { background:var(--sev-low); }
.sev-bg-info,     .sev-chip-info     { background:var(--sev-info); }

/* ===========================================================================
 * Utility vocabulary (#840) — a BOUNDED set for the inline-style patterns that
 * actually repeat across templates. Each maps 1:1 to one inline value so a
 * `style="x"` → `class="…"` swap is behaviour-preserving. Placed LAST so a
 * utility wins over a component class of equal specificity by source order
 * (e.g. `class="muted fs-12"` renders 12px, not .muted's 13px).
 *
 * Keep this set small — it is not a utility framework. New entries earn their
 * place by replacing many inline `style=` repeats. Spacing/size utilities are
 * value-named (.mt-10 = margin-top:10px) because the inline values here are
 * irregular, not a clean scale — a value name stays honest and the swap exact.
 * The inline-style count is ratcheted down by tests/test_inline_style_ratchet.py.
 *
 * `!important`: these REPLACE inline `style=` attributes, which win over every
 * component/descendant rule in the cascade. Marking the utilities important
 * reproduces that "always wins" behaviour so a `style="x"` → `class="u"` swap
 * is provably equivalent and cannot silently lose to a higher-specificity
 * selector. The only existing `!important` rules are on xterm / terminal-popout
 * nodes (no template `style=` lived there), so there is no collision.
 * ========================================================================= */
.w-full { width: 100% !important; }
.w-auto { width: auto !important; }
.m-0 { margin: 0 !important; }
.mt-4 { margin-top: 4px !important; }
.mt-8 { margin-top: 8px !important; }
.mt-10 { margin-top: 10px !important; }
.mt-12 { margin-top: 12px !important; }
.fs-12 { font-size: 12px !important; }
.fw-600 { font-weight: 600 !important; }
.inline { display: inline !important; }
.text-danger { color: var(--danger) !important; }
.p-0-4 { padding: 0 4px !important; }
