/* ══════════════════════════════════════════════════════════════════════
   Keep design system v1 — legacy component overrides
   Loads AFTER blackhole.css so single-class selectors win without
   !important. Retrofits the existing markup with editorial styling
   while we leave the HTML largely alone. Combined with the alias map
   in keep-design.css, this turns every legacy page into the new design.
   ══════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════
   Rebuild v2 design tokens — root scope, aliased to legacy variables so
   the rest of the dashboard keeps working unchanged. Hex values below
   are the locked v1 palette; the --fg-*, --bg-*, --signal, --brand-sky,
   and --border-* aliases override the ones declared in keep-design.css
   so legacy selectors automatically pick up the new palette without
   per-rule edits. Subsequent band commits reference these tokens
   directly.
   ══════════════════════════════════════════════════════════════════════ */
:root {
  /* Raw palette */
  --ink-black:      #001d29;
  --pumpkin-spice:  #fa8334;
  --fresh-sky:      #00b2ff;
  --dim-grey:       #4a4a4a;     /* was #6d6d6d — darker so secondary body text reads */
  --silver:         #6a6a6a;     /* was #b0b0b0 — darker so kicker data labels read */
  --alabaster:      #e7e7e7;
  --white-smoke:    #f5f5f5;
  --white:          #ffffff;

  /* Semantic aliases — override keep-design.css */
  --fg-primary:     var(--ink-black);
  --fg-secondary:   var(--dim-grey);
  --fg-tertiary:    var(--silver);
  --bg-canvas:      var(--white-smoke);
  --bg-surface:     var(--white);
  --signal:         var(--pumpkin-spice);
  --brand-sky:      var(--fresh-sky);
  --border-subtle:  rgba(0,29,41,.08);
  --border-default: rgba(0,29,41,.16);
  --border-strong:  rgba(0,29,41,.32);

  /* Type stack — Open Sans across UI and display; JetBrains Mono for
     domains/timestamps. Fonts loaded via <link> in index.html. */
  --font-display:   "Open Sans", -apple-system, system-ui, sans-serif;
  --font-ui:        "Open Sans", -apple-system, system-ui, sans-serif;
  --font-mono:      "JetBrains Mono", "SF Mono", Menlo, monospace;

  --radius-pill:    999px;
}

/* ══════════════════════════════════════════════════════════════════════
   Rebuild v2 #3a — Canonical .kicker

   Single source of truth for every kicker across all six bands (TODAY ·
   SUNDAY MAY 3, NEXT, REQUESTS TODAY, DEVICES — 11 ACTIVE, LIVE — LAST
   5 MINUTES, NOTICE, NETWORK, etc.). Overrides keep-design.css:262
   which had weight 700 / letter-spacing 0.08em — the v1 spec tightens
   both to 600 / 0.04em so kickers read as quiet structural labels, not
   loud all-caps callouts.

   The active state (e.g., the kicker above the active stat-tab value)
   shifts to --signal (pumpkin orange). Provided via two hooks so the
   rule fits both ancestor-driven activation (.keep-stat-tab.active) and
   sibling/standalone activation (.kicker.is-active) if needed later.
   ══════════════════════════════════════════════════════════════════════ */
.kicker {
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fg-tertiary);
  line-height: 16px;
}
.keep-stat-tab.active .kicker,
.kicker.is-active {
  color: var(--signal);
}

/* ══════════════════════════════════════════════════════════════════════
   Rebuild v2 — Band 1 (Nav)

   Wordmark-only brand (shield removed; favicon-only per brand guide),
   static PROTECTED kicker on the right (replaces the legacy
   ss-status-pill green-live indicator), and a redesigned user pill
   with an avatar disc + dynamic username + chevron SVG.

   These rules sit alongside the legacy .ss-topnav-* selectors below so
   the legacy structure on non-overview sections keeps working unchanged;
   the new selectors override the brand-specific bits per the spec.
   ══════════════════════════════════════════════════════════════════════ */

/* Brand wordmark — Open Sans 800 + 1px text-stroke to push the
   letters thicker than the highest font-weight Open Sans ships. */
.ss-topnav-brand {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}
.ss-topnav-brand .ss-logo-mark { display: none !important; }
.ss-topnav-brand span,
.ss-topnav-brand span strong {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -0.03em;
  color: var(--ink-black);
  -webkit-text-stroke: 1px var(--ink-black);
  text-stroke: 1px var(--ink-black);
}

/* PROTECTED kicker — Fresh Sky, 11px, uppercase. This is the nav's
   "good state" signal and uses the same Fresh Sky token as the Band 4B
   live-feed pulse (commit #7) so cyan reads as "positive / connected"
   throughout the dashboard. */
.ss-nav-protected {
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fresh-sky);
  white-space: nowrap;
}

/* Disabled-tone dot separator between PROTECTED and the user pill. */
.ss-nav-dot {
  display: inline-block;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--silver);
  flex-shrink: 0;
}

/* User pill — rounded-999, white fill, --border-default 1px border.
   Replaces the legacy fa-user-circle + fa-caret-down icon trio with an
   avatar disc + username + chevron SVG. */
.ss-user-btn {
  background: var(--white);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-pill);
  padding: 3px 12px 3px 3px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--ink-black);
  font-family: var(--font-ui);
  cursor: pointer;
  transition: border-color var(--duration-fast, 150ms) ease;
}
.ss-user-btn:hover {
  background: var(--white);
  border-color: var(--border-strong);
}
/* Legacy icons inside the pill — hidden so the rebuild doesn't render
   them if any HTML still ships them. The new HTML uses .ss-user-avatar
   and .ss-user-chevron instead. */
.ss-user-btn > i.fa-user-circle,
.ss-user-btn > i.fa-caret-down { display: none !important; }

.ss-user-btn .ss-user-avatar {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--ink-black);
  color: var(--white);
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.ss-user-btn #user-display-name {
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-black);
  line-height: 1;
}
.ss-user-btn .ss-user-chevron {
  color: var(--ink-black);
  flex-shrink: 0;
}

/* Active nav underline = ink-black (structural). The legacy rule below
   already sets `border-bottom-color: var(--fg-primary)` which now resolves
   to ink-black via the rebuild v2 alias — explicit re-declaration here
   so the rule is documented in the band block. */
.ss-topnav-section.active {
  color: var(--ink-black);
  border-bottom-color: var(--ink-black);
}

/* (Beta) tag inline with a section label — applied to Community in v2.1.
   Quieter than the section text so it reads as a secondary annotation,
   not a parallel label. */
.ss-topnav-section .beta {
  font-family: var(--font-ui);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--silver);
  vertical-align: middle;
  margin-left: 6px;
  text-transform: none;
}

/* Chrome is flat. Top nav sits on --bg-canvas with only the 1px bottom
   hairline; nav-wrap and the dropdown panel get the same treatment.
   Per-button shadows inside menus are preserved (those signal
   pressability per brand). */
.ss-topnav,
.ss-nav-wrap {
  box-shadow: none !important;
}

/* When the current section has no sub-nav (Devices, Community), hide
   the empty 38px strip and pull main content up under the topnav.
   Toggled from renderSubnav() in app.js. */
body.ss-no-subnav .ss-subnav { display: none; }
body.ss-no-subnav .ss-main { padding-top: calc(var(--ss-topnav-h) + 1px); }

/* User dropdown container — flat. The legacy rule at blackhole.css:214
   sets `box-shadow: 0 8px 32px rgba(0,0,0,0.45)` on `.ss-user-dropdown`
   (class). The keep-overrides.css aliases earlier in this file target
   `.ss-user-dd` (which only exists as an ID on the element, so those
   class selectors match nothing). The selector below targets the real
   class so the override actually wins. */
.ss-user-dropdown {
  box-shadow: none !important;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* Toggle switch — canonical tokens. The legacy track was
   rgba(255,255,255,0.12) (invisible on white cards in light mode) and
   the on-state was green --ss-success. Replaced with sunken cream
   off-state and signal-default orange on-state so the control reads
   in either theme and follows the "orange = interactivity" rule. */
.ss-switch-track {
  background: var(--bg-sunken);
  border: 1px solid var(--border-default);
  box-sizing: border-box;
}
.ss-switch-track::before {
  background: #fff;
  box-shadow: 0 1px 2px rgba(0,0,0,0.12);
}
.ss-switch input:checked + .ss-switch-track {
  background: var(--signal-default);
  border-color: var(--signal-default);
}

/* Placeholder for the new Advanced section (sec-advanced). Honest
   "coming soon" state — centered card, no chrome elevation. */
.keep-advanced-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: calc(100vh - 200px);
  padding: 40px 20px;
}
.keep-advanced-card {
  max-width: 480px;
  text-align: center;
}
.keep-advanced-card .kicker {
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--silver);
  margin-bottom: 12px;
}
.keep-advanced-card h2 {
  font-family: var(--font-display);
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--ink-black);
  margin: 0 0 12px;
}
.keep-advanced-card p {
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 1.55;
  color: var(--dim-grey);
  margin: 0;
}

/* ── Top nav (legacy chrome rules — preserved for non-overview pages) ── */
.ss-topnav {
  background: var(--bg-canvas);
  border-bottom: 1px solid var(--border-subtle);
  height: 60px;                              /* 18 top + ~24 content + 18 bot = 60 → 18×36 effective */
  padding: 0 36px;                           /* rebuild v2 #3a: Band 1 density 18×36 */
  display: flex;
  align-items: center;
  gap: var(--space-8);
  box-shadow: none;
}
.ss-topnav-brand .ss-logo-mark {
  background: var(--brand-sky-900);
  color: #fff;
  box-shadow: none;
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: -0.02em;
}
.ss-topnav-brand span { font-family: var(--font-display); font-size: 22px; letter-spacing: -0.02em; color: var(--brand-sky-900); font-weight: 600; }
.ss-topnav-brand span strong { color: var(--brand-sky-900); font-weight: 600; }
.ss-topnav-sections { gap: var(--space-6); }
.ss-topnav-section {
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 600;                          /* rebuild v2 #3a: was 500 — brand-guide nav weight */
  color: var(--fg-secondary);
  padding: 18px 0;
  border-bottom: 2px solid transparent;
  letter-spacing: 0;
  text-transform: none;
}
.ss-topnav-section:hover { color: var(--fg-primary); border-bottom-color: transparent; }
.ss-topnav-section.active { color: var(--fg-primary); border-bottom-color: var(--fg-primary); }

.ss-topnav-right { gap: var(--space-3); }
.ss-status-pill {
  background: transparent;
  border: 0;
  color: var(--fg-tertiary);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0;
}
.ss-status-pill.ok      { color: var(--fg-tertiary); }
.ss-status-pill.warn,
.ss-status-pill.err     { color: var(--status-danger-fg); }
.ss-pulse { background: var(--signal-default); }

/* User dropdown chip */
.ss-user-btn {
  border: 1px solid var(--border-default);
  background: var(--bg-surface);
  color: var(--fg-primary);
  border-radius: var(--radius-pill);
  padding: 4px 10px 4px 4px;
  font-size: 13px;
  font-weight: 500;
}
.ss-user-btn:hover { border-color: var(--border-strong); background: var(--bg-surface); }
.ss-user-btn .ss-user-avatar,
.ss-user-btn .avatar {
  background: var(--brand-sky-900);
  color: #fff;
}

.ss-user-dd {
  background: var(--bg-overlay);
  border: 1px solid var(--border-default);
  box-shadow: var(--shadow-lg);
  border-radius: var(--radius);
}
.ss-user-dd a, .ss-user-dd button {
  color: var(--fg-primary);
  font-family: var(--font-ui);
  font-size: 13px;
  padding: var(--space-3) var(--space-4);
}
.ss-user-dd a:hover, .ss-user-dd button:hover { background: var(--bg-sunken); }

/* ── Sub nav ─────────────────────────────────────────────────────────── */
.ss-subnav {
  background: var(--bg-canvas);
  border-bottom: 1px solid var(--border-subtle);
  padding: var(--space-3) var(--space-12);
  display: flex;
  align-items: center;
  gap: var(--space-5);
  height: auto;
  box-shadow: none;
}
.ss-subnav-item {
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 500;
  color: var(--fg-tertiary);
  padding: 6px 0;
  border-bottom: 1.5px solid transparent;
  letter-spacing: 0;
  text-transform: none;
}
.ss-subnav-item:hover { color: var(--fg-primary); }
.ss-subnav-item.active { color: var(--fg-primary); border-bottom-color: var(--fg-primary); }
.ss-subnav-sep { display: none; }

/* ── Page container ─────────────────────────────────────────────────── */
.ss-section {
  padding: var(--space-8) var(--space-12);
  background: var(--bg-canvas);
  color: var(--fg-primary);
}
.ss-section.active { display: block; }
@media (max-width: 1024px) { .ss-section { padding: var(--space-6) var(--space-8); } }
@media (max-width: 768px)  { .ss-section { padding: var(--space-5); } }

/* The overview wrapper does its own negative margins; cancel section
   padding for it so the editorial composition spans full width. */
.ss-section:has(> .keep-overview) { padding: 0; }

/* ── Cards ──────────────────────────────────────────────────────────── */
.card {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  box-shadow: none;
  backdrop-filter: none;
  overflow: hidden;
  margin-bottom: var(--space-5);
}
.card:hover {
  background: var(--bg-surface);
  border-color: var(--border-subtle);
  box-shadow: none;
}
.card-hd {
  background: transparent;
  border-bottom: 1px solid var(--border-subtle);
  padding: var(--space-4) var(--space-5);
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.card-title {
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-tertiary);
}
.card-title i { color: var(--fg-tertiary); margin-right: 6px; }
.card-body { padding: var(--space-5); color: var(--fg-primary); font-size: 14px; line-height: 20px; }
.card-footer {
  background: transparent;
  border-top: 1px solid var(--border-subtle);
  padding: var(--space-3) var(--space-5);
  font-size: 12px;
  color: var(--fg-tertiary);
}

/* ── Stat cards (used outside the overview tab) ──────────────────────── */
.stats-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}
.stat-card {
  padding: var(--space-5);
  box-shadow: none;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
}
.stat-top { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--space-3); }
.stat-icon {
  width: 32px; height: 32px;
  border-radius: var(--radius-sm);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--bg-sunken);
  color: var(--fg-tertiary);
  font-size: 14px;
}
.stat-icon.si-blue, .stat-icon.si-red, .stat-icon.si-amber, .stat-icon.si-green {
  background: var(--bg-sunken);
  color: var(--fg-tertiary);
}
.stat-sub {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-tertiary);
  background: transparent;
  border: 0;
  padding: 0;
}
.stat-sub.blue, .stat-sub.red, .stat-sub.amber, .stat-sub.green { color: var(--fg-tertiary); }
.stat-val {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 32px;
  line-height: 1.1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--fg-primary);
}
.stat-lbl {
  font-size: 12px;
  color: var(--fg-secondary);
  margin-top: 2px;
}

@media (max-width: 1024px) { .stats-row { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px)  { .stats-row { grid-template-columns: 1fr; } }

/* ── Buttons ────────────────────────────────────────────────────────── */
.btn {
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  padding: 10px 16px;
  border-radius: var(--radius);
  border: 1px solid transparent;
  background: transparent;
  color: var(--fg-primary);
  cursor: pointer;
  transition: background-color var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  box-shadow: none;
}
.btn:focus-visible { outline: 2px solid var(--signal-focus); outline-offset: 2px; }
.btn:active:not(:disabled) { transform: none; }

/* Rebuild v2 #2.2 — global button-system redefinition.
   Primary is the Ink Black pill: white text on ink-black, rounded-999,
   layered drop shadow per brand guide (buttons elevate, chrome is flat).
   Ghost is the inverse: white fill, ink-black border, no shadow.
   .btn-default is aliased to .btn-ghost so the ~30 legacy callers
   (Add rule, Copy DNS, Reset, etc.) shift to the new ghost in one step.
   Both classes apply to every .btn-primary / .btn-default in the app
   — the banner, the walkthrough wizard, the settings forms, the upgrade
   button, etc. This is the reusable button system referenced by Bands
   5+ in the rebuild plan. */
.btn-primary {
  background: var(--ink-black);
  border: 1px solid var(--ink-black);
  color: var(--white);
  padding: 10px 20px;
  border-radius: var(--radius-pill);
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0;
  text-transform: none;
  box-shadow: 0 2px 0 rgba(0,29,41,.25), 0 6px 14px rgba(0,29,41,.18);
}
.btn-primary:hover {
  background: var(--ink-black);
  border-color: var(--ink-black);
  box-shadow: 0 2px 0 rgba(0,29,41,.25), 0 8px 18px rgba(0,29,41,.22);
}
/* :not(:disabled) bumps specificity to match the base .btn:active rule
   at line 477 so this transform actually applies. Source order wins
   the tie. */
.btn-primary:active:not(:disabled) {
  transform: translateY(1px);
  box-shadow: 0 1px 0 rgba(0,29,41,.25), 0 3px 8px rgba(0,29,41,.12);
}

.btn-default,
.btn-ghost {
  background: var(--white);
  border: 1px solid var(--border-default);
  color: var(--ink-black);
  padding: 10px 20px;
  border-radius: var(--radius-pill);
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0;
  text-transform: none;
  box-shadow: none;
}
.btn-default:hover,
.btn-ghost:hover {
  background: var(--white);
  border-color: var(--ink-black);
  color: var(--ink-black);
}

.btn-success { background: var(--status-success-fg); border-color: var(--status-success-fg); color: #fff; }
.btn-warning { background: var(--status-warning-fg); border-color: var(--status-warning-fg); color: #fff; }
.btn-danger  { background: var(--status-danger-fg);  border-color: var(--status-danger-fg);  color: #fff; }
.btn-danger:hover  { background: #9B2F25; border-color: #9B2F25; }

.btn-sm { padding: 6px 12px; font-size: 12px; }

.btn.ss-success-state { background-color: var(--status-success-fg) !important; border-color: var(--status-success-fg) !important; color: #fff !important; }
.btn.ss-error-state   { background-color: var(--status-danger-fg)  !important; border-color: var(--status-danger-fg)  !important; }

/* ── Inline links ───────────────────────────────────────────────────── */
a { color: var(--brand-sky-500); }
a:hover { color: var(--brand-sky-700); }

/* ── Forms ──────────────────────────────────────────────────────────── */
.form-control {
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 20px;
  color: var(--fg-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius);
  padding: 10px 12px;
  width: 100%;
  transition: border-color var(--duration-fast) var(--ease-standard),
              box-shadow var(--duration-fast) var(--ease-standard);
}
.form-control:focus {
  outline: none;
  border-color: var(--signal-default);
  box-shadow: 0 0 0 3px var(--signal-focus);
}
.form-control::placeholder { color: var(--fg-tertiary); }
select.form-control, textarea.form-control { background: var(--bg-surface); }
.form-group label, .form-label { font-size: 12px; font-weight: 600; color: var(--fg-secondary); margin-bottom: 6px; display: block; }

/* ── Tables ─────────────────────────────────────────────────────────── */
.ss-table {
  width: 100%;
  /* Was collapse; using separate so sticky thead positioning works without
     breaking borders. border-spacing:0 keeps the row-divider lines tight
     against each other so the visual is unchanged. */
  border-collapse: separate;
  border-spacing: 0;
  font-size: 13px;
}
.ss-table thead { background: transparent; }
.ss-table th {
  text-align: left;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-tertiary);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-default);
  background: transparent;
}
.ss-table td {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-subtle);
  color: var(--fg-primary);
  background: transparent;
  vertical-align: middle;
}
.ss-table tr:last-child td { border-bottom: 0; }
.ss-table tr:hover td { background: var(--bg-sunken); color: var(--fg-primary); }
.ss-table td.mono, .ss-table .mono { font-family: var(--font-mono); color: var(--fg-primary); }

/* ── Severity badges (pills) ────────────────────────────────────────── */
.sev {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.sev.crit { background: var(--status-danger-bg);  color: var(--status-danger-fg); }
.sev.high { background: var(--status-warning-bg); color: var(--status-warning-fg); }
.sev.med, .sev.medium { background: var(--signal-subtle); color: var(--signal-default); }
.sev.low  { background: var(--bg-sunken); color: var(--fg-secondary); }

/* ── Device rows (injected by app.js) ────────────────────────────────── */
.device-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  padding: var(--space-3) var(--space-5);
  border-bottom: 1px solid var(--border-subtle);
  background: transparent;
}
.device-row:last-child { border-bottom: 0; }
.device-row:hover { background: var(--bg-sunken); }
.device-row:hover .device-row-actions { opacity: 1; }

/* Row action toolbar — hidden until row hover so the right edge of every
   row isn't permanently shouting "delete". Touch devices show the actions
   immediately via the @media block below. */
.device-row-actions {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  opacity: 0;
  transition: opacity 120ms ease;
}
@media (hover: none) {
  .device-row-actions { opacity: 1; }
}

.keep-icon-btn {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-subtle);
  background: transparent;
  color: var(--fg-tertiary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
  font-size: 12px;
}
.keep-icon-btn:hover {
  background: var(--bg-overlay);
  border-color: var(--border-default);
  color: var(--fg-primary);
}
.keep-icon-btn:focus-visible {
  outline: 2px solid var(--signal-default);
  outline-offset: 2px;
}

/* Device filter button active state — was previously toggled on via JS but
   no CSS rule painted the active state, so the chosen filter was invisible. */
.device-filter-btn.active,
.activity-filter-btn.active {
  background: var(--bg-overlay);
  border-color: var(--border-default);
  color: var(--fg-primary);
  font-weight: 600;
}

/* Privacy score chip — backgrounds rebalanced; class names retained for
   compatibility but the *visual* mapping is intuitive: green = safer, red =
   riskier. The matching "unknown" rule was missing entirely. */
.ss-score-unknown {
  background: var(--bg-sunken);
  color: var(--fg-tertiary);
}

/* Wizard overlay — desktop gets a blurred backdrop; mobile drops it to
   avoid the GPU stutter that backdrop-filter causes on low-end phones. */
.wizard-overlay { backdrop-filter: none; }
@media (min-width: 768px) {
  .wizard-overlay { backdrop-filter: blur(8px); }
}

/* Login #130 — between 580px and 720px the two-column login+FAQ layout
   crammed each column into a sliver. Force single-column below 768px. */
@media (max-width: 767px) {
  #sec-login > div { flex-direction: column !important; max-width: 480px !important; }
  #sec-login > div > div { max-width: 100% !important; width: 100% !important; }
}

/* ── Cross-cutting helpers ──────────────────────────────────────────── */

/* #131 — single visibility utility so JS can flip elements via class
   toggling instead of mutating inline display. Older inline display:none
   handlers are gradually being replaced; the !important keeps them in sync
   with this utility when both are applied. */
.hidden { display: none !important; }

/* #133 — disabled state should read as visibly disabled, not as a hover
   accident. */
.btn:disabled,
.keep-btn:disabled,
.ss-btn:disabled,
.btn[disabled],
.keep-btn[disabled],
.ss-btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
  filter: grayscale(0.2);
}

/* #134 — focus rings need to be visible on light surfaces. Apply a clear
   outline on focus-visible across our standard interactive elements. */
.btn:focus-visible,
.keep-btn:focus-visible,
.ss-btn:focus-visible,
.icon-btn:focus-visible,
.ss-topnav-section:focus-visible,
.ss-subnav-item:focus-visible,
.form-control:focus-visible {
  outline: 3px solid var(--signal-default);
  outline-offset: 2px;
}

/* #150 — sticky table headers so column context survives scrolling.
   Requires `border-collapse: separate` for reliable Chrome/Safari behavior;
   we set that and re-add the per-row underline via the th's own border. */
.ss-table {
  border-collapse: separate;
  border-spacing: 0;
}
.ss-table thead th {
  position: sticky;
  top: 0;
  background: var(--bg-canvas, var(--ss-bg-deep));
  z-index: 2;
}
@media (max-width: 480px) {
  .ss-table thead th { position: static; }
}

/* #149 — small dark-mode patches for components that hardcoded colors. */
html.keep-dark .keep-user-avatar { background: var(--bg-overlay); }
html.keep-dark .chart-legend { color: var(--fg-tertiary); }

/* Skip-setup button in the wizard — was a near-invisible muted link in the
   top corner. Use a higher-contrast token and a clearer icon. */
.wizard-skip {
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--border-default);
  color: var(--fg-secondary);
  border-radius: var(--radius-sm);
  padding: 6px 12px;
  font-size: 13px;
  cursor: pointer;
}
.wizard-skip:hover { color: var(--fg-primary); border-color: var(--fg-secondary); }

/* Router brand grid: auto-fill so "TP-Link" doesn't wrap into "TP-/Link"
   on narrow screens. */
.wizard-brand-grid {
  display: grid !important;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)) !important;
}
.device-ico {
  width: 28px; height: 28px;
  border-radius: var(--radius-sm);
  background: var(--bg-sunken);
  color: var(--fg-secondary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
}
.device-name { font-size: 13px; font-weight: 500; color: var(--fg-primary); }
.device-name + .device-sub,
.device-sub { font-size: 12px; color: var(--fg-secondary); }
.device-count {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 18px;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  color: var(--fg-primary);
}

/* ── Alert cards ─────────────────────────────────────────────────────── */
.alert-card {
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-3);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
}
.alert-card.warn { background: var(--status-warning-bg); border-color: rgba(163, 102, 8, 0.18); }
.alert-card.err  { background: var(--status-danger-bg);  border-color: rgba(179, 59, 46, 0.18); }
.alert-card.info { background: var(--bg-surface);        border-color: var(--border-subtle); }
.alert-top {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: 4px;
}
.alert-ico { font-size: 14px; color: var(--fg-tertiary); }
.alert-ico.warn { color: var(--status-warning-fg); }
.alert-ico.err  { color: var(--status-danger-fg); }
.alert-device { font-size: 13px; font-weight: 600; color: var(--fg-primary); flex: 1; }
.alert-badge {
  font-size: 10px;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  background: var(--bg-sunken);
  color: var(--fg-secondary);
}
.alert-badge.warn { background: var(--status-warning-bg); color: var(--status-warning-fg); }
.alert-badge.err  { background: var(--status-danger-bg);  color: var(--status-danger-fg); }
.alert-desc { font-size: 12px; color: var(--fg-secondary); line-height: 1.45; }
.alert-meta { font-size: 11px; color: var(--fg-tertiary); margin-top: 4px; }

/* ── Three-column grid (used on overview but also a few others) ─────── */
.three-col {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
}
@media (max-width: 1024px) { .three-col { grid-template-columns: 1fr 1fr; } }
@media (max-width: 768px)  { .three-col { grid-template-columns: 1fr; } }

/* ── Placeholder (empty states) ──────────────────────────────────────── */
.ph {
  padding: var(--space-8);
  text-align: center;
  color: var(--fg-tertiary);
  font-size: 13px;
}
.ph-icon { font-size: 22px; opacity: 0.4; margin-bottom: var(--space-2); }
.ph-text { color: var(--fg-tertiary); }

/* ── Modal ───────────────────────────────────────────────────────────── */
.modal-backdrop, .ss-modal-backdrop {
  background: rgba(10, 10, 15, 0.4);
  backdrop-filter: blur(2px);
}
.modal, .ss-modal {
  background: var(--bg-overlay);
  border: 1px solid var(--border-default);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  color: var(--fg-primary);
}
.modal-hd, .ss-modal-hd { border-bottom: 1px solid var(--border-subtle); padding: var(--space-4) var(--space-5); }
.modal-title, .ss-modal-title { font-family: var(--font-ui); font-weight: 600; font-size: 16px; color: var(--fg-primary); }
.modal-body { padding: var(--space-5); }
.modal-footer { border-top: 1px solid var(--border-subtle); padding: var(--space-3) var(--space-5); }

/* ── Toast ──────────────────────────────────────────────────────────── */
.toast, .ss-toast {
  background: var(--bg-overlay);
  border: 1px solid var(--border-default);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  color: var(--fg-primary);
  padding: var(--space-3) var(--space-5);
  font-size: 13px;
}
.toast.success { border-color: rgba(31, 122, 77, 0.3); }
.toast.error   { border-color: rgba(179, 59, 46, 0.3); }

/* ── Activity log (table on overview subpage 1) ──────────────────────── */
.activity-log-page { color: var(--fg-primary); }
.activity-log-card { background: var(--bg-surface); border-radius: var(--radius); }
.activity-log-controls {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border-subtle);
  flex-wrap: wrap;
}
.filter-buttons { display: flex; gap: var(--space-2); }
.activity-filter-btn {
  background: transparent !important;
  border: 1px solid var(--border-default) !important;
  color: var(--fg-secondary) !important;
  font-weight: 500 !important;
  font-size: 12px !important;
  padding: 6px 12px !important;
  border-radius: var(--radius-pill) !important;
}
.activity-filter-btn.active {
  background: var(--fg-primary) !important;
  border-color: var(--fg-primary) !important;
  color: #fff !important;
}
.activity-filter-btn:hover:not(.active) {
  border-color: var(--border-strong) !important;
  color: var(--fg-primary) !important;
}
.search-wrapper {
  position: relative;
  display: flex;
  align-items: center;
}
.search-wrapper i { position: absolute; left: 12px; color: var(--fg-tertiary); font-size: 13px; }
.search-wrapper input { padding-left: 32px !important; width: 240px; }
.activity-log-table th { white-space: nowrap; }
.activity-log-table td.mono, .activity-log-table .mono { font-family: var(--font-mono); font-size: 12px; }

/* ── Toggle switch (categories etc) ──────────────────────────────────── */
.toggle-switch, .ss-toggle {
  position: relative;
  display: inline-block;
  width: 36px;
  height: 20px;
  flex-shrink: 0;
}
.toggle-switch input, .ss-toggle input { opacity: 0; width: 0; height: 0; }
.toggle-switch .slider, .ss-toggle .slider,
.toggle-switch-slider, .ss-toggle-slider {
  position: absolute;
  cursor: pointer;
  inset: 0;
  background: var(--fg-disabled);
  border-radius: var(--radius-pill);
  transition: background-color var(--duration-fast) var(--ease-standard);
}
.toggle-switch .slider::before, .ss-toggle .slider::before,
.toggle-switch-slider::before, .ss-toggle-slider::before {
  content: "";
  position: absolute;
  height: 14px; width: 14px;
  left: 3px; top: 3px;
  background: #fff;
  border-radius: 50%;
  transition: transform var(--duration-fast) var(--ease-standard);
}
.toggle-switch input:checked + .slider,
.ss-toggle input:checked + .slider,
.toggle-switch input:checked + .toggle-switch-slider,
.ss-toggle input:checked + .ss-toggle-slider { background: var(--signal-default); }
.toggle-switch input:checked + .slider::before,
.ss-toggle input:checked + .slider::before,
.toggle-switch input:checked + .toggle-switch-slider::before,
.ss-toggle input:checked + .ss-toggle-slider::before { transform: translateX(16px); }

/* ── Categories grid ─────────────────────────────────────────────────── */
.cat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-4);
}
.cat-tile, .category-card, .category-tile {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.cat-tile.enabled, .category-card.enabled, .category-tile.enabled { border-color: var(--signal-default); }
.cat-tile .cat-name, .category-card .cat-name { font-size: 14px; font-weight: 600; color: var(--fg-primary); }
.cat-tile .cat-desc, .category-card .cat-desc { font-size: 12px; color: var(--fg-secondary); line-height: 1.5; }

/* ── Mode/segmented controls ─────────────────────────────────────────── */
.segmented {
  display: inline-flex;
  gap: var(--space-3);
  font-size: 11px;
}
.segmented .seg {
  cursor: pointer;
  padding: 4px 8px;
  border-bottom: 1.5px solid transparent;
  color: var(--fg-tertiary);
}
.segmented .seg.active { color: var(--fg-primary); border-bottom-color: var(--fg-primary); }

/* ── Login section ───────────────────────────────────────────────────── */
#sec-login {
  background: var(--bg-canvas);
  padding: var(--space-12) var(--space-8) !important;
}
#sec-login .ss-login-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  padding: var(--space-12) var(--space-8);
}
#sec-login .ss-login-brand { text-align: center; margin-bottom: var(--space-6); }
#sec-login .ss-login-brand .ss-logo-mark {
  width: 0 !important;
  height: 0 !important;
  display: none !important;
}
#sec-login .ss-login-brand::before {
  content: "KEEP.";
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 30px;
  letter-spacing: -0.02em;
  color: var(--brand-sky-900);
  margin-bottom: var(--space-2);
}
#sec-login .ss-login-title {
  display: none; /* replaced by ::before above */
}
#sec-login .ss-login-host {
  font-family: var(--font-ui);
  font-size: 14px;
  color: var(--fg-secondary);
  line-height: 1.4;
  margin-top: var(--space-1);
}
#sec-login .ss-login-input-wrap {
  position: relative;
  margin-bottom: var(--space-3);
}
#sec-login .ss-login-input-icon {
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--fg-tertiary);
  font-size: 13px;
  pointer-events: none;
}
#sec-login .ss-login-input {
  width: 100%;
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 20px;
  color: var(--fg-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius);
  padding: 12px 14px 12px 38px;
  transition: border-color var(--duration-fast) var(--ease-standard),
              box-shadow var(--duration-fast) var(--ease-standard);
}
#sec-login .ss-login-input:focus {
  outline: none;
  border-color: var(--signal-default);
  box-shadow: 0 0 0 3px var(--signal-focus);
}
#sec-login .ss-login-input::placeholder { color: var(--fg-tertiary); }

#sec-login .ss-login-btn {
  width: 100%;
  background: var(--signal-default);
  color: #fff;
  border: 0;
  border-radius: var(--radius);
  padding: 12px 16px;
  font-family: var(--font-ui);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  margin-top: var(--space-2);
  transition: background-color var(--duration-fast) var(--ease-standard);
}
#sec-login .ss-login-btn:hover { background: var(--signal-hover); }
#sec-login .ss-login-btn:active { background: var(--signal-pressed); }

/* FAQ details — editorial-style accordion */
#sec-login details.card {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  margin-bottom: var(--space-2);
  box-shadow: none;
  overflow: hidden;
}
#sec-login details.card summary {
  list-style: none;
  padding: var(--space-4) var(--space-5);
  font-family: var(--font-ui);
  font-size: 14px;
  font-weight: 600;
  color: var(--fg-primary);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
#sec-login details.card summary::-webkit-details-marker { display: none; }
#sec-login details.card summary i {
  color: var(--fg-tertiary) !important;
  font-size: 10px !important;
  transition: transform var(--duration-fast) var(--ease-standard);
}
#sec-login details[open].card summary i { transform: rotate(90deg); }
#sec-login details.card > div {
  padding: 0 var(--space-5) var(--space-4) calc(var(--space-5) + 14px);
  font-size: 13px;
  line-height: 1.65;
  color: var(--fg-secondary);
}
#sec-login details.card > div br { line-height: 2; }

/* "Common questions" kicker above FAQ */
#sec-login [style*="Common questions"],
#sec-login div[style*="text-transform:uppercase"] {
  color: var(--fg-tertiary) !important;
}

/* ── Walkthrough / onboarding modal ──────────────────────────────────── */
.walkthrough-overlay, .walkthrough-backdrop {
  background: rgba(10, 10, 15, 0.4);
  backdrop-filter: blur(2px);
}
.walkthrough-modal {
  background: var(--bg-overlay);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  color: var(--fg-primary);
}
.walkthrough-step h2, .walkthrough-step h3 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--fg-primary);
}

/* ── Misc legacy decorative wash — neutralize ───────────────────────── */
.ss-section .ambient,
.ss-glow, .ss-orb { display: none !important; }

/* ── Network link banner (overview) ─────────────────────────────────── */
#link-network-banner {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-5);
  box-shadow: none;
}
#link-network-banner .lnb-title, #link-network-banner h3 {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 14px;
  color: var(--fg-primary);
  margin: 0 0 var(--space-1);
}

/* ── Chart legend ────────────────────────────────────────────────────── */
.chart-legend {
  display: flex;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-5);
  font-size: 12px;
  color: var(--fg-secondary);
  flex-wrap: wrap;
}
.legend-item { display: inline-flex; align-items: center; gap: 6px; }
.legend-line { width: 18px; height: 2px; }

/* ── Page typography defaults inside sections ────────────────────────── */
.ss-section h1, .ss-section h2 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--fg-primary);
}
.ss-section h1 { font-size: 28px; line-height: 1.2; margin: 0 0 var(--space-2); }
.ss-section h2 { font-size: 22px; line-height: 1.3; margin: 0 0 var(--space-2); }
.ss-section h3, .ss-section h4 {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 15px;
  color: var(--fg-primary);
  margin: 0 0 var(--space-2);
}
.ss-section p { color: var(--fg-secondary); font-size: 14px; line-height: 20px; }

/* Section intro line — kicker-style paragraph above page content */
.ss-section .page-intro,
.ss-section .section-intro,
.ss-section .lead {
  font-size: 13px;
  color: var(--fg-tertiary);
  margin-bottom: var(--space-5);
}

/* ── Hamburger mobile nav ─────────────────────────────────────────────── */
.ss-hamburger {
  background: transparent;
  border: 1px solid var(--border-default);
  color: var(--fg-primary);
  border-radius: var(--radius-sm);
  width: 36px;
  height: 36px;
}

/* ── Reduce decorative shadows on misc tiles ─────────────────────────── */
[class*="card"]:not(.alert-card):not(.cat-tile):not(.category-tile):not(.category-card):not(.stat-card) {
  box-shadow: none !important;
}

/* ══════════════════════════════════════════════════════════════════════
   Section header pattern — kicker + title + intro line on each tab
   ══════════════════════════════════════════════════════════════════════ */
.keep-page-header {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-6);
}
.keep-page-header .kicker { color: var(--fg-tertiary); }
.keep-page-header h1 {
  font-family: var(--font-display);
  font-size: 28px;
  line-height: 1.2;
  letter-spacing: -0.02em;
  font-weight: 600;
  color: var(--fg-primary);
  margin: 0;
}
.keep-page-header p { font-size: 14px; color: var(--fg-secondary); margin: 0; max-width: 640px; }

/* ══════════════════════════════════════════════════════════════════════
   Step 7: kill remaining legacy decorative effects
   Targets specific blackhole.css rules that hardcode RGB values and
   therefore don't follow the new tokens. Done as overrides rather than
   deletions so we can roll back cleanly.
   ══════════════════════════════════════════════════════════════════════ */

/* Logo mark — was a sky-blue gradient. Now a neutral brand-deep disc. */
.ss-topnav-brand .ss-logo-mark {
  background: var(--brand-sky-900);
  color: #fff;
  border-radius: var(--radius-sm);
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: -0.02em;
}
.ss-topnav-brand .ss-logo-mark i { font-size: 13px; }

/* Theme toggle button */
.ss-theme-toggle {
  background: transparent;
  border: 1px solid var(--border-default);
  color: var(--fg-tertiary);
  width: 36px; height: 36px;
  border-radius: var(--radius-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard);
}
.ss-theme-toggle:hover {
  background: transparent;
  color: var(--fg-primary);
  border-color: var(--border-strong);
}

/* Legacy stat icon color washes — flatten */
.si-blue, .si-red, .si-amber, .si-green,
.stat-sub.blue, .stat-sub.red, .stat-sub.amber, .stat-sub.green {
  background: var(--bg-sunken);
  color: var(--fg-tertiary);
}

/* Medium severity pill — was sky-blue */
.sev.med, .sev.medium {
  background: var(--signal-subtle);
  color: var(--signal-default);
}
.alert-card.info { background: var(--bg-surface); border-color: var(--border-subtle); }

/* Gradient onboarding/promo tiles — were sky-blue + green */
[class*="promo-tile"], [class*="upgrade-tile"], [class*="celebrate"],
.ss-promo, .ss-upgrade-tile {
  background: var(--bg-surface) !important;
  border: 1px solid var(--border-subtle) !important;
  color: var(--fg-primary) !important;
}

/* Progress bar — single-tone signal */
.ss-progress-bar { background: var(--bg-sunken); border-radius: var(--radius-pill); }
.ss-progress-bar-fill { background: var(--signal-default); border-radius: var(--radius-pill); }

/* Strip remaining glass blur effects everywhere — light mode shouldn't
   blur translucent surfaces. Dark mode opts back into a subtle blur
   for overlays only via the rule below. */
.ss-topnav,
.ss-subnav,
.ss-user-dd,
.modal, .ss-modal,
.toast, .ss-toast,
.glass {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
html.keep-dark .modal,
html.keep-dark .ss-modal,
html.keep-dark .ss-user-dd {
  background: var(--bg-overlay);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

/* Legacy ambient orb / glow decoration — neutralize */
[class*="ambient"], [class*="orb"], [class*="aurora"] { display: none !important; }

/* ══════════════════════════════════════════════════════════════════════
   Dark mode polish — surface lift + restrained dividers
   ══════════════════════════════════════════════════════════════════════ */
html.keep-dark body {
  background: var(--bg-canvas);
  color: var(--fg-primary);
}
html.keep-dark .ss-topnav,
html.keep-dark .ss-subnav {
  background: var(--bg-canvas);
  border-bottom-color: var(--border-subtle);
}
html.keep-dark .card,
html.keep-dark .stat-card,
html.keep-dark .alert-card,
html.keep-dark .cat-tile,
html.keep-dark .category-tile,
html.keep-dark .category-card,
html.keep-dark #link-network-banner,
html.keep-dark #sec-login .ss-login-card {
  background: var(--bg-surface);
  border-color: var(--border-subtle);
}
html.keep-dark .form-control,
html.keep-dark .ss-login-input,
html.keep-dark .keep-input,
html.keep-dark .keep-select,
html.keep-dark .keep-textarea {
  background: var(--bg-overlay);
  color: var(--fg-primary);
  border-color: var(--border-default);
}
html.keep-dark .form-control:focus,
html.keep-dark .ss-login-input:focus {
  border-color: var(--signal-default);
  box-shadow: 0 0 0 3px var(--signal-focus);
}
html.keep-dark .ss-table tr:hover td { background: var(--bg-overlay); }
html.keep-dark .btn-default {
  background: var(--bg-overlay);
  border-color: var(--border-default);
  color: var(--fg-primary);
}
html.keep-dark .btn-default:hover {
  background: var(--bg-sunken);
  border-color: var(--border-strong);
}
html.keep-dark .lede-sentence,
html.keep-dark .lede-sentence .domain,
html.keep-dark .lede-sentence .num { color: var(--fg-primary); }
html.keep-dark .lede-sentence .domain { color: var(--fg-secondary); }

/* Brand wordmark must read in dark mode */
html.keep-dark .keep-logo,
html.keep-dark #sec-login .ss-login-brand::before { color: var(--fg-primary); }

/* ══════════════════════════════════════════════════════════════════════
   Responsive nav adjustments
   ══════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {
  .ss-topnav { padding: 0 var(--space-5); height: 56px; }
  .ss-topnav-sections { display: none; }
  .ss-subnav { padding: var(--space-3) var(--space-5); overflow-x: auto; flex-wrap: nowrap; }
  .ss-subnav-item { white-space: nowrap; }

  /* Login screen (login/FAQ columns) gets cramped between roughly 580 and
     720px. Force a vertical stack below 768px so both columns get full
     width and aren't squished into ~280px each. */
  #sec-login > div { flex-direction: column; }
  #sec-login > div > div { flex-basis: 100%; max-width: 100%; }
}

/* FAQ chevron: rotate the arrow when its <details> is open. The inline
   transition:transform on the icon makes this animate smoothly. */
details[open] > summary > i.fa-chevron-right { transform: rotate(90deg); }

/* ══════════════════════════════════════════════════════════════════════
   PASS 2 — Dashboard visual polish (May 2026)
   Each block is keyed to the correction number in the brief so the diff
   reads top-to-bottom in the same order as the spec. Overrides are
   scoped to .keep-overview (the dashboard overview tab) where possible
   so other pages keep their existing treatment.
   ══════════════════════════════════════════════════════════════════════ */

/* ── #1 Hero lede ─────────────────────────────────────────────────────
   Pad the lede column generously and re-tune the sentence: the body
   reads in fg-secondary so the numeric/proper-noun spans (which keep
   fg-primary) become the figures the eye lands on. Display face at 36px
   on small screens, 44px once we have the horizontal room. Existing
   .lede-sentence .num rule in keep-design.css line 310 already sets
   font-weight 600 — we re-assert color here so it survives the
   secondary-color base. */
/* ── Rebuild v2 #3a — Band 2 (Lede + Next-action) typography & density
   Hero density per spec: 36×56 left cell, 36×36 right cell.
   Lede sentence shrinks from Fraunces 36-44px billboard to the v1
   editorial sentence: Open Sans 500 / 22px / line-height 1.45 /
   letter-spacing -0.015em / fg-primary / max-width 720, with
   text-wrap: pretty so the sentence wraps gracefully without orphaned
   single words on the last line. Bold spans (.num, <strong>) lift to
   weight 800 (brand-guide ExtraBold). Mono domain (.domain) renders in
   JetBrains Mono 18 / weight 500, ink-black, letter-spacing 0 so it
   sits cleanly inline with the surrounding sans body.
   ───────────────────────────────────────────────────────────────────── */
.keep-overview .keep-overview-lede {
  padding: 36px 56px 32px;
}
.keep-overview .keep-overview-lede .lede-sentence {
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: 22px;
  line-height: 1.45;
  letter-spacing: -0.015em;
  color: var(--fg-primary);
  max-width: 720px;
  text-wrap: pretty;
  margin: 0;
}
.keep-overview .keep-overview-lede .lede-sentence .num,
.keep-overview .keep-overview-lede .lede-sentence strong {
  color: var(--fg-primary);
  font-weight: 800;
}
.keep-overview .keep-overview-lede .lede-sentence .domain {
  font-family: var(--font-mono);
  font-size: 18px;
  font-weight: 500;
  color: var(--fg-primary);
  letter-spacing: 0;
}

/* Right cell — kicker NEXT + body + action link. Hero density at 36×36
   so the cell breathes alongside the lede but reads as the secondary
   surface (narrower padding asymmetry, no max-width). */
.keep-overview .keep-next-action {
  padding: 36px 36px 32px;
}

/* ── #2 Stat numbers ──────────────────────────────────────────────────
   Promote all three numbers to fg-primary (previously only the .active
   tab got the lift). Replace the orange ::after underline with a single
   1px top border on the row — the tabs become an editorial figure strip
   rather than a tabbed control. Stack column-reverse so the number sits
   above its kicker label without touching the HTML order, which the
   lede composer in keep-lede.js still reads via #stat-blocked etc. */
/* ── Rebuild v2 #3a — Band 3A (Stats-as-tabs) typography & density
   Reference density per spec: 20×56 per tab. Kicker on top, value
   below (flex-direction: column, matching HTML order kicker→value).
   Value: Open Sans 800 / 32px / line-height 1.05 / letter-spacing
   -0.03em / tabular-nums. Inactive tabs render the value in
   --fg-secondary so the active tab reads as the loud one; the
   active-state kicker color (--signal) is handled by the canonical
   .kicker rule at the top of this file.

   Pass-2's column-reverse override is dropped — the HTML ships
   kicker-first, value-second, and the spec wants visual order to
   match. The orange ::after underline on the active tab and the
   active-state signal bar (left:56 right:56 bottom:-1) are Band 3
   concerns that land with the chart commit, not here.
   ───────────────────────────────────────────────────────────────────── */
.keep-overview .keep-stat-tabs {
  border-top: 1px solid var(--border-subtle);
}
.keep-overview .keep-stat-tab {
  padding: 20px 56px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  cursor: pointer;
  background: transparent;
  position: relative;
}
/* Rebuild v2 #3e — active stat tab styling.
   bg-canvas wash to lift the active tab off the row; 2px Pumpkin Spice
   bar at left:56 right:56 bottom:-1 crosses the row's bottom border so
   the tab visually ties into the chart below. */
.keep-overview .keep-stat-tab.active { background: var(--bg-canvas); }
.keep-overview .keep-stat-tab.active::after {
  content: "";
  position: absolute;
  left: 56px;
  right: 56px;
  bottom: -1px;
  height: 2px;
  background: var(--signal);
}
.keep-overview .keep-stat-tab .keep-stat-value,
.keep-overview .keep-stat-tab.active .keep-stat-value {
  font-family: var(--font-ui);
  font-weight: 800;
  font-size: 32px;
  line-height: 1.05;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
  color: var(--fg-primary);
  margin: 0;
}
/* Inactive tabs render the value in fg-secondary so the active tab
   reads as the loud one. */
.keep-overview .keep-stat-tab:not(.active) .keep-stat-value {
  color: var(--fg-secondary);
}
@media (max-width: 768px) {
  .keep-overview .keep-stat-tab { padding: 16px 24px; }
  .keep-overview .keep-stat-tab .keep-stat-value,
  .keep-overview .keep-stat-tab.active .keep-stat-value { font-size: 26px; }
}

/* ── #3 Chart canvas styling — JS follow-up required ─────────────────
   Chart.js (chart.umd.min.js loaded in index.html line 31) draws to a
   <canvas> element. Canvas pixels are rasterised from JS-set colors and
   cannot be styled via CSS — there is no underlying SVG/DOM for the
   line, area fill, or axis labels. The five chart-styling asks below
   therefore require an edit to dashboard/js/app.js (out of scope for
   this CSS-only pass — flagged for follow-up):

     · Line stroke (app.js:1256, 1267): set both datasets' borderColor
       to '#E66B2C' (signal-default); the current blue/red lines stop
       reading as "Requests vs Blocked" and become one signal series.
     · Area fill (app.js:1257, 1268): set backgroundColor to a flat
       'rgba(230,107,44,0.06)' (drop makeGradient).
     · Y-axis tick labels (app.js:1318): set ticks:{display:false} on
       scales.y to hide the 1–10 stack entirely.
     · X-axis tick label color (app.js:1309): set ticks.color to
       'rgba(138,138,146,1)' (matches --fg-tertiary).
     · Conversational time formatting (app.js:1303): swap 'HH:mm' →
       'h a' (date-fns "12 AM" → manual map to "12a", "6a" etc. via
       a callback on ticks.callback if needed).

   Once the JS lands, the only CSS-side adjustment needed is keeping
   the canvas background transparent, which keep-design.css:928 already
   sets. The wrapper height (280px inline on index.html line 440) is
   fine. */

/* ── #4 Chart period toggle ──────────────────────────────────────────
   keep-design.css:933-954 already styles .chart-range-btn with
   !important rules, so each override here re-asserts !important at a
   higher specificity (adding .keep-range-toggle to the selector lifts
   specificity from 0,2,0 to 0,3,0 so the cascade still picks the most
   specific !important rule). The active underline is moved off the
   border-bottom and onto a ::after pseudo so it can sit 4px below the
   text baseline — a border-bottom can't be offset. */
.keep-overview .keep-range-toggle { gap: var(--space-1); }  /* 4px */
.keep-overview .keep-range-toggle .chart-range-btn {
  font-family: var(--font-ui) !important;
  font-size: 12px !important;            /* was 11px (keep-design.css:942) */
  font-weight: 500 !important;
  letter-spacing: 0 !important;
  text-transform: none !important;
  padding: 4px 10px !important;          /* was "0 0 2px" (keep-design.css:939) */
  color: var(--fg-tertiary) !important;
  background: transparent !important;
  border: 0 !important;                  /* drops the transparent border-bottom underline */
  border-radius: 0 !important;
  box-shadow: none !important;
  position: relative;
  cursor: pointer;
}
.keep-overview .keep-range-toggle .chart-range-btn:hover {
  color: var(--fg-primary) !important;
}
.keep-overview .keep-range-toggle .chart-range-btn.active {
  color: var(--fg-primary) !important;
}
.keep-overview .keep-range-toggle .chart-range-btn.active::after {
  content: "";
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: -4px;                          /* 4px below text per spec */
  height: 1.5px;
  background: var(--signal-default);
}

/* ══════════════════════════════════════════════════════════════════════
   Rebuild v2 #2.2 — Link-network banner (slim Notice-band)

   Replaces the entire pass-3 hardening block. Pass-3 rules were
   defending the OLD inline-styled HTML shape (3-column flex with an
   icon-tinted-box, a 14/700 title, and a small orange-pill CTA). The
   commit #2.2 HTML rewrites that into a 2-column shape (kicker + body
   on the left, primary + ghost on the right), so the pass-3 positional
   and :has() selectors no longer match any element — and the structural
   rules (background, padding, border-radius) actively conflicted with
   the new flat design. Net effect of the replacement: the old shape is
   gone, so are its defenses; the new shape gets its own rules below.

   The banner is interim — commit #10 removes it entirely once the
   "Not configured" top banner (TODO rebuild-v2 #9.5) takes over.
   The .btn-primary / .btn-ghost styles defined globally above stay
   alive for Band 5 and the rest of the rebuild.
   ══════════════════════════════════════════════════════════════════════ */
#link-network-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 14px 28px;
  background: var(--bg-canvas);
  border-top: 1px solid var(--border-subtle);
  border-bottom: 1px solid var(--border-subtle);
  border-radius: 0;
  box-shadow: none;
  margin: 0 0 14px 0;
}
#link-network-banner .lnb-body {
  flex: 1;
  min-width: 0;
}
#link-network-banner .kicker {
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fg-tertiary);
  margin-bottom: 6px;
}
#link-network-banner .lnb-sentence {
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 1.5;
  color: var(--fg-primary);
  margin: 0;
}
/* The connecting IP — mono, legible against the white-smoke canvas. */
#link-network-banner .lnb-ip {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 500;
  color: var(--fg-primary);
}
/* Tertiary "Want to give this network a name?" link — Fresh Sky, 12px,
   underlined. <details>/<summary> keeps the show-input toggle semantics
   from the original HTML; CSS hides the default disclosure triangle. */
#link-network-banner .lnb-disclosure { margin-top: 6px; }
#link-network-banner .lnb-disclosure-toggle {
  display: inline-block;
  font-family: var(--font-ui);
  font-size: 12px;
  color: var(--fresh-sky);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  cursor: pointer;
  list-style: none;
}
#link-network-banner .lnb-disclosure-toggle::-webkit-details-marker { display: none; }
#link-network-banner .lnb-disclosure-toggle::marker { content: ""; }
#link-network-banner .lnb-label-input {
  margin-top: 8px;
  max-width: 240px;
  font-size: 13px;
}
#link-network-banner .lnb-success {
  margin-top: 8px;
  font-size: 13px;
  color: var(--ink-black);
  font-weight: 600;
}
#link-network-banner .lnb-actions {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-shrink: 0;
}
/* Icon-only ✕ dismiss — square, compact, ghost-style. The .btn-ghost
   default padding (10×20) is overridden here so the icon-only button
   reads as a small square rather than a wide pill. */
#link-network-banner .lnb-dismiss {
  width: 36px;
  height: 36px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* ── #6 Top nav simplification ───────────────────────────────────────
   Hides three nav affordances without touching the HTML structure: the
   theme toggle (light is the only mode we ship), the wrench shortcut
   (it duplicates the user-dropdown route to troubleshooting), and the
   overview subnav (the "Dashboard / Activity Log" row).

   The subnav is shared across all sections (renderSubnav in app.js:124
   rebuilds it per section from NAV[sec].subs), so we can NOT hide it
   globally — that would break Protection / Devices / etc. The :has()
   selector hides it only when the Overview topnav anchor is the active
   one, leaving every other section's subnav untouched. */
#ss-theme-toggle { display: none !important; }

/* The wrench button has no class hook — only inline style + an inner
   <i class="fa-wrench">. :has() picks it out without relying on the
   inline `onclick` or `title` attribute. */
.ss-topnav-right > button:has(> i.fa-wrench) {
  display: none !important;
}

.ss-nav-wrap:has(.ss-topnav-section[data-sec="overview"].active) .ss-subnav,
.ss-nav-wrap:has(.ss-topnav-section[data-sec="devices"].active) .ss-subnav,
.ss-nav-wrap:has(.ss-topnav-section[data-sec="community"].active) .ss-subnav {
  display: none !important;
}

/* Restyle the "Active" status pill as a dot + word. The pill div in
   the HTML contains a .ss-pulse dot and a span — re-style both so the
   composite reads as a green dot followed by sentence-case "Active"
   in fg-secondary, no pill chrome.

   The legacy .ss-pulse comes from blackhole.css with its own sizing /
   animation; we hard-reset to a 6px static green disc. */
.ss-status-pill {
  background: transparent !important;
  border: 0 !important;
  padding: 0 !important;
  font-family: var(--font-ui) !important;
  font-size: 13px !important;
  font-weight: 500 !important;
  letter-spacing: 0 !important;
  text-transform: none !important;
  color: var(--fg-secondary) !important;
  display: inline-flex !important;
  align-items: center !important;
  gap: 6px !important;
}
.ss-status-pill .ss-pulse {
  width: 6px !important;
  height: 6px !important;
  background: var(--status-success-fg) !important;
  border-radius: 50% !important;
  animation: none !important;
  box-shadow: none !important;
}
/* Override the earlier .ok / .warn / .err color shifts — the dot
   carries the state now, not the word. */
.ss-status-pill.ok,
.ss-status-pill.warn,
.ss-status-pill.err { color: var(--fg-secondary) !important; }
.ss-status-pill.warn .ss-pulse { background: var(--status-warning-fg) !important; }
.ss-status-pill.err .ss-pulse  { background: var(--status-danger-fg)  !important; }

/* ── #7 Top Devices panel ────────────────────────────────────────────
   The renderDeviceList helper in app.js:798 emits a 3-cell row:
     <div class="device-row">
       <div class="device-ico"><i class="fa-solid …"></i></div>
       <div class="device-name">…</div>
       <div class="device-count">…</div>
     </div>
   That helper does NOT render the model/sparkline/timestamp the brief
   describes — those only exist on the richer renderDeviceListWithPrivacy
   helper used by the Devices section. Adding them here would require a
   JS edit in app.js (out of scope, flagged for follow-up). What CSS can
   do right now: hide the icon, collapse the grid to name + count, and
   restyle to spec. Scoped to #top-devices-list so the Devices section
   row layout (which uses .device-row with the same class) is untouched. */
#top-devices-list .device-row {
  grid-template-columns: 1fr auto;
  gap: var(--space-4);
  padding: 16px 0;                          /* 16px vertical, 0 horizontal */
  align-items: center;
}
#top-devices-list .device-ico { display: none !important; }
#top-devices-list .device-name {
  font-family: var(--font-ui);
  font-size: 16px;
  font-weight: 600;
  color: var(--fg-primary);
}
#top-devices-list .device-count {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 24px;
  line-height: 1;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
  text-align: right;
  color: var(--fg-primary);
}
#top-devices-list .device-row:hover { background: transparent; }

/* ── #9 Notice panel — drop the cream-yellow tint ────────────────────
   The "New device detected" / block-suggestion / breakage notices all
   render as <div class="alert-card warn"> via app.js:598, :766, :1070.
   Pass-1 keep-overrides.css:389 painted them with status-warning-bg
   (cream-yellow). Pass-2 wants the notice to dissolve into the page:
   bg-canvas with 1px top/bottom hairlines and no rounded card chrome.
   Orange info icon and (when present) "INFO" badge get hidden so the
   notice reads as prose with quiet borders. */
.alert-card.warn {
  background: var(--bg-canvas);
  border: 0;
  border-top: 1px solid var(--border-subtle);
  border-bottom: 1px solid var(--border-subtle);
  border-radius: 0;
  padding: var(--space-5);
}
.alert-card.warn .alert-ico,
.alert-card.warn .alert-ico.warn { display: none; }
.alert-card.warn .alert-badge,
.alert-card.warn .alert-badge.warn { display: none; }

/* The block-suggestion notice ships with "Allow this" / "Keep it
   blocked" buttons (renderSuggestions in app.js:604-605) — NOT the
   "Confirm" / "Not mine" pair the brief describes. Renaming is a copy
   change in JS so we don't touch it here; flagged for HTML follow-up. */

/* ── #12 Stray "library size" number near trust strip ────────────────
   #stat-library is intentionally hidden in HTML (index.html:502 sets
   style="display:none") because the library size isn't part of the v1
   headline composition. But app.js's setText helper (app.js:1541)
   unhides any element when it writes content into it:
       if (el.style.display === 'none' && val) el.style.display = '';
   So the call at app.js:336 — setText('stat-library', ...) — flips the
   inline display back to "" and the raw "600,000"-ish number renders
   bare near the trust footer with no label or styling.
   Forcing display:none via CSS beats the cleared-empty inline style.
   Proper fix is a JS change (don't unhide intentionally-hidden els, or
   stop writing to this id), but this is a CSS-only pass — flagged. */
#stat-library {
  display: none !important;
}

/* ── #11 Title-case sweep ────────────────────────────────────────────
   Bump every .kicker to 0.1em letter-spacing per spec (was 0.08em via
   keep-design.css:262). All section headings on the overview use
   <span class="kicker"> so a single override covers the sweep — there
   are no mixed-case Title-Case strings outside the kicker pattern to
   worry about. */
.kicker, .label-md {
  letter-spacing: 0.1em;
}

/* ── Rebuild v2 #3c — Band 5 (Notice) ────────────────────────────────
   Slim full-width notice band, layered on top of .keep-notice (which
   already gives bg-canvas + bottom hairline). Adds the top hairline,
   tightens horizontal padding to 28px per spec, and lays out a kicker
   row above a flex space-between row holding the body sentence and the
   Confirm / Not-mine button cluster. Show/hide is toggled by
   renderNoticeBand() in app.js — section starts display:none and only
   renders when GET /api/alerts has an un-acknowledged alert. */
.keep-band-notice {
  border-top: 1px solid var(--border-subtle);
  padding: var(--space-5) 28px;
}
.keep-band-notice .keep-block-head {
  margin-bottom: 10px;
}
.keep-band-notice-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
}
.keep-band-notice-body {
  font-family: var(--font-ui);
  font-size: 14px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--fg-primary);
}
.keep-band-notice-actions {
  display: inline-flex;
  gap: 10px;
  flex-shrink: 0;
}

/* ══════════════════════════════════════════════════════════════════════
   Devices page (list view) — second pass
   All styles namespaced under .kd-*. Tokens are canonical (no
   aliases). Font weights stop at 600 because keep-design.css only
   loads Open Sans 400/500/600 — anything heavier renders as a
   browser fallback that doesn't match the loaded face.
   ══════════════════════════════════════════════════════════════════════ */

/* Devices used to live in #sec-devices on visual-updates-v2; in this
   branch the kd-devices block is inlined on the Overview. The full-
   bleed canvas reset moved off ss-section and onto kd-devices itself
   so the inline location still reads as editorial. The huge horizontal
   padding (56px) is dropped here — Overview's container already
   provides the outer spacing. */
.kd-devices {
  background: transparent;
  color: var(--fg-primary);
  font-family: var(--font-ui);
  padding: var(--space-4) 0 var(--space-6);
  margin: 0 auto;
}

/* ── Detail stub (route target for /devices/:id) ────────────────────
   Real detail page is a separate brief; this is just enough UI to
   confirm "you clicked into something" without faking content. */
.kd-detail-stub {
  padding: 18px 0 28px;
  border-bottom: 1px solid var(--border-subtle);
  margin-bottom: 28px;
}
.kd-detail-stub[hidden] { display: none; }
.kd-back-link {
  font-size: 12px;
  font-weight: 600;
  color: var(--fg-secondary);
  text-decoration: none;
  cursor: pointer;
  display: inline-block;
  margin-bottom: 12px;
}
.kd-back-link:hover { color: var(--fg-primary); }
.kd-detail-stub-body {
  margin: 0;
  font-size: 15px;
  color: var(--fg-secondary);
  font-weight: 500;
}
.kd-detail-stub-body #kd-detail-stub-id {
  color: var(--fg-primary);
  font-weight: 600;
}

/* ── New-device surface ──────────────────────────────────────────── */
.kd-new-device {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  padding: var(--space-5) var(--space-6);
  margin-bottom: 28px;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-left: 3px solid var(--signal-default);
  border-radius: 10px;
}
.kd-new-device[hidden] { display: none; }
.kd-new-device-body {
  margin: 0;
  font-size: 14px;
  line-height: 1.55;
  color: var(--fg-primary);
  font-weight: 500;
}
.kd-new-device-body .kd-nd-type { font-weight: 600; }
.kd-nd-seen { color: var(--fg-secondary); font-size: 12px; }
.kd-new-device-actions {
  display: inline-flex;
  gap: 8px;
  flex-shrink: 0;
}

/* ── Time-window control ─────────────────────────────────────────── */
.kd-window-row {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: var(--space-5);
}
.kd-window-label { color: var(--fg-tertiary); margin: 0; }
.kd-window {
  display: inline-flex;
  background: transparent;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-pill);
  padding: 3px;
}
.kd-window-pill {
  appearance: none;
  border: 0;
  background: transparent;
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 600;
  color: var(--fg-secondary);
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  cursor: pointer;
  line-height: 1;
  transition: color var(--duration-fast) var(--ease-standard),
              background-color var(--duration-fast) var(--ease-standard);
}
.kd-window-pill:hover { color: var(--fg-primary); }
.kd-window-pill.active {
  background: var(--fg-primary);
  color: #fff;
}
.kd-window-pill:focus-visible {
  outline: 2px solid var(--signal-focus);
  outline-offset: 2px;
}

/* ── Summary lines ───────────────────────────────────────────────── */
.kd-summary { margin-bottom: 8px; }
.kd-sum-line {
  margin: 0 0 6px;
  font-family: var(--font-ui);
  font-size: 22px;
  line-height: 1.45;
  letter-spacing: -0.015em;
  font-weight: 500;
  color: var(--fg-primary);
  text-wrap: pretty;
}
.kd-sum-line:last-child { margin-bottom: 0; }
/* Numbers use weight 600 (the heaviest loaded weight). Size + color
   carry the data emphasis the brief asks for without reaching for
   an unloaded 700/800. */
.kd-sum-line strong {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--fg-primary);
}
.kd-mid-dot {
  color: var(--fg-tertiary);
  margin: 0 8px;
  font-weight: 400;
}

/* ── As-of timestamp ─────────────────────────────────────────────── */
.kd-asof {
  font-size: 11px;
  color: var(--fg-tertiary);
  margin-top: 6px;
  margin-bottom: var(--space-8);
}

/* ── Error surface ───────────────────────────────────────────────── */
.kd-error {
  margin-bottom: var(--space-8);
  padding: var(--space-3) var(--space-4);
  font-size: 13px;
  color: var(--fg-primary);
}
.kd-error[hidden] { display: none; }
.kd-link {
  color: var(--brand-sky-500);
  text-decoration: underline;
  text-underline-offset: 3px;
  font-weight: 500;
  cursor: pointer;
}
.kd-link:hover { color: var(--brand-sky-700); }

/* ── Controls bar ────────────────────────────────────────────────── */
.kd-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  padding: 14px 0;
  border-top: 1px solid var(--border-subtle);
  border-bottom: 1px solid var(--border-subtle);
  background: var(--bg-canvas);
}
.kd-controls-left {
  display: flex;
  align-items: center;
  gap: 14px;
  flex: 1;
  min-width: 0;
}
.kd-controls-right {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  flex-shrink: 0;
}
.kd-search {
  appearance: none;
  width: 280px;
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 500;
  line-height: 1;
  color: var(--fg-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-pill);
  padding: 9px 14px;
}
.kd-search::placeholder { color: var(--fg-tertiary); font-weight: 400; }
.kd-search:focus {
  outline: none;
  border-color: var(--fg-primary);
  box-shadow: 0 0 0 3px var(--border-subtle);
}
.kd-chips {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.kd-chip {
  appearance: none;
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 600;
  line-height: 1;
  color: var(--fg-secondary);
  background: transparent;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-pill);
  padding: 6px 12px;
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--duration-fast) var(--ease-standard),
              background-color var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard);
}
.kd-chip:hover { color: var(--fg-primary); border-color: var(--border-strong); }
.kd-chip.active {
  color: #fff;
  background: var(--fg-primary);
  border-color: var(--fg-primary);
}
.kd-sort {
  display: inline-flex;
  align-items: center;
  gap: 14px;
}
.kd-sort-tab {
  appearance: none;
  border: 0;
  background: transparent;
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 600;
  color: var(--fg-tertiary);
  padding: 4px 0;
  border-bottom: 1.5px solid transparent;
  cursor: pointer;
  line-height: 1.2;
  transition: color var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard);
}
.kd-sort-tab:hover { color: var(--fg-primary); }
.kd-sort-tab.active {
  color: var(--fg-primary);
  border-bottom-color: var(--fg-primary);
}
.kd-counter {
  font-size: 11px;
  color: var(--fg-tertiary);
  font-variant-numeric: tabular-nums;
}

/* ── Row anatomy ─────────────────────────────────────────────────── */
.kd-list { background: var(--bg-canvas); }
.kd-row {
  position: relative;
  padding: 18px 0;
  border-bottom: 1px solid var(--border-subtle);
  cursor: pointer;
  transition: background-color var(--duration-fast) var(--ease-standard);
}
.kd-row:last-child { border-bottom: 0; }
.kd-row:focus-visible {
  outline: 2px solid var(--signal-focus);
  outline-offset: -2px;
  border-radius: var(--radius-sm);
}
@media (hover: hover) {
  .kd-row:hover { background: var(--bg-surface); }
  /* Reveal the dotted underline on the label only while the row is
     hovered — quiet at rest, discoverable on intent. Without this
     cue, no one finds inline rename. */
  .kd-row:hover .kd-id-label {
    text-decoration: underline dotted var(--fg-tertiary);
    text-underline-offset: 3px;
    cursor: text;
  }
}

.kd-row-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
}
.kd-row-text { min-width: 0; flex: 1; }

.kd-row-identity {
  font-size: 15px;
  line-height: 1.35;
  color: var(--fg-primary);
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
}
.kd-id-label { font-weight: 600; }
.kd-id-meta  { font-weight: 500; color: var(--fg-secondary); }
.kd-id-sep {
  color: var(--fg-tertiary);
  font-weight: 400;
  margin: 0 10px;
}

/* Loud-row dot — prefixed to the identity line on the page's single
   max-blocked row (tie-broken by most recent lastSeen — see JS). */
.kd-row--loud .kd-id-label::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--signal-default);
  margin-right: 10px;
  vertical-align: middle;
  position: relative;
  top: -2px;
}

.kd-row-activity {
  margin-top: 4px;
  font-size: 13px;
  line-height: 1.45;
  color: var(--fg-secondary);
  font-weight: 500;
}
.kd-row-activity strong {
  color: var(--fg-primary);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.kd-row-activity .kd-mid-dot { margin: 0 8px; font-weight: 400; }

/* ── Sparkline ───────────────────────────────────────────────────── */
.kd-spark {
  width: 92px;
  height: 26px;
  flex-shrink: 0;
  /* No padding / background — the SVG stroke is the entire visual
     and inherits its color via currentColor below. Tertiary grey is
     deliberate: orange is reserved for the loud-row dot and
     interactivity. A coloured sparkline would trip the
     "one signal color, one meaning" rule. */
  color: var(--fg-tertiary);
}
.kd-spark svg { display: block; }
.kd-spark svg polyline,
.kd-spark svg line {
  stroke: currentColor;
  fill: none;
}
/* Quiet state: a single hairline along the baseline — neither
   alarming nor decorative. Brief: "renders a flat baseline or
   nothing — never a misleading spike." */
.kd-spark--flat svg line { stroke-width: 1; }

/* ── Inline rename input ─────────────────────────────────────────── */
.kd-id-input {
  appearance: none;
  font-family: var(--font-ui);
  font-size: 15px;
  line-height: 1.35;
  font-weight: 600;
  color: var(--fg-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: 1px 6px;
  margin: -2px -6px;
  min-width: 160px;
  max-width: 320px;
}
.kd-id-input:focus {
  outline: none;
  border-color: var(--fg-primary);
  box-shadow: 0 0 0 3px var(--border-subtle);
}

/* ── Skeletons ───────────────────────────────────────────────────── */
.kd-row--skel { padding: 22px 0; cursor: default; }
.kd-skel {
  height: 12px;
  background: var(--bg-sunken);
  border-radius: var(--radius-sm);
  animation: kd-skel 1.6s ease-in-out infinite;
}
.kd-skel--w1     { width: 36%; margin-bottom: 8px; }
.kd-skel--w2     { width: 58%; height: 10px; }
.kd-skel--spark  { width: 92px; height: 14px; flex-shrink: 0; }
@keyframes kd-skel {
  0%, 100% { opacity: 0.6; }
  50%      { opacity: 1; }
}
.kd-summary.kd-loading .kd-sum-line {
  color: transparent;
  background: var(--bg-sunken);
  border-radius: var(--radius-sm);
  height: 28px;
  margin-bottom: 8px;
  animation: kd-skel 1.6s ease-in-out infinite;
}
.kd-summary.kd-loading .kd-sum-line strong,
.kd-summary.kd-loading .kd-sum-line span { visibility: hidden; }

/* ── Empty + search-empty states ─────────────────────────────────── */
.kd-empty {
  padding: 80px 0;
  max-width: 720px;
  text-align: left;
}
.kd-empty-title {
  font-family: var(--font-ui);
  font-size: 24px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--fg-primary);
  margin: 0 0 12px;
}
.kd-empty-body {
  font-size: 15px;
  line-height: 1.5;
  color: var(--fg-secondary);
  margin: 0 0 28px;
}
.kd-empty-body strong {
  color: var(--fg-primary);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.kd-search-empty {
  padding: 40px 0;
  font-size: 14px;
  color: var(--fg-secondary);
}

/* ── Page-local buttons ──────────────────────────────────────────── */
.kd-btn {
  appearance: none;
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
  padding: 9px 16px;
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  transition: background-color var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.kd-btn--ink {
  background: var(--fg-primary);
  color: #fff;
  border-color: var(--fg-primary);
}
.kd-btn--ink:hover { background: #000; }
.kd-btn--ghost {
  background: transparent;
  color: var(--fg-primary);
  border-color: var(--border-default);
}
.kd-btn--ghost:hover { border-color: var(--border-strong); background: var(--bg-sunken); }

/* ── Undo toast ──────────────────────────────────────────────────── */
.kd-toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--fg-primary);
  color: #fff;
  border-radius: var(--radius-pill);
  padding: 10px 18px;
  display: inline-flex;
  align-items: center;
  gap: 14px;
  font-size: 13px;
  font-weight: 500;
  box-shadow: var(--shadow-lg);
  z-index: 9999;
  animation: kd-toast-in var(--duration-base) var(--ease-emphasized);
}
.kd-toast[hidden] { display: none; }
.kd-toast-action {
  appearance: none;
  border: 0;
  background: transparent;
  color: #fff;
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 600;
  padding: 2px 4px;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.kd-toast-action:hover { opacity: 0.85; }
@keyframes kd-toast-in {
  from { opacity: 0; transform: translateX(-50%) translateY(8px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

/* ── Mobile reflow ───────────────────────────────────────────────── */
@media (max-width: 1024px) {
  .kd-devices { padding: var(--space-6) 28px 64px; }
  .kd-search  { width: 220px; }
}
@media (max-width: 768px) {
  .kd-devices { padding: var(--space-5) 20px 56px; }
  .kd-sum-line { font-size: 17px; line-height: 1.4; }
  .kd-asof { margin-bottom: var(--space-6); }

  .kd-window-pill { padding: 5px 11px; font-size: 11px; }
  .kd-window-label { font-size: 10px; }

  .kd-new-device {
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;
    padding: 14px 16px;
  }
  .kd-new-device-actions { width: 100%; }
  .kd-new-device-actions .kd-btn { flex: 1; justify-content: center; }

  .kd-controls {
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
  }
  .kd-controls-left {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
  }
  .kd-controls-right { justify-content: space-between; }
  .kd-search { width: 100%; }

  /* Filter chips become a horizontally-scrolling track on mobile. */
  .kd-chips {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    -ms-overflow-style: none;
    padding-bottom: 2px;
  }
  .kd-chips::-webkit-scrollbar { display: none; }
  .kd-chip { flex-shrink: 0; }

  .kd-row { padding: 16px 0; }
  .kd-row-identity { font-size: 14px; }
  .kd-row-activity { font-size: 12px; }

  /* Sparkline shrinks; the numeric counts on line 2 remain the
     canonical signal. No hover affordance on touch — the dotted
     underline cue only makes sense with a cursor. Rename happens on
     the detail page (separate brief). */
  .kd-spark { width: 64px; height: 22px; }
}
@media (max-width: 480px) {
  .kd-devices { padding: var(--space-5) 16px 48px; }
  .kd-sum-line { font-size: 16px; }
  .kd-spark { display: none; }
}

/* ═════════════════════════════════════════════════════════════════════
   PROTECTION → ACTIVITY sub-page
   Domain-axis live activity. Hero density for the lede + top-blocked;
   reference density for the feed below. One signal color (orange) =
   interactivity. No severity colors. JetBrains Mono restricted to
   domain + count cells.
   ═════════════════════════════════════════════════════════════════════ */
.kp-activity {
  background: var(--bg-canvas);
  color: var(--fg-primary);
  font-family: var(--font-ui);
  padding: var(--space-8) 56px 80px;
  max-width: 1080px;
  margin: 0 auto;
}

/* ── Window control + lede ───────────────────────────────────────── */
.kp-activity-head {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  margin-bottom: var(--space-10);
}

/* .kp-window and .kp-window-pill are aliased to the Devices style
   (.kd-window / .kd-window-pill) so Protection (Activity), Community,
   and Devices all render the same window control — outlined pill row,
   inactive pills are quiet --fg-secondary, the active pill is filled
   ink-black with white text. .is-active mirrors .active so either JS
   hook (Activity/Community toggle .is-active, Devices toggles .active)
   wins regardless of cascade order. */
.kp-window {
  display: inline-flex;
  background: transparent;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-pill);
  padding: 3px;
  align-self: flex-start;
}
.kp-window-pill {
  appearance: none;
  border: 0;
  background: transparent;
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 600;
  color: var(--fg-secondary);
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  cursor: pointer;
  line-height: 1;
  transition: color 120ms ease, background-color 120ms ease;
}
.kp-window-pill:hover { color: var(--fg-primary); }
.kp-window-pill.is-active,
.kp-window-pill.active {
  background: var(--fg-primary);
  color: #fff;
  box-shadow: none;
}

.kp-lede {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 28px;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--fg-primary);
  margin: 0;
  max-width: 720px;
}
.kp-lede-line { display: inline; }
.kp-lede-num {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.kp-lede-asof {
  display: block;
  margin-top: var(--space-3);
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--fg-tertiary);
}
.kp-dim { opacity: 0.45; transition: opacity 180ms ease; }

.kp-error {
  font-family: var(--font-ui);
  font-size: 13px;
  color: var(--fg-secondary);
}
.kp-error-retry { color: var(--signal-default); text-decoration: underline; }

/* ── Sections (top-blocked / feed) ───────────────────────────────── */
.kp-block { margin-bottom: var(--space-12); }
.kp-block:last-of-type { margin-bottom: var(--space-8); }

.kp-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-4);
}
.kp-kicker {
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-tertiary);
}

/* ── Top blocked list ────────────────────────────────────────────── */
.kp-topblocked {
  list-style: none;
  margin: 0;
  padding: 0;
  background: var(--bg-surface);
  border-radius: var(--radius-lg);
  border: 1px solid color-mix(in oklab, var(--fg-tertiary) 15%, transparent);
  overflow: hidden;
}
.kp-tb-row {
  display: grid;
  grid-template-columns: 14px minmax(0,1fr) 88px 180px 110px auto;
  align-items: center;
  gap: var(--space-4);
  padding: 14px 20px;
  border-top: 1px solid color-mix(in oklab, var(--fg-tertiary) 10%, transparent);
  position: relative;
  min-height: 52px;
}
.kp-tb-row:first-child { border-top: 0; }
.kp-tb-row:hover { background: var(--bg-sunken); }
.kp-tb-empty {
  padding: 28px 20px;
  text-align: center;
  color: var(--fg-tertiary);
  font-size: 13px;
}

.kp-tb-dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--signal-default);
  display: inline-block;
}
.kp-tb-dot--empty { background: transparent; }

.kp-tb-domain {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--fg-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.kp-tb-count {
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--fg-primary);
  text-align: right;
}
.kp-tb-cat {
  font-size: 12px;
  color: var(--fg-secondary);
}
.kp-tb-dev {
  font-size: 12px;
  color: var(--fg-tertiary);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.kp-topblocked-foot {
  margin-top: var(--space-3);
  display: flex;
  justify-content: flex-end;
}

.kp-textbtn {
  appearance: none;
  background: transparent;
  border: 0;
  font: 500 12px/1 var(--font-ui);
  letter-spacing: 0.02em;
  color: var(--signal-default);
  cursor: pointer;
  padding: 4px 6px;
  border-radius: var(--radius-sm);
}
.kp-textbtn:hover { background: color-mix(in oklab, var(--signal-default) 8%, transparent); }
.kp-textbtn[hidden] { display: none; }

/* ── Row actions (Allow / Block) ─────────────────────────────────── */
.kp-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  opacity: 0;
  transform: translateX(2px);
  transition: opacity 140ms ease, transform 140ms ease;
}
.kp-tb-row:hover .kp-actions,
.kp-feed-row:hover .kp-actions,
.kp-actions:focus-within {
  opacity: 1;
  transform: translateX(0);
}
.kp-action {
  appearance: none;
  background: transparent;
  border: 1px solid color-mix(in oklab, var(--signal-default) 50%, transparent);
  color: var(--signal-default);
  font: 500 11px/1 var(--font-ui);
  letter-spacing: 0.02em;
  padding: 5px 10px;
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
}
.kp-action:hover {
  background: var(--signal-default);
  color: var(--bg-surface);
}
.kp-action:disabled { opacity: 0.5; cursor: default; }
.kp-ruled {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--fg-tertiary);
}

/* ── Live feed ───────────────────────────────────────────────────── */
.kp-block--feed { margin-bottom: var(--space-8); }

.kp-feed-head { align-items: center; }
.kp-feed-kicker {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
.kp-live-dot {
  width: 6px; height: 6px;
  background: var(--signal-default);
  border-radius: 50%;
  display: inline-block;
  animation: kp-pulse 2.4s ease-in-out infinite;
}
.kp-feed-label { color: var(--fg-tertiary); }

.kp-feed-controls {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
}
.kp-segctl {
  display: inline-flex;
  gap: 2px;
  background: var(--bg-sunken);
  padding: 3px;
  border-radius: var(--radius-pill);
}
.kp-seg-pill {
  appearance: none;
  border: 0;
  background: transparent;
  font: 500 11px/1 var(--font-ui);
  letter-spacing: 0.02em;
  color: var(--fg-tertiary);
  padding: 6px 11px;
  border-radius: var(--radius-pill);
  cursor: pointer;
}
.kp-seg-pill:hover { color: var(--fg-secondary); }
.kp-seg-pill.is-active {
  background: var(--bg-surface);
  color: var(--fg-primary);
  box-shadow: 0 1px 2px rgba(0,0,0,0.04);
}
.kp-pausebtn.is-paused {
  color: var(--fg-primary);
  background: color-mix(in oklab, var(--signal-default) 12%, transparent);
}

.kp-feed {
  background: var(--bg-surface);
  border-radius: var(--radius-lg);
  border: 1px solid color-mix(in oklab, var(--fg-tertiary) 15%, transparent);
  overflow: hidden;
}
.kp-feed-empty {
  padding: 36px 20px;
  text-align: center;
  color: var(--fg-tertiary);
  font-size: 13px;
}

.kp-feed-row {
  display: grid;
  grid-template-columns: minmax(0,1fr) auto;
  gap: var(--space-2) var(--space-4);
  padding: 9px 18px;
  border-top: 1px solid color-mix(in oklab, var(--fg-tertiary) 8%, transparent);
}
.kp-feed-row:first-child { border-top: 0; }
.kp-feed-row:hover { background: var(--bg-sunken); }

.kp-feed-main {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-4);
  min-width: 0;
  grid-column: 1 / -1;
}
.kp-feed-domain {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--fg-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.kp-feed-time {
  font-family: var(--font-ui);
  font-size: 11px;
  color: var(--fg-tertiary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.kp-feed-meta {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  grid-column: 1 / -1;
  min-height: 18px;
}
.kp-feed-meta-text {
  font-size: 12px;
  color: var(--fg-secondary);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
}
.kp-feed-count {
  font-family: var(--font-mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--fg-tertiary);
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  background: var(--bg-sunken);
}

/* Blocked vs allowed: differentiated by weight + position, never color.
   Blocked rows carry slightly heavier domain weight; allowed rows sit
   regular. Time stays right-aligned for both — domain remains the spine. */
.kp-feed-row--blocked .kp-feed-domain { font-weight: 500; }
.kp-feed-row--allowed .kp-feed-domain { font-weight: 400; color: var(--fg-secondary); }

.kp-feed-resume {
  appearance: none;
  display: block;
  width: 100%;
  margin-top: var(--space-3);
  padding: 10px 14px;
  background: color-mix(in oklab, var(--signal-default) 10%, var(--bg-surface));
  border: 1px solid color-mix(in oklab, var(--signal-default) 30%, transparent);
  border-radius: var(--radius-lg);
  color: var(--signal-default);
  font: 500 12px/1 var(--font-ui);
  cursor: pointer;
}
.kp-feed-resume[hidden] { display: none; }

/* ── Trust footer ────────────────────────────────────────────────── */
.kp-trust {
  margin: var(--space-8) 0 0;
  font-size: 12px;
  color: var(--fg-tertiary);
  text-align: center;
}

/* ── Motion (Arrive + Bump + Pulse) ──────────────────────────────── */
@keyframes kp-arrive {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes kp-bump {
  0%   { background-color: color-mix(in oklab, var(--signal-default) 8%, transparent); }
  100% { background-color: transparent; }
}
@keyframes kp-pulse {
  0%, 100% { opacity: 0.6; transform: scale(1); }
  50%      { opacity: 1;   transform: scale(1.25); }
}
.kp-arrive { animation: kp-arrive 200ms cubic-bezier(0.2, 0, 0, 1) both; }
.kp-bump   { animation: kp-bump 600ms ease-out both; }

/* ── Skeletons ───────────────────────────────────────────────────── */
.kp-skeleton {
  display: inline-block;
  background: linear-gradient(90deg,
    color-mix(in oklab, var(--fg-tertiary) 10%, transparent) 0%,
    color-mix(in oklab, var(--fg-tertiary) 20%, transparent) 50%,
    color-mix(in oklab, var(--fg-tertiary) 10%, transparent) 100%);
  background-size: 200% 100%;
  height: 12px;
  width: 60%;
  border-radius: 3px;
  animation: kp-shimmer 1.6s ease-in-out infinite;
}
.kp-skeleton--lede { height: 22px; width: 80%; }
.kp-skel-row .kp-skeleton { width: 70%; height: 14px; }
.kp-skel-row { padding: 14px 20px; }
@keyframes kp-shimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}

/* ── Reduced motion ──────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .kp-arrive, .kp-bump, .kp-live-dot, .kp-skeleton { animation: none; }
  .kp-actions { transition: none; }
}

/* ── Mobile ──────────────────────────────────────────────────────── */
@media (max-width: 900px) {
  .kp-activity { padding: var(--space-6) 28px 64px; }
  .kp-tb-row {
    grid-template-columns: 14px minmax(0,1fr) 70px auto;
    gap: var(--space-3);
  }
  .kp-tb-cat, .kp-tb-dev { display: none; }
  .kp-lede { font-size: 24px; }
}
@media (max-width: 600px) {
  .kp-activity { padding: var(--space-5) 16px 48px; }
  .kp-lede { font-size: 21px; }
  .kp-feed-controls { gap: var(--space-3); }
  .kp-seg-pill { padding: 6px 9px; }
  .kp-actions { opacity: 1; transform: none; } /* no hover on touch */
  .kp-feed-row { padding: 8px 14px; }
  .kp-tb-row { padding: 12px 14px; }
}

/* ═════════════════════════════════════════════════════════════════════
   COMMUNITY page — top-level tab (sibling of Dashboard, Devices,
   Protection). Two surfaces, nothing else: a Fraunces lead-time lede
   and a real-detections list. Read-only. No severity colors, no scores,
   no gauges; type is plain text. Reuses .kp-activity-head / .kp-window
   / .kp-kicker / .kp-block / .kp-skeleton / .kp-trust for chrome parity.
   ═════════════════════════════════════════════════════════════════════ */
.kc-community {
  background: var(--bg-canvas);
  color: var(--fg-primary);
  font-family: var(--font-ui);
  /* Padding + max-width match .kd-devices so Devices and Community
     (the two no-subnav pages) align in both width and top spacing. */
  padding: var(--space-8) 56px 80px;
  max-width: 1280px;
  margin: 0 auto;
}

/* The lede shares typographic role with .kp-lede; we redeclare instead
   of relying on inheritance so the markup classes stay independent and
   the page reads cleanly without Activity loaded. */
.kc-lede {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 28px;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--fg-primary);
  margin: 0;
  max-width: 760px;
}
.kc-lede-line { display: inline; }
.kc-lede-num {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.kc-lede-asof {
  display: block;
  margin-top: var(--space-3);
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--fg-tertiary);
}

/* "Learning" pill sits next to the kicker when the detections list is
   empty. Neutral grey — never red, never orange, never severity. */
.kc-learning-pill {
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--fg-tertiary);
  background: var(--bg-sunken);
  padding: 3px 9px;
  border-radius: var(--radius-pill);
}
.kc-learning-pill[hidden] { display: none; }

/* Detections list. Domain mono; type, first-seen, reach in text only.
   No icons in rows — specificity carries the meaning. */
.kc-detections {
  list-style: none;
  margin: 0;
  padding: 0;
  background: var(--bg-surface);
  border-radius: var(--radius-lg);
  border: 1px solid color-mix(in oklab, var(--fg-tertiary) 15%, transparent);
  overflow: hidden;
}
.kc-row {
  display: grid;
  grid-template-columns: minmax(0,1fr) 88px 1fr auto;
  align-items: center;
  gap: var(--space-4);
  padding: 14px 20px;
  border-top: 1px solid color-mix(in oklab, var(--fg-tertiary) 10%, transparent);
  min-height: 52px;
}
.kc-row:first-child { border-top: 0; }
.kc-row-domain {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--fg-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.kc-row-type {
  font-size: 12px;
  color: var(--fg-secondary);
}
.kc-row-when {
  font-size: 12px;
  color: var(--fg-tertiary);
}
.kc-row-reach {
  font-size: 12px;
  color: var(--fg-tertiary);
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.kc-empty {
  padding: 28px 24px;
  text-align: center;
  color: var(--fg-tertiary);
  font-size: 13px;
  line-height: 1.55;
}

/* ── Mobile — Community ──────────────────────────────────────────── */
@media (max-width: 900px) {
  .kc-community { padding: var(--space-6) 28px 64px; }
  .kc-lede { font-size: 24px; }
  .kc-row {
    grid-template-columns: minmax(0,1fr) auto;
    row-gap: var(--space-2);
  }
  .kc-row-domain { grid-column: 1 / 2; }
  .kc-row-type   { grid-column: 2 / 3; text-align: right; }
  .kc-row-when   { grid-column: 1 / 2; }
  .kc-row-reach  { grid-column: 2 / 3; }
}
@media (max-width: 600px) {
  .kc-community { padding: var(--space-5) 16px 48px; }
  .kc-lede { font-size: 21px; }
}

/* ═════════════════════════════════════════════════════════════════════
   User dropdown — simplified per brief. Email-only header, four items
   (Manual setup · Setup wizard · Settings · Sign out). Sentence case,
   canonical tokens, weights capped at 600.
   ═════════════════════════════════════════════════════════════════════ */
.ss-user-dropdown .ss-dd-header {
  padding: 14px 16px 10px;
  border-bottom: 1px solid var(--border-subtle);
}
.ss-user-dropdown .ss-dd-email {
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 600;
  color: var(--fg-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ss-user-dropdown a {
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 500;
  color: var(--fg-primary);
}
.ss-user-dropdown a:hover {
  background: var(--bg-sunken);
  color: var(--fg-primary);
}
.ss-user-dropdown a i {
  color: var(--fg-tertiary);
}
.ss-user-dropdown a:hover i { color: var(--fg-secondary); }
.ss-user-dropdown a[data-action="signout"] {
  /* Plain destructive-leaning, not red — per brief. */
  color: var(--fg-secondary);
}
.ss-user-dropdown a[data-action="signout"]:hover {
  color: var(--fg-primary);
}

/* ═════════════════════════════════════════════════════════════════════
   Settings — one page, anchored sections (Account · Preferences ·
   Billing · Networks). Sub-tabs hidden (NAV.settings.subs = []); the
   in-page jump nav at the top is the only sub-navigation.
   ═════════════════════════════════════════════════════════════════════ */
.kx-settings {
  background: var(--bg-canvas);
  color: var(--fg-primary);
  font-family: var(--font-ui);
  padding: var(--space-6) 0 var(--space-12);
  max-width: 880px;
  margin: 0 auto;
}
.kx-settings-head {
  margin-bottom: var(--space-8);
}
.kx-settings-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 28px;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--fg-primary);
  margin: 0 0 var(--space-4);
}
.kx-settings-nav {
  display: flex;
  gap: var(--space-5);
  flex-wrap: wrap;
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--border-subtle);
}
.kx-jump {
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--fg-tertiary);
  text-decoration: none;
  padding: 4px 2px;
  border-bottom: 2px solid transparent;
  transition: color 120ms ease, border-color 120ms ease;
}
.kx-jump:hover { color: var(--fg-primary); border-bottom-color: var(--signal-default); }

.kx-section { margin-top: var(--space-8); scroll-margin-top: 80px; }
.kx-section-h {
  font-family: var(--font-ui);
  font-size: 18px;
  font-weight: 600;
  color: var(--fg-primary);
  margin: 0 0 var(--space-4);
}

.kx-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg, 10px);
  padding: var(--space-5) var(--space-6);
  margin-bottom: var(--space-4);
}
.kx-card--danger {
  border-color: color-mix(in oklab, var(--status-danger-fg, #B73824) 25%, transparent);
}
.kx-card-h {
  font-family: var(--font-ui);
  font-size: 14px;
  font-weight: 600;
  color: var(--fg-primary);
  margin: 0 0 var(--space-3);
}
.kx-row { margin-bottom: var(--space-4); }
.kx-row:last-child { margin-bottom: 0; }
.kx-label {
  display: block;
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 500;
  color: var(--fg-secondary);
  margin-bottom: 4px;
}
.kx-input { max-width: 420px; }
.kx-mono { font-family: var(--font-mono); }
.kx-copy-row { display: flex; gap: 8px; max-width: 420px; }
.kx-copy-row .kx-input { flex: 1; max-width: none; }
.kx-hint {
  font-family: var(--font-ui);
  font-size: 12px;
  color: var(--fg-tertiary);
  margin: 6px 0 0;
  line-height: 1.5;
}
.kx-form {
  display: grid;
  gap: 10px;
  max-width: 380px;
}
.kx-form-error {
  font-family: var(--font-ui);
  font-size: 13px;
  color: var(--status-danger-fg, #B73824);
}
.kx-danger-summary {
  cursor: pointer;
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 500;
  color: var(--status-danger-fg, #B73824);
}

/* Preferences — header strip + toggle rows inside one card. */
.kx-prefs { padding: 0; }
.kx-prefs-head {
  padding: 14px var(--space-6);
  border-bottom: 1px solid var(--border-subtle);
}
.kx-prefs-h { margin: 0; }
.kx-prefs-list { display: flex; flex-direction: column; }
.kx-pref {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  padding: 14px var(--space-6);
  border-top: 1px solid var(--border-subtle);
}
.kx-prefs-list > .kx-pref:first-child { border-top: 0; }
.kx-pref-text { flex: 1; min-width: 0; }
.kx-pref-name {
  font-family: var(--font-ui);
  font-size: 14px;
  font-weight: 600;
  color: var(--fg-primary);
}
.kx-pref-desc {
  font-family: var(--font-ui);
  font-size: 12px;
  color: var(--fg-secondary);
  margin-top: 2px;
  line-height: 1.5;
}

/* Blocklists card — sub-label in the header (e.g. "3 blocklists on") and the
   subtle saved/error indicator under the toggle list. */
.kx-prefs-sub {
  font-family: var(--font-ui);
  font-size: 12px;
  color: var(--fg-tertiary);
}
.kx-save-state {
  font-family: var(--font-ui);
  font-size: 12px;
  color: var(--fg-tertiary);
  min-height: 16px;
  padding: 0 var(--space-6) 12px;
  transition: opacity 0.2s;
}

/* "Coming soon" placeholder row (Keep Community Blocklist): visually muted,
   non-interactive toggle, badge. The row itself is a button that explains the
   feature isn't available yet. */
.kx-pref--soon { cursor: pointer; }
.kx-pref--soon .kx-pref-name,
.kx-pref--soon .kx-pref-desc { color: var(--fg-tertiary); }
.kx-soon-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 999px;
  font-family: var(--font-ui);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--pumpkin-spice, #fa8334);
  background: color-mix(in srgb, var(--pumpkin-spice, #fa8334) 14%, transparent);
  vertical-align: middle;
}
/* Muted, non-interactive switch for the placeholder. */
.ss-switch--soon { opacity: 0.45; pointer-events: none; }

/* Billing — current plan row + upgrade affordance. */
.kx-billing-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

/* Networks — connected-from card + linked-networks table. */
.kx-kicker {
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-tertiary);
  margin-bottom: 6px;
}
.kx-net-current { padding: 0; }
.kx-net-current-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
}
.kx-net-ip {
  font-size: 18px;
  font-weight: 600;
  color: var(--fg-primary);
}
.kx-link {
  color: var(--signal-default);
  text-decoration: underline;
}
.kx-link:hover { color: var(--signal-default); opacity: 0.85; }

/* ── Mobile ──────────────────────────────────────────────────────── */
@media (max-width: 900px) {
  .kx-settings { padding: var(--space-5) 16px 64px; }
  .kx-settings-title { font-size: 24px; }
}
@media (max-width: 600px) {
  .kx-billing-row { flex-direction: column; align-items: flex-start; }
  .kx-pref { flex-direction: row; }
}

/* ═════════════════════════════════════════════════════════════════════
   Manual setup — address cards (IPv6 / IPv4 / DoH / DoT / Link).
   Replaces the legacy green/blue tinted boxes with canonical kx-card
   chrome. No severity colors — Recommended/Fallback live as quiet
   tertiary kicker tags next to the title.
   ═════════════════════════════════════════════════════════════════════ */
.kx-addr {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.kx-addr-label {
  display: inline-flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
}
.kx-addr-title {
  font-family: var(--font-ui);
  font-size: 14px;
  font-weight: 600;
  color: var(--fg-primary);
}
.kx-addr-tag {
  font-family: var(--font-ui);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--signal-default);
}
.kx-addr-tag--muted { color: var(--fg-tertiary); }
.kx-addr .kx-hint { margin: 0; }
.kx-addr--primary { border-color: color-mix(in oklab, var(--signal-default) 24%, var(--border-subtle)); }

.kx-guide { color: var(--fg-secondary); font-size: 13px; }
.kx-guide ol { padding-left: 18px; line-height: 1.7; margin: 0; }
.kx-guide ol li + li { margin-top: 6px; }

/* ═════════════════════════════════════════════════════════════════════
   Custom Rules — kicker headers, mono domains, plain-text actions,
   quiet edit/delete textbtns. Replaces the legacy .card + .sev pill
   styling so this page reads in the same voice as Activity and
   Settings.
   ═════════════════════════════════════════════════════════════════════ */
.kr-rules { /* extends .kp-activity wrapper — no overrides yet */ }
.kr-form-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg, 10px);
  padding: var(--space-5) var(--space-6);
}
.kr-form {
  display: grid;
  grid-template-columns: 1fr 180px auto;
  gap: 12px;
  align-items: end;
}
.kr-form-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.kr-form-field--grow { min-width: 220px; }
.kr-label {
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 500;
  color: var(--fg-secondary);
}
.kr-input { width: 100%; }
.kr-form-actions {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.kr-submit { white-space: nowrap; }
.kr-hint {
  font-family: var(--font-ui);
  font-size: 12px;
  color: var(--fg-tertiary);
  line-height: 1.55;
  margin: var(--space-4) 0 0;
}

.kr-list-actions {
  display: inline-flex;
  gap: 4px;
}
.kr-count {
  margin-left: 8px;
  color: var(--fg-tertiary);
  font-variant-numeric: tabular-nums;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
}
.kr-table-wrap {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg, 10px);
  overflow: hidden;
}
.kr-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-ui);
  font-size: 13px;
}
.kr-table thead th {
  text-align: left;
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-tertiary);
  padding: 12px 20px;
  border-bottom: 1px solid var(--border-subtle);
  background: transparent;
}
.kr-table .kr-th-controls { text-align: right; }
.kr-table tbody td {
  padding: 14px 20px;
  border-top: 1px solid color-mix(in oklab, var(--fg-tertiary) 10%, transparent);
  color: var(--fg-primary);
  vertical-align: middle;
}
.kr-table tbody tr:first-child td { border-top: 0; }
.kr-table tbody tr:hover td { background: var(--bg-sunken); }
.kr-td-domain {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--fg-primary);
  max-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.kr-td-action { color: var(--fg-secondary); font-size: 13px; white-space: nowrap; }
.kr-td-added { color: var(--fg-tertiary); font-size: 12px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.kr-td-controls { text-align: right; white-space: nowrap; }
.kr-row-btn { margin-left: 4px; }
.kr-row-btn--danger { color: var(--status-danger-fg, #B73824); }
.kr-row-btn--danger:hover {
  background: color-mix(in oklab, var(--status-danger-fg, #B73824) 8%, transparent);
}

.kr-more {
  text-align: center;
  padding: 14px 20px;
  border-top: 1px solid var(--border-subtle);
}
.kr-more-meta { color: var(--fg-tertiary); font-size: 12px; margin-right: 10px; }

.kr-empty {
  padding: 28px 24px;
  text-align: center;
  color: var(--fg-tertiary);
  font-size: 13px;
  line-height: 1.55;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg, 10px);
}

/* Mobile — form stacks, table action column narrows. */
@media (max-width: 700px) {
  .kr-form { grid-template-columns: 1fr; }
  .kr-form-actions { justify-content: flex-start; }
  .kr-table thead th, .kr-table tbody td { padding: 10px 14px; }
  .kr-td-added { display: none; }
}
