/*
 * SPDX-FileCopyrightText: 2026 Human-Focused Security OÜ
 * SPDX-License-Identifier: LicenseRef-Limen-Proprietary
 */

/* Limen fleet console — design system v3 tokens (design-system.html PALETTES).
   Color law: green=affirmative/ALLOW only · action-blue=interactive · brass=brand
   chrome · amber=attention · red=negative. Never color alone (label + dot). */

:root {
  /* Light is the bare-:root default; dark overrides live under [data-theme]. */
  --desk: #E7EBEF; --bg: #F6F8FA; --surface: #FFFFFF; --surface2: #EDF1F5;
  --border: #D9E1E8; --text: #17222D; --dim: #56697B;
  --accent: #087F56; --accent-ink: #FFFFFF; --brand: #8A5E1C; --brand-ink: #FFFFFF;
  --deny: #C23434; --deny-ink: #FFFFFF; --warn: #96650F; --warn-ink: #FFFFFF;
  --action: #1273B8; --action-ink: #FFFFFF;
  --allow-bg: rgba(8,127,86,0.10); --deny-bg: rgba(194,52,52,0.09);
  --warn-bg: rgba(150,101,15,0.10); --action-bg: rgba(18,115,184,0.09);
  --brand-bg: rgba(138,94,28,0.12);
  --shadow: 0 8px 30px rgba(20,30,45,0.12);
  --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", "IBM Plex Mono", Menlo, Consolas, monospace;
}

/* Dark is the design default. We ship it as an explicit [data-theme="dark"] set
   by the app on first load, and also honor the OS preference when unset. */
:root[data-theme="dark"],
:root[data-theme="oled"] {
  --desk: #05080C; --bg: #0B1118; --surface: #121A24; --surface2: #1A2530;
  --border: #233140; --text: #E7EEF5; --dim: #8296A8;
  --accent: #3DDC97; --accent-ink: #052A1B; --brand: #CBA25E; --brand-ink: #201603;
  --deny: #FF6B6B; --deny-ink: #2A0A0A; --warn: #FFB454; --warn-ink: #1A1102;
  --action: #5BB8FF; --action-ink: #06253D;
  --allow-bg: rgba(61,220,151,0.12); --deny-bg: rgba(255,107,107,0.12);
  --warn-bg: rgba(255,180,84,0.12); --action-bg: rgba(91,184,255,0.12);
  --brand-bg: rgba(203,162,94,0.14);
  --shadow: 0 8px 30px rgba(0,0,0,0.5);
}
:root[data-theme="oled"] {
  --desk: #000; --bg: #000; --surface: #0A0E13; --surface2: #141C24; --border: #232F3B;
}

/* Honor the OS preference when the operator has made no explicit choice. Dark is
   the design default; an explicit [data-theme] (set by the toggle) always wins. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --desk: #05080C; --bg: #0B1118; --surface: #121A24; --surface2: #1A2530;
    --border: #233140; --text: #E7EEF5; --dim: #8296A8;
    --accent: #3DDC97; --accent-ink: #052A1B; --brand: #CBA25E; --brand-ink: #201603;
    --deny: #FF6B6B; --deny-ink: #2A0A0A; --warn: #FFB454; --warn-ink: #1A1102;
    --action: #5BB8FF; --action-ink: #06253D;
    --allow-bg: rgba(61,220,151,0.12); --deny-bg: rgba(255,107,107,0.12);
    --warn-bg: rgba(255,180,84,0.12); --action-bg: rgba(91,184,255,0.12);
    --brand-bg: rgba(203,162,94,0.14);
    --shadow: 0 8px 30px rgba(0,0,0,0.5);
  }
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0; background: var(--desk); color: var(--text);
  font-family: var(--sans); font-size: 14px; line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--action); text-decoration: none; }
a:hover { text-decoration: underline; }
h1, h2, h3, h4 { margin: 0; font-weight: 600; }
code, .mono { font-family: var(--mono); }
button { font-family: inherit; }

/* ---------- App shell ---------- */
.app { display: grid; grid-template-columns: 232px 1fr; min-height: 100vh; }
/* Bare screens (sign-in, accept-invite) replace the shell entirely: plain block flow so
   .authwrap gets the full viewport instead of the nav column. */
.bare { display: block; min-height: 100vh; }
.sidebar {
  background: var(--surface); border-right: 1px solid var(--border);
  display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh;
}
/* ---------- The lockup (docs/HANDOFF-identity.md, "Lockup and clear space") ----
   Gap and clear space are steps on the base-4 spacing scale, CHOSEN BY SIZE BAND
   -- never a fraction of the mark and never an optical nudge. The sidebar mark is
   22px, which is the 21-40px band: gap 12, clear space 16. The old values were
   gap 10 and a 14px bottom padding, neither of which is on the scale.

   Alignment is geometric, not optical: the mark's datum sits on the wordmark's
   BASELINE, which is why this is `align-items: baseline` rather than the `center`
   it used to be. An inline SVG is a flex item with no baseline of its own, so the
   browser synthesizes one at its foot; the datum's lower edge is at y=30 of the
   48-unit grid, which is (48-30)/48 = 0.375 of the mark's height above that foot,
   so the mark is pushed back down by exactly that fraction. `position: relative`
   rather than a negative margin because the synthesized baseline is specified
   from the BORDER box, so a margin would not reliably move it -- and because the
   shift is visual only, it cannot change the row's height. Nothing overflows: the
   word block (15px line + 11px sub-line) is taller than the 22px mark, so the
   flex line already reserves more space than the shifted mark occupies. */
.brandmark { display: flex; align-items: baseline; gap: 12px; padding: 18px 18px 16px; }
/* The Limen mark is brass, always. --brand resolves to #8A5E1C on light and
   #CBA25E on dark from the token blocks above, so the mark follows the theme
   without either value being written here. It must NEVER be given --accent,
   --deny, --warn or --action: a mark that carries a verdict asserts it
   permanently, which is exactly the bug the green shield was. */
.brandmark .mark { color: var(--brand); flex-shrink: 0; position: relative; top: calc(22px * 0.375); }
.brandmark .word { color: var(--brand); font-size: 15px; }
.brandmark .word small { display: block; font-weight: 500; color: var(--dim); font-size: 11px; letter-spacing: .3px; }

/* The wordmark itself: lowercase `limen`, IBM Plex Sans Medium, 0.03em, brass.
   TEXT form at both sites (15px and 17px, both under the 24px threshold), so the
   `i` keeps its own round tittle and no packet is substituted -- see WORDMARK in
   app.js for why that threshold is load-bearing rather than a preference.

   The console bundles no webfont (its asset contract is everything embedded, no
   CDNs, no extra requests), so Plex is REQUESTED and not guaranteed: an operator
   who has it installed gets the wordmark as specified, everyone else gets the
   same lowercase, medium-weight, 0.03em-tracked brass setting in the UI stack.
   Shipping a subsetted Plex woff2 here is a separate decision -- a new binary
   asset plus its OFL notice -- and is deliberately not smuggled in under this
   change. The app has no such gap: it already bundles Plex under the OFL. */
.wordmark { font-family: "IBM Plex Sans", var(--sans); font-weight: 500; letter-spacing: .03em; color: var(--brand); }

.nav { display: flex; flex-direction: column; gap: 2px; padding: 6px 10px; overflow-y: auto; }
.nav a {
  display: flex; align-items: center; gap: 11px; padding: 9px 12px; border-radius: 9px;
  color: var(--dim); font-weight: 500; position: relative;
}
.nav a:hover { background: var(--surface2); color: var(--text); text-decoration: none; }
.nav a.on { background: var(--brand-bg); color: var(--brand); font-weight: 600; }
/* Base size for every class="ico" glyph (nav icons carry no intrinsic width/height,
   so outside the nav — e.g. the activity feed — they must be bounded here or they
   balloon to fill their flow box). More-specific rules below override per context. */
.ico { width: 16px; height: 16px; flex: none; vertical-align: -2px; }
.nav a .ico { width: 18px; height: 18px; }
.banner .bico .ico, .banner .bico > svg { width: 18px; height: 18px; }
.proto-tag .ico, .proto-tag .lock { width: 12px; height: 12px; }
.nav a .badge {
  margin-left: auto; font-size: 11px; font-weight: 700; min-width: 20px; text-align: center;
  padding: 1px 6px; border-radius: 999px; background: var(--warn-bg); color: var(--warn);
  border: 1px solid transparent;
}
.nav .navsep { margin: 8px 12px 4px; font-size: 10px; letter-spacing: .8px; text-transform: uppercase; color: var(--dim); opacity: .7; }
.nav a .lock { margin-left: auto; opacity: .55; }

.sidefoot { margin-top: auto; padding: 12px; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 8px; }

/* ---------- Topbar + content ---------- */
.main { display: flex; flex-direction: column; min-width: 0; background: var(--bg); }
.topbar {
  display: flex; align-items: center; gap: 14px; padding: 14px 26px;
  border-bottom: 1px solid var(--border); background: var(--bg);
  position: sticky; top: 0; z-index: 5;
}
.topbar h1 { font-size: 18px; }
.topbar .sub { color: var(--dim); font-size: 13px; }
.topbar .spacer { flex: 1; }
.content { padding: 24px 26px 60px; max-width: 1240px; width: 100%; }

/* ---------- Chips / dots / mode ---------- */
.chip {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600;
  padding: 3px 9px; border-radius: 6px; border: 1px solid var(--border); background: var(--surface2);
  white-space: nowrap;
}
.dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; background: var(--dim); }
.dot.ok { background: var(--accent); }
.dot.warn { background: var(--warn); }
.dot.bad { background: var(--deny); }
.dot.off { background: var(--dim); }
.chip.approved { color: var(--accent); background: var(--allow-bg); border-color: color-mix(in srgb, var(--accent) 30%, transparent); }
.chip.pending  { color: var(--warn);   background: var(--warn-bg);  border-color: color-mix(in srgb, var(--warn) 30%, transparent); }
.chip.revoked  { color: var(--deny);   background: var(--deny-bg);  border-color: color-mix(in srgb, var(--deny) 30%, transparent); }
/* quarantined: an auto-parked node (lifecycle sweep) — reversible, not an operator
   revoke, so it borrows the warn treatment rather than deny's severity. */
.chip.quarantined { color: var(--warn); background: var(--warn-bg); border-color: color-mix(in srgb, var(--warn) 30%, transparent); }
.chip.connected { color: var(--accent); background: var(--allow-bg); border-color: color-mix(in srgb, var(--accent) 30%, transparent); }
.chip.offline  { color: var(--dim); }
.chip.info { color: var(--action); background: var(--action-bg); border-color: color-mix(in srgb, var(--action) 30%, transparent); }
.chip.plan { color: var(--brand); background: var(--brand-bg); border-color: color-mix(in srgb, var(--brand) 30%, transparent); }
/* Rule-action chips (color law: ALLOW is the only affirmative green; deny/reject red). */
.chip.allow { color: var(--accent); background: var(--allow-bg); border-color: color-mix(in srgb, var(--accent) 30%, transparent); }
.chip.deny  { color: var(--deny);   background: var(--deny-bg);  border-color: color-mix(in srgb, var(--deny) 30%, transparent); }

.modechip { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; font-weight: 600; padding: 4px 11px; border-radius: 999px; border: 1px solid var(--border); }
.modechip.mock { color: var(--warn); background: var(--warn-bg); border-color: color-mix(in srgb, var(--warn) 35%, transparent); }
.modechip.live { color: var(--accent); background: var(--allow-bg); border-color: color-mix(in srgb, var(--accent) 35%, transparent); }

.proto-tag {
  display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 700; letter-spacing: .4px;
  padding: 3px 8px; border-radius: 6px; color: var(--action); background: var(--action-bg);
  border: 1px dashed color-mix(in srgb, var(--action) 45%, transparent); text-transform: uppercase;
}

/* ---------- Cards / grid ---------- */
.card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 18px; }
.card h3 { font-size: 14px; margin-bottom: 4px; }
.card .cardsub { color: var(--dim); font-size: 12px; margin-bottom: 12px; }
.grid { display: grid; gap: 16px; }
.grid.kpi { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.grid.cols2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.section-title { font-size: 12px; letter-spacing: .6px; text-transform: uppercase; color: var(--dim); margin: 26px 0 12px; font-weight: 600; }

.kpicard { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 16px 18px; }
.kpicard .k { font-size: 12px; color: var(--dim); font-weight: 600; display: flex; align-items: center; gap: 7px; }
.kpicard .v { font-size: 30px; font-weight: 700; margin-top: 6px; font-variant-numeric: tabular-nums; }
.kpicard .foot { font-size: 12px; color: var(--dim); margin-top: 3px; }
.kpicard.accent .v { color: var(--accent); }
.kpicard.warn .v { color: var(--warn); }
.kpicard.deny .v { color: var(--deny); }

/* ---------- Getting-started wizard (WP-CON2) ---------- */
.wizsteps { display: flex; flex-direction: column; gap: 14px; }
.wizstep {
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
  padding: 18px 20px; display: flex; gap: 16px;
}
.wizstep.done { border-color: color-mix(in srgb, var(--accent) 35%, var(--border)); }
.wizstep.info { border-style: dashed; }
.wizdot {
  width: 30px; height: 30px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 13px; border: 2px solid var(--border); color: var(--dim); background: var(--surface2);
}
.wizstep.done .wizdot { border-color: var(--accent); color: var(--accent-ink); background: var(--accent); }
.wizstep.info .wizdot { border-style: dashed; }
.wizbody { flex: 1; min-width: 0; }
.wizbody h3 { margin: 0 0 6px; font-size: 15px; display: flex; align-items: center; gap: 9px; }
.wizbody .cardsub { color: var(--dim); font-size: 12.5px; margin-bottom: 12px; }
.wizhero { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 20px; }

/* ---------- Attention triage strip ---------- */
.triage { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 4px; }
.triage .t {
  display: flex; align-items: center; gap: 9px; padding: 10px 14px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--surface); cursor: pointer; font-weight: 500;
}
.triage .t:hover { border-color: var(--action); }
.triage .t .n { font-weight: 700; font-size: 16px; font-variant-numeric: tabular-nums; }
.triage .t.warn { border-left: 3px solid var(--warn); }
.triage .t.warn .n { color: var(--warn); }
.triage .t.info { border-left: 3px solid var(--action); }
.triage .t.info .n { color: var(--action); }
.triage .t.quiet .n { color: var(--dim); }
/* A cleared tile: nothing to act on — calm, non-interactive, an affirmative ✓. */
.triage .t.clear { cursor: default; color: var(--dim); background: transparent; }
.triage .t.clear:hover { border-color: var(--border); }
.triage .t.clear .tcheck { display: inline-flex; color: var(--accent); }
.triage .t.clear .tcheck .ico, .triage .t.clear .tcheck svg { width: 15px; height: 15px; }
.triage .t.clear .tclear { margin-left: 2px; font-size: 11px; text-transform: uppercase; letter-spacing: .5px; opacity: .7; }

/* ---------- Group health bars ---------- */
.grouprow { display: grid; grid-template-columns: 130px 1fr auto; align-items: center; gap: 14px; padding: 10px 0; border-bottom: 1px solid var(--border); }
.grouprow:last-child { border-bottom: none; }
.grouprow .gname { font-weight: 600; }
/* Interactive dashboard tiles/rows: the whole dashboard is a launch surface. */
a.kpicard.link, a.grouprow.link { text-decoration: none; color: inherit; cursor: pointer; transition: border-color .12s, background .12s, transform .06s; }
a.kpicard.link { display: block; }
a.kpicard.link:hover { border-color: var(--action); background: var(--surface2); }
a.grouprow.link { margin: 0 -10px; padding-left: 10px; padding-right: 10px; border-radius: 8px; }
a.grouprow.link:hover { background: var(--surface2); border-bottom-color: transparent; }
a.kpicard.link:focus-visible, a.grouprow.link:focus-visible { outline: 2px solid var(--action); outline-offset: 2px; }
.actrow { display: flex; align-items: center; gap: 9px; min-width: 0; }
.actrow > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.seeall { font-size: 12px; font-weight: 600; text-transform: none; letter-spacing: 0; margin-left: 10px; }
.bar { height: 8px; border-radius: 5px; background: var(--surface2); overflow: hidden; display: flex; }
.bar .seg { height: 100%; }
.bar .seg.ok { background: var(--accent); }
.bar .seg.off { background: color-mix(in srgb, var(--dim) 45%, transparent); }
.grouprow .gstat { font-size: 12px; color: var(--dim); font-variant-numeric: tabular-nums; white-space: nowrap; }
.grouprow .gstat b { color: var(--text); }

/* ---------- Tables ---------- */
.tablewrap { overflow-x: auto; border: 1px solid var(--border); border-radius: 12px; background: var(--surface); }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
thead th { text-align: left; padding: 11px 14px; color: var(--dim); font-weight: 600; font-size: 11px; letter-spacing: .5px; text-transform: uppercase; border-bottom: 1px solid var(--border); background: var(--surface); position: sticky; top: 0; }
tbody td { padding: 11px 14px; border-bottom: 1px solid var(--border); vertical-align: middle; }
tbody tr:last-child td { border-bottom: none; }
tbody tr.clickable { cursor: pointer; }
tbody tr.clickable:hover { background: var(--surface2); }
/* REV-CON-SPA-4: a clickable row's primary cell is a real <button> so Enter and
   Space activate it, it sits in the tab order, and a screen reader announces it.
   Styled back down to plain text — the row looks and behaves for a mouse exactly
   as it did before — with the one visible addition a keyboard user needs: a
   focus ring, plus the same hover tint on the whole row via :focus-within, so
   "which row am I on" is answerable without a pointer. */
.rowopen { display: block; width: 100%; text-align: left; background: none; border: 0; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; }
.rowopen:focus-visible { outline: 2px solid var(--action); outline-offset: 3px; border-radius: 6px; }
tbody tr.clickable:focus-within { background: var(--surface2); }
.bundlehead .rowopen { width: auto; flex: 1; min-width: 0; }
.bundlecard.clickable:focus-within { border-color: var(--action); }
td .nodename { font-weight: 600; }
td .fp { font-family: var(--mono); font-size: 11px; color: var(--dim); }
.labels { display: flex; flex-wrap: wrap; gap: 5px; }
/* Review actions (WP-CON2): the per-row scope picker + approve/dismiss/propose
   buttons stay a fixed, readable width instead of the default full-bleed .input. */
.reviewactions { min-width: 220px; }
.reviewactions .reviewScope { width: 200px; }
.reviewactions .btnrow { flex-wrap: wrap; }
.lbl { font-family: var(--mono); font-size: 11px; padding: 1px 7px; border-radius: 5px; background: var(--surface2); border: 1px solid var(--border); color: var(--dim); }

/* ---------- Filters / toolbar ---------- */
.toolbar { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 14px; }
.toolbar .grow { flex: 1; }
.pillsel { display: inline-flex; border: 1px solid var(--border); border-radius: 999px; overflow: hidden; background: var(--surface); }
.pillsel button { border: none; background: transparent; color: var(--dim); padding: 6px 13px; cursor: pointer; font-weight: 600; font-size: 12px; }
.pillsel button.on { background: var(--action-bg); color: var(--action); }
.pillsel button .pilln { display: inline-block; margin-left: 5px; padding: 0 6px; border-radius: 999px; background: var(--surface2); color: var(--dim); font-size: 11px; font-variant-numeric: tabular-nums; }
.pillsel button.on .pilln { background: color-mix(in srgb, var(--action) 22%, transparent); color: var(--action); }
.input {
  background: var(--surface); border: 1px solid var(--border); border-radius: 9px;
  color: var(--text); padding: 8px 12px; font-size: 13px; width: 100%; font-family: inherit;
}
.input:focus { outline: none; border-color: var(--action); box-shadow: 0 0 0 2px var(--action-bg); }
label.fld { display: block; font-size: 12px; color: var(--dim); font-weight: 600; margin-bottom: 6px; }
.formrow { margin-bottom: 16px; }

/* ---------- Buttons ---------- */
.btn { display: inline-flex; align-items: center; gap: 7px; border: 1px solid var(--border); background: var(--surface); color: var(--text); padding: 8px 14px; border-radius: 9px; font-weight: 600; font-size: 13px; cursor: pointer; }
.btn:hover { background: var(--surface2); }
.btn.primary { background: var(--action); color: var(--action-ink); border-color: transparent; }
.btn.primary:hover { filter: brightness(1.06); background: var(--action); }
.btn.allow { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
.btn.allow:hover { filter: brightness(1.06); background: var(--accent); }
.btn.destructive { background: var(--deny); color: var(--deny-ink); border-color: transparent; }
.btn.destructive:hover { filter: brightness(1.06); background: var(--deny); }
.btn.quiet { background: transparent; border-color: transparent; color: var(--action); }
.btn.sm { padding: 5px 10px; font-size: 12px; }
.btn.block { display: flex; width: 100%; justify-content: center; }
.btn:disabled { opacity: .5; cursor: not-allowed; }
/* Loading state: label stays put, a small spinner replaces where an icon would go
   (never "just" a disabled button — the operator sees work is happening). */
.btn .btnspin { width: 14px; height: 14px; border: 2px solid color-mix(in srgb, currentColor 30%, transparent); border-top-color: currentColor; border-radius: 50%; animation: sp .7s linear infinite; flex-shrink: 0; }
.btnrow { display: flex; gap: 10px; flex-wrap: wrap; }
.iconbtn { background: transparent; border: 1px solid var(--border); border-radius: 8px; color: var(--dim); width: 34px; height: 32px; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }
.iconbtn:hover { color: var(--text); background: var(--surface2); }

/* ---------- Auth (sign-in / accept-invite) ---------- */
/* Full-viewport centered card, no app shell — used for /#/accept and the
   pre-session sign-in screen (mountBare/authCard in app.js). */
.authwrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px; background: var(--desk); }
.authcard { width: 100%; max-width: 380px; background: var(--surface); border: 1px solid var(--border); border-radius: 16px; padding: 32px 30px; box-shadow: var(--shadow); }
/* The sign-in lockup is the same lockup as the sidebar's, one band larger: a 34px
   mark is still 21-40, so gap 12 and clear space 16 (the 24px margin below clears
   that minimum and is on the scale; 22px was not). It used to stack the mark ABOVE
   the word, which the handoff's one alignment rule cannot describe -- a datum
   cannot sit on a baseline that is on another line -- so it is now horizontal and
   centred as a whole, and the geometry is the rule rather than an eyeball. */
.authbrand { display: flex; align-items: baseline; justify-content: center; gap: 12px; margin-bottom: 24px; }
/* Brass, never a state token -- the same rule as the sidebar's mark. */
.authbrand .mark { color: var(--brand); width: 34px; height: 34px; flex-shrink: 0; position: relative; top: calc(34px * 0.375); }
.authbrand .word { color: var(--brand); font-size: 17px; line-height: 1.3; }
.authbrand .word small { display: block; font-weight: 500; color: var(--dim); font-size: 12px; letter-spacing: .3px; }
.authcard h2 { font-size: 19px; text-align: center; margin-bottom: 6px; }
.authsub { color: var(--dim); font-size: 13px; text-align: center; margin-bottom: 20px; line-height: 1.5; }
.authform { display: flex; flex-direction: column; gap: 14px; margin-top: 4px; }
.authform .fld { margin-bottom: -6px; }
.authform .input { font-size: 14px; padding: 10px 12px; }
.autherr {
  display: flex; align-items: flex-start; gap: 9px; font-size: 13px; font-weight: 500;
  color: var(--deny); background: var(--deny-bg); border: 1px solid color-mix(in srgb, var(--deny) 30%, transparent);
  border-radius: 9px; padding: 10px 12px; margin-bottom: 4px;
}
/* ICON.warn is injected here as a BARE <svg> (no .ico class), so `.ico { flex: none }`
   never applied and the flex algorithm squashed the icon narrower and narrower as the
   message got longer. `.banner .bico > svg` already guards the same shape elsewhere. */
.autherr > svg { width: 18px; height: 18px; flex: none; }
.authfoot { margin-top: 22px; padding-top: 16px; border-top: 1px solid var(--border); color: var(--dim); font-size: 12px; text-align: center; line-height: 1.5; }
@media (prefers-reduced-motion: reduce) {
  .btn .btnspin { animation: none; }
}

/* ---------- Detail panel / drawer ---------- */
.overlay { position: fixed; inset: 0; background: color-mix(in srgb, var(--desk) 70%, transparent); backdrop-filter: blur(2px); z-index: 40; display: flex; justify-content: flex-end; }
.drawer { width: min(560px, 100%); height: 100%; background: var(--surface); border-left: 1px solid var(--border); overflow-y: auto; box-shadow: var(--shadow); }
.drawer .dhead { display: flex; align-items: flex-start; gap: 12px; padding: 20px 22px; border-bottom: 1px solid var(--border); position: sticky; top: 0; background: var(--surface); }
.drawer .dbody { padding: 20px 22px; }
.kv { display: grid; grid-template-columns: 130px 1fr; gap: 8px 14px; font-size: 13px; }
.kv dt { color: var(--dim); }
.kv dd { margin: 0; word-break: break-word; }

.dialog-back { position: fixed; inset: 0; background: color-mix(in srgb, var(--desk) 72%, transparent); z-index: 50; display: flex; align-items: center; justify-content: center; padding: 20px; }
.dialog { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 22px; width: min(560px, 100%); box-shadow: var(--shadow); max-height: 90vh; overflow-y: auto; }

/* ---------- Bundle / code block ---------- */
.codeblock { background: var(--desk); border: 1px solid var(--border); border-radius: 10px; padding: 14px 16px; font-family: var(--mono); font-size: 12.5px; color: var(--text); overflow-x: auto; white-space: pre; position: relative; }
.copyrow { display: flex; align-items: center; gap: 10px; }

/* The enrolment QR (REV-ENROLL-1). The code itself is server-rendered SVG with an
   opaque WHITE ground baked in — a QR that inherits a dark theme behind it is a QR
   scanners read intermittently, which is the worst kind of broken. So this wrapper
   deliberately does NOT tint the symbol; it only centres it and gives the light
   square a visible edge against a dark surface. */
.qrwrap { display: flex; justify-content: center; padding: 14px; margin-bottom: 14px; background: #ffffff; border: 1px solid var(--border); border-radius: 12px; }
.qrwrap svg { display: block; width: min(260px, 100%); height: auto; }

/* ---------- Banners ---------- */
.banner { display: flex; gap: 12px; align-items: flex-start; padding: 12px 16px; border-radius: 10px; border: 1px solid var(--border); background: var(--surface); margin-bottom: 18px; }
.banner .bico { flex-shrink: 0; margin-top: 1px; }
.banner.info { border-left: 3px solid var(--action); background: var(--action-bg); }
.banner.info .bico { color: var(--action); }
.banner.warn { border-left: 3px solid var(--warn); background: var(--warn-bg); }
.banner.warn .bico { color: var(--warn); }
.banner .bt { font-weight: 600; }
.banner .bd { color: var(--dim); font-size: 13px; }

/* ---------- RBAC matrix ---------- */
.matrix td, .matrix th { text-align: center; }
.matrix td:first-child, .matrix th:first-child { text-align: left; font-weight: 600; }
.matrix .yes { color: var(--accent); font-weight: 700; }
.matrix .no { color: var(--dim); opacity: .5; }
.matrix .partial { color: var(--warn); font-size: 12px; }

/* ---------- misc ---------- */
.empty { text-align: center; color: var(--dim); padding: 48px 20px; }
/* The console's indeterminate indicator is the Limen mark running (REV-ID-5,
   docs/HANDOFF-identity.md § Motion). It replaced `.spin`, a 2px arc in
   var(--action) — the *interactive* colour — spinning on surfaces where nothing was
   interactive. Brass and never stateful: the mark carries no verdict, so it takes no
   accent/deny/warn/action, ever (handoff rule 1, ADR-019).
   Geometry and timing come from motion.js; every keyframe here would be a second
   source for them. With JS off, or under reduced motion, the markup app.js emits is
   already the resting mark, which is what "a suppressed playback renders its final
   state rather than nothing" means on this surface. */
.markbusy { color: var(--brand); display: inline-flex; flex: none; line-height: 0; }
@keyframes sp { to { transform: rotate(360deg); } }
.loadwrap { display: flex; align-items: center; gap: 10px; color: var(--dim); padding: 40px; justify-content: center; }
.muted { color: var(--dim); }
.usage-bar { height: 10px; border-radius: 6px; background: var(--surface2); overflow: hidden; }
.usage-bar .fill { height: 100%; background: var(--action); }
.usage-bar .fill.near { background: var(--warn); }
.usage-bar .fill.over { background: var(--deny); }
.stat-inline { display: flex; gap: 22px; flex-wrap: wrap; }
.stat-inline .si .n { font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; }
.stat-inline .si .l { font-size: 12px; color: var(--dim); }
.toast { position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%); background: var(--surface2); border: 1px solid var(--border); border-radius: 10px; padding: 11px 18px; box-shadow: var(--shadow); z-index: 60; font-weight: 500; display: flex; gap: 10px; align-items: center; }
.toast.ok { border-left: 3px solid var(--accent); }
.toast.err { border-left: 3px solid var(--deny); }

/* ---------- Live-refresh indicator ---------- */
.refresh-ind { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; font-weight: 600; color: var(--dim); white-space: nowrap; }
.refresh-ind #refreshAge { color: var(--text); font-variant-numeric: tabular-nums; }
.refreshDot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 60%, transparent); }
.refreshDot.beat { animation: beat 1.1s ease-out; }
/* While reconnecting there is nothing live to pulse, so the green liveness dot goes
   and the running mark stands in its place (app.js setReconnecting). */
.refresh-ind.reconnecting .refreshDot { display: none; }
.refresh-ind #reconnectSlot:empty { display: none; }
@keyframes beat {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 60%, transparent); }
  70% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--accent) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 0%, transparent); }
}

/* ---------- Search box ---------- */
.searchbox { position: relative; display: inline-flex; align-items: center; min-width: 260px; flex: 1 1 260px; max-width: 420px; }
.searchbox .sico { position: absolute; left: 11px; color: var(--dim); display: inline-flex; pointer-events: none; }
.searchbox .input { padding-left: 34px; }
.searchbox input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; }

/* ---------- Sparkline ---------- */
.spark-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; margin-bottom: 10px; }
.spark-now { text-align: right; flex-shrink: 0; }
.spark-now .v { font-size: 24px; font-weight: 700; color: var(--accent); font-variant-numeric: tabular-nums; line-height: 1; }
.spark-now .v span { font-size: 14px; color: var(--dim); font-weight: 600; }
.spark-now .foot { font-size: 12px; color: var(--dim); margin-top: 3px; }
.spark { display: block; width: 100%; height: 96px; }
.spark-grid { stroke: var(--border); stroke-width: 1; opacity: .6; }
.spark-area { fill: var(--action-bg); stroke: none; }
.spark-line { fill: none; stroke: var(--action); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.spark-end { fill: var(--action); stroke: var(--surface); stroke-width: 2; }
.spark-val { fill: var(--text); font-size: 12px; font-weight: 700; font-family: var(--mono); }

/* ---------- Confirm dialog ---------- */
.dialog.confirm { width: min(460px, 100%); }
.confirm-head { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.confirm-head h2 { font-size: 17px; }
.confirm .cico { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 9px; flex-shrink: 0; }
.confirm .cico.destructive { color: var(--deny); background: var(--deny-bg); }
.confirm .cico.primary { color: var(--action); background: var(--action-bg); }
.confirm-body p { margin: 0 0 10px; }
.confirm-body p:last-child { margin-bottom: 0; }

/* Quiet destructive affordance (drawer "Delete…") — red only on intent. */
.btn.quiet.danger { color: var(--deny); }
.btn.quiet.danger:hover { background: var(--deny-bg); }

/* ---------- Policy (ADR-015): bundles, rules, drift, rollout ---------- */
.bundlecard { display: flex; flex-direction: column; gap: 2px; }
.bundlehead { display: flex; align-items: center; gap: 10px; }
.bundlehead b { font-size: 15px; }
.bundlehead .chip { margin-left: auto; }
.bundlemeta { display: flex; gap: 12px; font-size: 12px; color: var(--dim); margin-bottom: 8px; }
.bundleassigns { display: flex; flex-wrap: wrap; gap: 6px; }

/* Curated bundles (BACKLOG §1c) — first-party, ready-to-apply content. */
.curatedcard { display: flex; flex-direction: column; gap: 2px; }
.curatedreadme { margin-top: 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface2); }
.curatedreadme summary { cursor: pointer; padding: 8px 10px; font-size: 12.5px; color: var(--dim); user-select: none; }
.curatedreadme summary:hover { color: var(--text); }
.curatedreadme pre {
  margin: 0; padding: 0 12px 12px; font-family: var(--mono); font-size: 12px; line-height: 1.5;
  white-space: pre-wrap; word-break: break-word; max-height: 320px; overflow-y: auto; color: var(--text);
}

/* Rule rows — mirrors the app's rule-row look (name · resolved operator · action). */
.rulelist { display: flex; flex-direction: column; gap: 8px; }
.rulerow { display: flex; gap: 12px; align-items: flex-start; padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--surface2); }
.rr-main { flex: 1; min-width: 0; }
.rr-name { font-weight: 600; font-size: 13px; }
.rr-op { font-size: 12px; margin-top: 2px; word-break: break-word; }
.rr-desc { font-size: 12px; margin-top: 3px; }
.rr-flags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.rr-side { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.addrule { margin-top: 8px; }
.assignrow { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border); }
.assignrow:last-child { border-bottom: none; }
.assignform { display: flex; gap: 10px; align-items: center; margin-top: 12px; }
.checkline { display: flex; gap: 9px; align-items: flex-start; font-size: 13px; cursor: pointer; }
.checkline input { margin-top: 3px; flex-shrink: 0; }

/* Drift cards — severity stripe (never color alone: each carries a text chip too). */
.driftcard { border: 1px solid var(--border); border-left: 3px solid var(--border); border-radius: 12px; background: var(--surface); margin-bottom: 10px; overflow: hidden; }
.driftcard.sev-sync   { border-left-color: var(--accent); }
.driftcard.sev-drift  { border-left-color: var(--warn); }
.driftcard.sev-extra  { border-left-color: var(--action); }
.driftcard.sev-unreach{ border-left-color: var(--dim); }
.drifthead { display: flex; align-items: center; gap: 14px; padding: 12px 14px; cursor: pointer; }
.drifthead:hover { background: var(--surface2); }
.dh-node { min-width: 160px; }
.dh-group { flex-shrink: 0; font-size: 12px; }
.dh-badges { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-left: auto; }
.drifthead .caret { color: var(--dim); transition: transform .15s ease; flex-shrink: 0; }
.driftcard.open .drifthead .caret { transform: rotate(90deg); }
.driftdetail { display: none; padding: 6px 14px 14px; border-top: 1px solid var(--border); }
.driftcard.open .driftdetail { display: block; }
.driftline { display: flex; align-items: center; gap: 10px; padding: 7px 0; border-bottom: 1px solid var(--border); font-size: 12px; }
.driftline:last-child { border-bottom: none; }
.planlist { margin: 10px 0 0; padding-left: 20px; font-size: 13px; }
.planlist li { margin-bottom: 5px; }
.sevrow.sev-unreach td:first-child { box-shadow: inset 3px 0 0 var(--dim); }
.sevrow.sev-sync td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

/* ---------- Monitoring · severity chips (ADR-017) ---------- */
.chip.warn { color: var(--warn); background: var(--warn-bg); border-color: color-mix(in srgb, var(--warn) 30%, transparent); }
.chip.crit { color: var(--deny); background: var(--deny-bg); border-color: color-mix(in srgb, var(--deny) 34%, transparent); font-weight: 700; }

.kpicard .v .unit { font-size: 16px; font-weight: 600; color: var(--dim); }

/* ---------- Monitoring · Overview drift bars ---------- */
.legend { display: flex; gap: 10px; flex-wrap: wrap; margin: 4px 0 12px; }
.driftbars { display: flex; flex-direction: column; }
/* Fixed stat column so every bar shares the same track length regardless of how
   many/long the count chips are (an `auto` column made bars different lengths). */
.driftrow { grid-template-columns: 130px 1fr 240px; }
.driftrow .driftstat { justify-content: flex-end; }
/* Keep linked drift rows column-aligned with any non-linked "(none)" row (no
   horizontal shift from the generic a.grouprow.link negative margin). */
a.driftrow.link { margin: 0; padding-left: 0; padding-right: 0; border-radius: 8px; }
a.driftrow.link:hover { background: var(--surface2); border-bottom-color: var(--border); }
.bar.drift { background: color-mix(in srgb, var(--dim) 22%, transparent); }
.bar.drift .seg.sync { background: var(--accent); }
.bar.drift .seg.drift { background: var(--warn); }
.bar.drift .seg.unreach { background: color-mix(in srgb, var(--dim) 55%, transparent); }
.driftstat { display: flex; gap: 8px; flex-wrap: wrap; }

/* ---------- Monitoring · Overview console service health (hand-rolled SVG histogram) ---------- */
.latencyhist { background: color-mix(in srgb, var(--dim) 10%, transparent); border-radius: 8px; padding: 4px; box-sizing: border-box; }
.latencyhist rect { transition: fill .12s; }
.latencyhist rect:hover { fill: var(--accent); }
.dchip { font-size: 11px; font-weight: 600; padding: 2px 9px; border-radius: 999px; border: 1px solid var(--border); display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.dchip::before { content: ""; width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.dchip.sync { color: var(--accent); background: var(--allow-bg); border-color: color-mix(in srgb, var(--accent) 30%, transparent); }
.dchip.sync::before { background: var(--accent); }
.dchip.drift { color: var(--warn); background: var(--warn-bg); border-color: color-mix(in srgb, var(--warn) 30%, transparent); }
.dchip.drift::before { background: var(--warn); }
.dchip.unreach { color: var(--dim); background: var(--surface2); }
.dchip.unreach::before { background: var(--dim); }

/* ---------- Monitoring · Alerts feed ---------- */
.alertfeed { display: flex; flex-direction: column; gap: 10px; }
.alertrow { display: grid; grid-template-columns: 150px 1fr auto; gap: 14px; align-items: center; padding: 12px 14px; border: 1px solid var(--border); border-left: 3px solid var(--border); border-radius: 12px; background: var(--surface); }
.alertrow.sev-crit { border-left-color: var(--deny); }
.alertrow.sev-warn { border-left-color: var(--warn); }
.alertrow.sev-info { border-left-color: var(--action); }
.alertrow.resolved { opacity: .62; }
.al-sev { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.al-kind { font-size: 11px; color: var(--dim); }
.al-summary { font-weight: 600; margin-bottom: 3px; }
.al-node .nodename { font-weight: 600; font-size: 13px; }
.al-node .fp { font-family: var(--mono); font-size: 11px; color: var(--dim); }
.al-meta { display: flex; flex-direction: column; gap: 5px; align-items: flex-end; font-size: 12px; white-space: nowrap; }

/* ---------- Monitoring · Self-heal ---------- */
.killcard { display: flex; }
.killcard.on { border-left: 3px solid var(--accent); }
.killcard.off { border-left: 3px solid var(--dim); }
.killhead { display: flex; align-items: center; gap: 16px; width: 100%; }
.killhead > div { flex: 1; }
.shrow { align-items: center; }
.shrow .gname { font-weight: 600; min-width: 120px; }

/* Accessible on/off switch (state carries a text label too, never color alone). */
.swtoggle { display: inline-flex; align-items: center; gap: 9px; background: transparent; border: none; cursor: pointer; padding: 4px; color: var(--dim); font-weight: 600; font-size: 12px; }
.swtoggle:disabled { cursor: not-allowed; opacity: .5; }
.swtrack { width: 40px; height: 22px; border-radius: 999px; background: color-mix(in srgb, var(--dim) 40%, transparent); border: 1px solid var(--border); position: relative; transition: background .15s ease; flex-shrink: 0; }
.swthumb { position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: var(--surface); box-shadow: 0 1px 2px rgba(0,0,0,.35); transition: transform .15s ease; }
.swtoggle.on .swtrack { background: var(--accent); border-color: var(--accent); }
.swtoggle.on .swthumb { transform: translateX(18px); }
.swtoggle.on { color: var(--accent); }
.swlabel { min-width: 30px; text-align: left; }

/* ---------- Policy · revision history ---------- */
.revcard { border: 1px solid var(--border); border-radius: 10px; background: var(--surface); margin-bottom: 8px; overflow: hidden; }
.revcard.current { border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); }
.revhead { display: flex; align-items: center; gap: 10px; padding: 10px 12px; cursor: pointer; }
.revhead:hover { background: var(--surface2); }
.revsum { font-size: 13px; }
.revwhen { font-size: 12px; }
.revhead .caret { color: var(--dim); transition: transform .15s ease; flex-shrink: 0; }
.revcard.open .revhead .caret { transform: rotate(90deg); }
.revbody { display: none; padding: 4px 12px 12px; border-top: 1px solid var(--border); }
.revcard.open .revbody { display: block; }

@media (prefers-reduced-motion: reduce) {
  .refreshDot.beat { animation: none; }
  .drifthead .caret, .revhead .caret, .swtrack, .swthumb { transition: none; }
  /* .markbusy is deliberately absent. The mark's motion is not a CSS animation and
     cannot be switched off from here: motion.js decides per playback, because
     reduced motion suppresses UNPROMPTED playback only — an indicator the operator
     asked for still plays, and a blanket rule in this block could not tell the two
     apart. It reads `prefers-reduced-motion` through matchMedia instead. */
}

@media (max-width: 860px) {
  .app { grid-template-columns: 1fr; }
  .sidebar { position: static; height: auto; flex-direction: row; flex-wrap: wrap; align-items: center; }
  .nav { flex-direction: row; flex-wrap: wrap; }
  .nav .navsep, .sidefoot { display: none; }
  .searchbox { max-width: none; }
  .drifthead { flex-wrap: wrap; }
  .dh-badges { margin-left: 0; }
  .assignform { flex-wrap: wrap; }
  /* Monitoring: let the grid rows stack instead of forcing a horizontal scroll. */
  .alertrow { grid-template-columns: 1fr; }
  .al-meta { align-items: flex-start; }
  .driftrow { grid-template-columns: 1fr; }
  .killhead { flex-wrap: wrap; }
}
