
/* Mirror — design system.
   One place for the look, so a view is markup and not a pile of inline styles.
   Dark mode is honoured because an operator watching a session often is at night,
   and a dashboard that blinds them is one they stop opening. */
:root{
  --bg:#f4f6fb; --bg-elev:#ffffff; --surface:#ffffff; --surface-2:#f8fafc;
  --border:#e4e9f2; --border-strong:#cdd6e6;
  --text:#0f172a; --text-2:#475569; --muted:#64748b;
  --brand:#2563eb; --brand-strong:#1d4ed8; --brand-soft:#eff4ff; --brand-ring:#2563eb33;
  --ok:#059669; --ok-soft:#ecfdf5;
  --warn:#b45309; --warn-soft:#fffbeb;
  --danger:#dc2626; --danger-soft:#fef2f2;
  --radius:10px; --radius-sm:7px; --radius-lg:14px;
  --shadow-sm:0 1px 2px rgba(15,23,42,.05);
  --shadow:0 1px 3px rgba(15,23,42,.07), 0 6px 16px -6px rgba(15,23,42,.10);
  --shadow-lg:0 10px 34px -10px rgba(15,23,42,.22);
  --sidebar:#0b1220; --sidebar-2:#131c31; --sidebar-text:#94a3b8;
  --font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Inter,sans-serif;
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,monospace;
}
@media (prefers-color-scheme:dark){
  :root{
    --bg:#080c14; --bg-elev:#0f1626; --surface:#0f1626; --surface-2:#141d31;
    --border:#1e293b; --border-strong:#2c3a52;
    --text:#e7edf7; --text-2:#a8b6cd; --muted:#7f8ea6;
    --brand:#5b8cff; --brand-strong:#7aa2ff; --brand-soft:#131f3d; --brand-ring:#5b8cff40;
    --ok:#34d399; --ok-soft:#0c2b22; --warn:#fbbf24; --warn-soft:#2a1f07;
    --danger:#f87171; --danger-soft:#2d1315;
    --shadow-sm:0 1px 2px rgba(0,0,0,.4);
    --shadow:0 1px 3px rgba(0,0,0,.5), 0 8px 20px -8px rgba(0,0,0,.6);
    --shadow-lg:0 14px 40px -12px rgba(0,0,0,.75);
    --sidebar:#070b12; --sidebar-2:#111a2b;
  }
}
*{box-sizing:border-box}
html,body{height:100%}
body{margin:0;font-family:var(--font);background:var(--bg);color:var(--text);
  font-size:14px;line-height:1.55;-webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility}
a{color:var(--brand);text-decoration:none}
a:hover{text-decoration:underline}
code,kbd{font-family:var(--mono);font-size:.92em}
:focus-visible{outline:2px solid var(--brand);outline-offset:2px;border-radius:4px}

/* ---------- layout ---------- */
.app{display:grid;grid-template-columns:236px 1fr;min-height:100vh}
.main{min-width:0;display:flex;flex-direction:column}
.wrap{padding:22px 26px 56px;max-width:1400px;width:100%}

/* ---------- sidebar ---------- */
.sidebar{position:sticky;top:0;height:100vh;background:var(--sidebar);color:var(--sidebar-text);
  display:flex;flex-direction:column;padding:18px 12px;gap:4px}
.brand{display:flex;align-items:center;gap:9px;padding:6px 10px 18px;
  color:#fff;font-weight:650;font-size:15px;letter-spacing:-.01em}
.brand .mark{width:26px;height:26px;border-radius:8px;flex:0 0 auto;
  background:linear-gradient(140deg,var(--brand),#7c3aed);
  box-shadow:0 2px 10px -2px var(--brand-ring);display:grid;place-items:center;
  color:#fff;font-size:13px}
.nav{display:flex;flex-direction:column;gap:2px;flex:1}
.nav a{display:flex;align-items:center;gap:10px;padding:8px 11px;border-radius:var(--radius-sm);
  color:var(--sidebar-text);font-size:13.5px;font-weight:500;transition:background .14s,color .14s}
.nav a:hover{background:var(--sidebar-2);color:#e2e8f0;text-decoration:none}
.nav a.active{background:var(--brand);color:#fff;box-shadow:0 2px 10px -3px var(--brand-ring)}
.nav a .glyph{width:17px;text-align:center;opacity:.9;font-size:13px}
.nav a .pill{margin-left:auto;font-size:11px;padding:1px 7px;border-radius:99px;
  background:var(--brand);color:#fff;font-weight:600}
.nav a.active .pill{background:rgba(255,255,255,.24)}
.side-foot{border-top:1px solid #1e293b;padding-top:12px;margin-top:8px;
  display:flex;flex-direction:column;gap:7px;font-size:12px}
.side-foot a{color:var(--sidebar-text)}
.side-foot a:hover{color:#e2e8f0}
.who{color:#cbd5e1;font-family:var(--mono);font-size:11.5px;overflow:hidden;text-overflow:ellipsis}

/* ---------- topbar ---------- */
.topbar{display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:16px 26px;background:color-mix(in srgb,var(--bg-elev) 88%,transparent);
  backdrop-filter:saturate(180%) blur(12px);
  border-bottom:1px solid var(--border);position:sticky;top:0;z-index:10}
.topbar h1{margin:0;font-size:17px;font-weight:650;letter-spacing:-.015em}
.topbar .sub{color:var(--muted);font-size:12.5px;font-weight:400;margin-top:1px}

/* ---------- cards ---------- */
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);
  padding:18px;box-shadow:var(--shadow-sm);margin-bottom:16px}
.card:last-child{margin-bottom:0}
.card h3{margin:0 0 12px;font-size:13px;font-weight:650;letter-spacing:.02em;
  text-transform:uppercase;color:var(--text-2);display:flex;align-items:center;gap:8px}
.card h3 small{font-weight:450;text-transform:none;letter-spacing:0;color:var(--muted);font-size:12.5px}
.card.flush{padding:0;overflow:hidden}
.card.flush>h3{padding:16px 18px 0;margin-bottom:10px}

/* ---------- page head + catalogue ---------- */
.page-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:16px}
.page-head h2{margin:0;font-size:22px;font-weight:680;letter-spacing:-.02em}
.page-head p{margin:3px 0 0;font-size:13.5px}
.groupHead{padding:10px 18px 6px;font-size:11.5px;font-weight:650;letter-spacing:.05em;
  text-transform:uppercase;color:var(--muted);background:var(--surface-2);
  border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
.groupHead:first-child{border-top:none}
.siteDot{width:9px;height:9px;border-radius:3px;display:inline-block;margin-right:8px;vertical-align:1px}
.form2{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.form2 label,.card label{display:block;font-size:12.5px;color:var(--text-2);font-weight:550}

/* ---------- hero ---------- */
.hero{background:linear-gradient(150deg,var(--surface),var(--brand-soft));
  border:1px solid var(--border);border-radius:var(--radius-lg);
  padding:26px;margin-bottom:16px;box-shadow:var(--shadow)}
.hero h2{margin:0 0 4px;font-size:20px;font-weight:680;letter-spacing:-.02em}
.hero p{margin:0 0 18px;color:var(--text-2);font-size:13.5px}
.hero .row2{display:flex;gap:10px;align-items:center;max-width:640px;flex-wrap:wrap}

/* ---------- controls ---------- */
button,.btn{font-family:inherit;font-size:13.5px;font-weight:560;line-height:1;
  padding:9px 14px;border-radius:var(--radius-sm);cursor:pointer;
  border:1px solid var(--brand);background:var(--brand);color:#fff;
  display:inline-flex;align-items:center;justify-content:center;gap:7px;
  transition:background .14s,border-color .14s,box-shadow .14s,transform .06s;
  box-shadow:0 1px 2px rgba(15,23,42,.08)}
button:hover:not(:disabled){background:var(--brand-strong);border-color:var(--brand-strong);text-decoration:none}
button:active:not(:disabled){transform:translateY(1px)}
button:disabled{opacity:.5;cursor:not-allowed}
button.ghost{background:var(--surface);color:var(--text);border-color:var(--border-strong);box-shadow:var(--shadow-sm)}
button.ghost:hover:not(:disabled){background:var(--surface-2);border-color:var(--brand);color:var(--brand)}
button.danger{background:var(--surface);color:var(--danger);border-color:var(--border-strong)}
button.danger:hover:not(:disabled){background:var(--danger-soft);border-color:var(--danger);color:var(--danger)}
button.small{font-size:12.5px;padding:6px 10px}
button:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
input,select{font-family:inherit;font-size:13.5px;width:100%;padding:9px 11px;
  border:1px solid var(--border-strong);border-radius:var(--radius-sm);
  background:var(--surface);color:var(--text);transition:border-color .14s,box-shadow .14s}
input:focus,select:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-ring)}
input::placeholder{color:var(--muted)}
label{font-size:12.5px;color:var(--text-2);font-weight:550}

/* ---------- health strip ---------- */
.health{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.metric{display:flex;flex-direction:column;gap:1px;padding:9px 14px;border-radius:var(--radius);
  background:var(--surface-2);border:1px solid var(--border);min-width:92px}
.metric b{font-size:18px;font-weight:660;letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;line-height:1.2}
.metric span{font-size:11.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;font-weight:550}
.healthState{display:inline-flex;align-items:center;gap:7px;padding:6px 12px;border-radius:99px;
  font-size:12.5px;font-weight:620;border:1px solid}
.healthState .dot{width:7px;height:7px;border-radius:50%;background:currentColor}
.healthState.info{background:var(--ok-soft);color:var(--ok);border-color:color-mix(in srgb,var(--ok) 30%,transparent)}
.healthState.warn{background:var(--warn-soft);color:var(--warn);border-color:color-mix(in srgb,var(--warn) 30%,transparent)}
.healthState.error{background:var(--danger-soft);color:var(--danger);border-color:color-mix(in srgb,var(--danger) 30%,transparent)}
.healthState.idle{background:var(--surface-2);color:var(--muted);border-color:var(--border)}
.healthErr{margin-top:12px;padding:10px 12px;border-radius:var(--radius);
  background:var(--danger-soft);border:1px solid color-mix(in srgb,var(--danger) 26%,transparent);
  color:var(--danger);font-size:13px}

/* ---------- list rows ---------- */
.list{display:flex;flex-direction:column;gap:8px}
.row{padding:13px 15px;border:1px solid var(--border);border-radius:var(--radius);
  background:var(--surface);display:flex;justify-content:space-between;align-items:center;
  gap:14px;transition:border-color .14s,box-shadow .14s,transform .06s}
.row:hover{border-color:var(--border-strong);box-shadow:var(--shadow-sm)}
.row .meta{display:flex;flex-direction:column;gap:5px;min-width:0;flex:1}
.row .title{font-weight:580;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.row .sub{font-size:12px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.row .actions{display:flex;gap:7px;flex-wrap:wrap;flex:0 0 auto}
.rowcode{font-family:var(--mono);font-size:11.5px;color:var(--muted)}

/* The DNS/nginx snippets an operator copies out of the Domains page.
   Monospace, wrapping, and selectable: this is text to paste, not decoration. */
.setupBlock{
  font-family:var(--mono);font-size:12px;line-height:1.5;
  background:var(--bg);border:1px solid var(--border);border-radius:6px;
  padding:10px 12px;margin:6px 0 0;white-space:pre-wrap;word-break:break-word;
  overflow-x:auto;user-select:all;color:var(--text);
}

/* A catalogue row that goes somewhere has to say so.
   The site name and the session count are real links, but a link with no underline and
   the surrounding text colour reads as plain text -- which is exactly how "3 sessions"
   ended up looking like a label while being the only route to those sessions. Inherits
   the row's weight and colour so it does not shout; the underline and pointer carry the
   affordance. */
.row a{color:inherit;text-decoration:none;cursor:pointer}
.row a:hover{text-decoration:underline}
.row a.siteLink{font-weight:600}
.row .title a.siteLink:hover{color:var(--brand)}
/* Keyboard focus has to be visible on the link, not just the buttons. */
.row a:focus-visible{outline:2px solid var(--brand);outline-offset:2px;border-radius:3px}

/* ---------- chips ---------- */
.chip{display:inline-flex;align-items:center;gap:6px;padding:2px 9px;border-radius:99px;
  font-size:11.5px;font-weight:600;border:1px solid;white-space:nowrap}
.chip .dot{width:6px;height:6px;border-radius:50%;background:currentColor}
.chip.info{background:var(--brand-soft);color:var(--brand);border-color:color-mix(in srgb,var(--brand) 24%,transparent)}
.chip.warn{background:var(--warn-soft);color:var(--warn);border-color:color-mix(in srgb,var(--warn) 28%,transparent)}
.chip.error{background:var(--danger-soft);color:var(--danger);border-color:color-mix(in srgb,var(--danger) 28%,transparent)}
.chip.idle{background:var(--surface-2);color:var(--muted);border-color:var(--border)}
.chip.ok{background:var(--ok-soft);color:var(--ok);border-color:color-mix(in srgb,var(--ok) 26%,transparent)}
.age{font-variant-numeric:tabular-nums;color:var(--muted);font-size:12px}
.age.stale{color:var(--warn);font-weight:640}

/* ---------- detail grid ---------- */
.kv{display:grid;grid-template-columns:repeat(auto-fit,minmax(168px,1fr));gap:14px 18px}
.kv>div{min-width:0}
.kv .k{font-size:11.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;font-weight:600}
.kv .v{margin-top:3px;font-size:13.5px;overflow:hidden;text-overflow:ellipsis}
table{width:100%;border-collapse:collapse}
th{font-size:11.5px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);
  text-align:left;font-weight:600;padding:6px 8px;border-bottom:1px solid var(--border)}
td{padding:7px 8px;border-bottom:1px solid var(--border);font-size:13px}
tr:last-child td{border-bottom:none}
td.num{text-align:right;font-variant-numeric:tabular-nums;font-family:var(--mono);font-size:12.5px}

/* ---------- misc ---------- */
.breadcrumb{font-size:12.5px;color:var(--muted);margin-bottom:12px;display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.breadcrumb a{color:var(--muted)}
.breadcrumb a:hover{color:var(--brand)}
.muted{color:var(--muted)}
.small{font-size:12.5px}
.mono{font-family:var(--mono)}
.log{font-family:var(--mono);font-size:11.5px;line-height:1.6;white-space:pre-wrap;
  max-height:340px;overflow:auto;background:var(--surface-2);border:1px solid var(--border);
  border-radius:var(--radius);padding:12px}
.empty{padding:26px 16px;text-align:center;color:var(--muted);font-size:13.5px}
.badge{padding:3px 9px;border-radius:99px;background:var(--brand-soft);color:var(--brand);
  font-size:11.5px;font-weight:620;border:1px solid color-mix(in srgb,var(--brand) 22%,transparent)}

/* ---------- toast ---------- */
.toast{position:fixed;bottom:20px;right:20px;background:var(--text);color:var(--bg-elev);
  padding:11px 16px;border-radius:var(--radius);font-size:13.5px;font-weight:520;
  box-shadow:var(--shadow-lg);transform:translateY(90px);opacity:0;
  transition:transform .22s cubic-bezier(.2,.8,.3,1),opacity .22s;z-index:1000;max-width:min(420px,80vw)}
.toast.show{transform:translateY(0);opacity:1}

/* ---------- auth ---------- */
.login-wrap{min-height:100vh;display:grid;place-items:center;padding:24px;
  background:
    radial-gradient(1000px 520px at 15% -10%,var(--brand-soft),transparent 60%),
    radial-gradient(760px 420px at 100% 0%,color-mix(in srgb,#7c3aed 12%,transparent),transparent 55%),
    var(--bg)}
.login{width:min(400px,100%);background:var(--surface);border:1px solid var(--border);
  border-radius:var(--radius-lg);padding:30px;box-shadow:var(--shadow-lg)}
.login h2{margin:0 0 3px;font-size:20px;font-weight:680;letter-spacing:-.02em}
.login p{margin:0 0 22px;color:var(--muted);font-size:13.5px}
.login input{margin-bottom:11px}
.login button{width:100%;margin-top:6px;padding:11px}

/* ---------- mirror ---------- */
.mirror{position:fixed;inset:0;background:var(--bg);display:none;flex-direction:column;z-index:900}
.mirrorBar{display:flex;justify-content:space-between;align-items:center;gap:12px;
  padding:9px 13px;background:var(--sidebar);color:#cbd5e1;font-family:var(--mono);font-size:12px;
  border-bottom:1px solid #1e293b}
html:not(.mirror-debug) .mirrorExit,html:not(.mirror-debug) .profileBtn{display:none}
.mirrorBar[data-level="warn"]{box-shadow:inset 3px 0 0 var(--warn)}
.mirrorBar[data-level="error"]{box-shadow:inset 3px 0 0 var(--danger)}
.mirrorBar #mTitle{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mirrorBar #mTitle[data-level="error"]{color:#fca5a5}
.mirrorBar #mTitle[data-level="warn"]{color:#fcd34d}
.mirrorExit{flex:0 0 auto;white-space:nowrap;opacity:.85;font-variant-numeric:tabular-nums}
.mirrorExit b{font-weight:600;color:#cfe0ff}
.mirrorExit.is-unknown b{color:#7d8db3;font-weight:400}
.mirrorExit.is-unpinned b{color:var(--warn)}
.mirrorNav{display:flex;align-items:center;gap:6px;flex:0 1 auto;min-width:0}
.mirrorUrl{background:#0a1223;color:#cfe0ff;border:1px solid #24344f;border-radius:6px;
  padding:6px 9px;font:12px var(--mono);width:min(38vw,340px);min-width:110px}
.mirrorBar button.ghost{background:transparent;color:#cbd5e1;border-color:#2b3a55;box-shadow:none}
.mirrorBar button.ghost:hover{background:#1b263c;color:#fff;border-color:#3b4d6e}
#mHost{flex:1;position:relative;background:#fff;min-height:0;overflow:hidden}
#mHost iframe{width:100%;height:100%;border:0;background:#fff}

/* ---------- cloudflare gate ---------- */
.cf-wrap{min-height:100vh;display:none;place-items:center;padding:24px;background:var(--bg)}
.cf-card{width:min(460px,100%);background:var(--surface);border:1px solid var(--border);
  border-radius:var(--radius-lg);padding:32px;box-shadow:var(--shadow-lg);text-align:center}
.cf-title{font-size:18px;font-weight:670;letter-spacing:-.02em;margin:0 0 5px}
.cf-sub{font-size:13px;color:var(--muted);margin:0 0 20px}
.cf-verify{display:flex;align-items:center;justify-content:space-between;gap:14px;
  border:1px solid var(--border);border-radius:var(--radius);padding:15px 17px;
  background:var(--surface-2);margin:0 0 16px;text-align:left}
.cf-verify-left{display:flex;align-items:center;gap:12px;cursor:pointer;user-select:none}
.cf-box{width:27px;height:27px;border:2px solid var(--border-strong);border-radius:7px;
  background:var(--surface);display:grid;place-items:center;flex:0 0 auto;transition:.16s;position:relative}
.cf-box.spin{border-color:var(--border);border-top-color:var(--brand);border-radius:50%;
  animation:cfspin .7s linear infinite}
.cf-box.checked{background:var(--ok);border-color:var(--ok)}
.cf-box.checked::after{content:"✓";color:#fff;font-size:15px;font-weight:700;line-height:1}
.cf-verify-label{font-size:13.5px;font-weight:550}
.cf-verify-right{text-align:right;line-height:1.25}
.cf-logo{font-size:11px;font-weight:700;letter-spacing:.04em;color:var(--text-2)}
.cf-foot{font-size:11.5px;color:var(--muted);margin-top:18px}
@keyframes cfspin{to{transform:rotate(360deg)}}
.spinner{width:15px;height:15px;border:2px solid var(--border);border-top-color:var(--brand);
  border-radius:50%;animation:cfspin .7s linear infinite;display:inline-block;vertical-align:-2px}

/* ---------- responsive ---------- */
@media(max-width:900px){
  .app{grid-template-columns:60px 1fr}
  .sidebar{padding:16px 8px}
  .sidebar .label,.side-foot,.brand span{display:none}
  .brand{justify-content:center;padding:6px 0 16px}
  .nav a{justify-content:center;padding:10px 0}
  .wrap{padding:16px 16px 48px}
  .topbar{padding:14px 16px}
}
@media(max-width:560px){
  .hero{padding:20px}
  .hero .row2{flex-direction:column;align-items:stretch}
  .row{flex-direction:column;align-items:flex-start}
  .row .actions{width:100%}
  /* The bar has a hard floor of roughly 456px: exit address, a 110px URL box and
     four buttons. On a phone it pushed the view sideways, so it wraps instead. */
  .mirrorBar{flex-wrap:wrap;gap:6px;padding:7px 9px}
  .mirrorBar #mTitle{flex:1 1 100%;order:1}
  .mirrorExit{order:2;flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}
  .mirrorNav{order:3;flex:1 1 100%;min-width:0}
  .mirrorUrl{flex:1 1 auto;width:auto;min-width:0}
  .mirrorBar>button{order:4}
}
@media (prefers-reduced-motion:reduce){
  *{animation-duration:.01ms !important;transition-duration:.01ms !important}
}

/* The React console's stylesheet.
 *
 * It imports the shipped design system rather than restating it. app.css holds
 * the tokens (--brand, --ok-fg, --surface-0 …) and the legacy console's own
 * classes; restating the tokens here would create exactly the drift
 * js/palette_parity.test.mjs exists to prevent, and adding React's classes to
 * app.css would mean editing a file the imperative console still reads.
 *
 * The React app previously shipped with no stylesheet at all -- every class below
 * was undefined, which is why the React build looked like an unstyled document
 * rather than like a broken one. *//* Mirror console -- design system.
 *
 * One stylesheet, sections in the order a reader needs them. The token values are
 * the ones already shipped in internal/tpl/templates/admin.css: this console and
 * the Platform panel cannot share a file (neither can see the other), so they share
 * VALUES, and js/palette_parity.test.mjs is what keeps them honest.
 *
 * Sampled from screenshots/, not eyeballed. The first version had the page and the
 * sidebar backwards -- grey page, white cards -- where the reference is a white
 * page, a grey sidebar and white cards. Nothing in it was wrong in isolation and
 * it read as a different product. */

:root {
  /* Surfaces. Cards carry NO drop shadow: a 1px border against the page does the
     work, and that single decision is most of what makes the reference read clean. */
  --surface-0: #ffffff;   /* page, cards, panels, topbar */
  --surface-1: #fafafa;   /* SIDEBAR, and row hover */
  --surface-2: #f2f2f2;   /* inset, active sidebar item, neutral pill */
  --surface-3: #e5e5e5;   /* skeleton base */

  --border: #e3e3e3;
  --border-strong: #d4d4d4;
  --border-focus: #4f6ef7;

  /* The reference's text is near-black, not the blue-black most palettes ship,
     and the difference is visible across a whole page of it. */
  --fg: #0a0a0a;
  --fg-muted: #737373;
  --fg-faint: #929292;    /* TABLE HEADERS ONLY */
  --fg-subtle: #a1a1aa;   /* placeholders, disabled, "not set" */
  --fg-invert: #ffffff;

  /* The reference accent is a soft periwinkle, #90a8fa. White text on it is 1.9:1,
     which fails WCAG AA by a factor of two, so the brand is darkened into the same
     hue family for anything carrying text and the reference value is kept for fills
     that carry none. Matching the reference exactly would inherit its a11y bug. */
  --brand: #4f6ef7;
  --brand-hover: #3d5ce0;
  --brand-soft: #90a8fa;
  --brand-subtle: #eef1fe;
  --brand-border: #c7d0fd;

  /* Destructive actions are TINTED, not solid. A solid red button on every row of a
     table reads as an alarm rather than as an available action, and a page where
     nothing is wrong should not be able to alarm you at a glance. */
  --danger: #dc2626;
  --danger-bg: #fef2f2;
  --danger-border: #fecaca;

  /* Six tones, and `neutral` is not `bad`. A disabled gate is a decision, not a
     fault; painting it red makes a correct configuration look broken. */
  /* Named -fg/-bg/-border per tone, matching the Platform panel exactly.
   *
   * This file originally invented its own single-token tones (--ok, --warn, --bad)
   * with slightly different backgrounds, which js/palette_parity.test.mjs caught:
   * the same green at two values is two greens, and "the consoles share one
   * palette" is not a claim you can make with a translation table full of
   * near-misses. One naming, one value. */
  --ok-bg: #ecfdf5;      --ok-fg: #047857;      --ok-border: #a7f3d0;
  --warn-bg: #fffbeb;    --warn-fg: #b45309;    --warn-border: #fde68a;
  --bad-bg: #fef2f2;     --bad-fg: #b91c1c;     --bad-border: #fecaca;
  --pending-bg: #f5f3ff; --pending-fg: #6d28d9; --pending-border: #ddd6fe;
  --neutral-bg: #f4f4f5; --neutral-fg: #52525b; --neutral-border: #e4e4e7;

  --radius: 8px;
  --radius-sm: 6px;
  --sidebar-w: 232px;
  --topbar-h: 52px;

  --mono: ui-monospace, SFMono-Regular, Menlo, monospace;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* ---------------------------------------------------------------- reset */
*, *::before, *::after { box-sizing: border-box; }

/* The UA's [hidden] rule is display:none at author-origin-beating-nothing: any
   class that sets `display` beats it, because a class selector outranks the UA
   sheet. So `hidden` silently does nothing on .modal-backdrop (display:grid) and
   on .shell (display:grid) -- which is exactly where this console toggles it.
   Without this, "signed out" and "signed in" render identically. */
[hidden] { display: none !important; }

html, body { height: 100%; }
body {
  margin: 0;
  font: 14px/1.5 var(--sans);
  color: var(--fg);
  background: var(--surface-0);
  -webkit-font-smoothing: antialiased;
}
button, input, select, textarea { font: inherit; color: inherit; }
a { color: var(--brand); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ---------------------------------------------------------------- shell */
.shell { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }

.sidebar {
  background: var(--surface-1);
  border-right: 1px solid var(--border);
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
}
.sidebar__brand {
  display: flex; align-items: center; gap: 10px;
  padding: 16px 16px 12px; font-weight: 650; font-size: 15px;
}
.sidebar__mark {
  width: 28px; height: 28px; border-radius: 7px; flex: 0 0 auto;
  background: var(--brand); color: var(--fg-invert);
  display: grid; place-items: center; font-size: 13px; font-weight: 700;
}
.sidebar__group {
  padding: 12px 16px 4px; font-size: 11px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--fg-faint); font-weight: 600;
}
.sidebar__item {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 16px; color: var(--fg-muted); cursor: pointer;
  border: none; background: none; width: 100%; text-align: left;
  border-left: 2px solid transparent;
}
.sidebar__item:hover { background: var(--surface-2); color: var(--fg); }
.sidebar__item.is-active {
  background: var(--surface-2); color: var(--fg); font-weight: 550;
  border-left-color: var(--brand);
}
.sidebar__item svg { width: 15px; height: 15px; flex: 0 0 auto; }
.sidebar__foot { margin-top: auto; padding: 12px 16px; border-top: 1px solid var(--border); }

.main { display: flex; flex-direction: column; min-width: 0; }

.topbar {
  height: var(--topbar-h); flex: 0 0 auto;
  display: flex; align-items: center; gap: 12px;
  padding: 0 20px; border-bottom: 1px solid var(--border);
  background: var(--surface-0); position: sticky; top: 0; z-index: 20;
}
.topbar__title { font-size: 15px; font-weight: 600; }
.topbar__spacer { flex: 1; }

.view { padding: 24px 20px 56px; max-width: 1500px; width: 100%; }

.page-head {
  display: flex; align-items: flex-start; gap: 16px; margin-bottom: 20px;
}
.page-head h1 { margin: 0; font-size: 22px; font-weight: 650; letter-spacing: -.01em; }
.page-head p { margin: 2px 0 0; color: var(--fg-muted); }
.page-head__actions { margin-left: auto; display: flex; gap: 8px; flex: 0 0 auto; }

/* ---------------------------------------------------------------- buttons */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  height: 32px; padding: 0 13px; border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong); background: var(--surface-0);
  color: var(--fg); cursor: pointer; white-space: nowrap; font-size: 13px;
}
.btn:hover { background: var(--surface-1); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn--primary {
  background: var(--brand); border-color: var(--brand); color: var(--fg-invert);
}
.btn--primary:hover { background: var(--brand-hover); border-color: var(--brand-hover); }
.btn--danger {
  background: var(--danger-bg); border-color: var(--danger-border); color: var(--danger);
}
.btn--danger:hover { background: #fee2e2; }
.btn--sm { height: 28px; padding: 0 10px; font-size: 12.5px; }
.btn--icon { width: 32px; padding: 0; justify-content: center; }
.btn svg { width: 14px; height: 14px; }

/* ---------------------------------------------------------------- cards */
.card {
  background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--radius); margin-bottom: 18px;
}
.card__head { padding: 16px 18px; }
.card__head h2 { margin: 0; font-size: 15px; font-weight: 600; }
.card__head p { margin: 2px 0 0; color: var(--fg-muted); font-size: 13px; }
.card__body { padding: 0 18px 18px; }
.card__foot {
  padding: 12px 18px; border-top: 1px solid var(--border);
  display: flex; align-items: center; gap: 10px;
}

/* ---------------------------------------------------------------- tables
 * Tabular for everything, as asked. Headers are sentence case, normal weight and
 * LIGHTER than the body: the reference does that, and uppercase+letterspaced
 * headers shout over the data they label. */
.table-wrap { overflow-x: auto; }
table.tbl { width: 100%; border-collapse: collapse; font-size: 13.5px; }
table.tbl th {
  text-align: left; font-weight: 500; color: var(--fg-faint);
  padding: 10px 14px; border-bottom: 1px solid var(--border); white-space: nowrap;
}
table.tbl td {
  padding: 12px 14px; border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
table.tbl tbody tr:last-child td { border-bottom: none; }
table.tbl tbody tr:hover { background: var(--surface-1); }
.tbl__id { font-family: var(--mono); font-size: 12px; color: var(--fg-muted); }
.tbl__actions { text-align: right; white-space: nowrap; }
.tbl__actions .btn + .btn { margin-left: 6px; }
.tbl__num { font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------- pills */
.pill {
  display: inline-flex; align-items: center; gap: 5px;
  height: 21px; padding: 0 8px; border-radius: 999px;
  font-size: 12px; font-weight: 500;
  border: 1px solid var(--neutral-border);
  background: var(--neutral-bg); color: var(--neutral-fg);
}
.pill--ok { background: var(--ok-bg); border-color: var(--ok-border); color: var(--ok-fg); }
.pill--warn { background: var(--warn-bg); border-color: var(--warn-border); color: var(--warn-fg); }
.pill--bad { background: var(--bad-bg); border-color: var(--bad-border); color: var(--bad-fg); }
.pill--pending { background: var(--pending-bg); border-color: var(--pending-border); color: var(--pending-fg); }
.pill__dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

/* ---------------------------------------------------------------- forms */
.field { margin-bottom: 14px; }
.field label { display: block; font-size: 13px; font-weight: 550; margin-bottom: 5px; }
.field .hint { color: var(--fg-muted); font-size: 12.5px; margin-top: 5px; }
.input, .select, textarea.input {
  width: 100%; height: 34px; padding: 0 11px;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface-0);
}
textarea.input { height: auto; padding: 9px 11px; resize: vertical; }
.input:focus, .select:focus, textarea.input:focus {
  outline: none; border-color: var(--border-focus);
  box-shadow: 0 0 0 3px var(--brand-subtle);
}
.row { display: flex; gap: 12px; }
.row > * { flex: 1; min-width: 0; }

/* Visible focus for everything reachable by keyboard.
 *
 * The inputs above use :focus because a bordered field changing colour is the
 * clearest possible signal and suppressing it on mouse clicks would be a small
 * loss for a large accessibility one. Buttons and links have no resting border to
 * recolour, so they get a ring -- and :focus-visible rather than :focus, so it
 * appears for a keyboard user and not for every mouse click.
 *
 * This was missing and was found by js/style.test.mjs, which asks both consoles
 * the same question. Removing an outline without replacing it makes a page
 * unusable by keyboard and invisible in a screenshot, which is the worst way for
 * an accessibility regression to hide. */
:where(button, a, [tabindex]):focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.search { position: relative; margin-bottom: 14px; max-width: 420px; }
.search .input { padding-left: 32px; }
.search svg {
  position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; color: var(--fg-subtle); pointer-events: none;
}

/* ---------------------------------------------------------------- modal */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(10, 10, 10, .45);
  display: grid; place-items: center; padding: 24px;
}
.modal {
  background: var(--surface-0); border-radius: 10px;
  width: min(560px, 100%); max-height: 90vh; overflow-y: auto;
  box-shadow: 0 10px 40px rgba(0, 0, 0, .18);
}
.modal__head { padding: 18px 20px 0; }
.modal__head h2 { margin: 0; font-size: 17px; font-weight: 650; }
.modal__head p { margin: 3px 0 0; color: var(--fg-muted); font-size: 13px; }
.modal__body { padding: 18px 20px; }
.modal__foot {
  padding: 14px 20px; border-top: 1px solid var(--border);
  display: flex; gap: 8px; justify-content: flex-end;
}
.modal__err {
  background: var(--bad-bg); border: 1px solid var(--bad-border);
  color: var(--bad-fg); border-radius: var(--radius-sm);
  padding: 9px 12px; font-size: 13px; margin-bottom: 14px;
}

/* Steps, for the three-step create flow. */
.steps { display: flex; align-items: center; gap: 8px; margin-bottom: 18px; }
.step { display: flex; align-items: center; gap: 7px; font-size: 13px; color: var(--fg-subtle); }
.step__dot {
  width: 21px; height: 21px; border-radius: 50%; display: grid; place-items: center;
  background: var(--surface-2); color: var(--fg-subtle); font-size: 11.5px; font-weight: 600;
}
.step.is-active { color: var(--fg); font-weight: 550; }
.step.is-active .step__dot { background: var(--brand); color: var(--fg-invert); }
.step.is-done .step__dot { background: var(--ok-bg); color: var(--ok-fg); }
.step__sep { flex: 1; height: 1px; background: var(--border); }

/* The template picker: a grid of choices, not a dropdown, because picking a
   template is the decision the whole flow is about and a <select> hides it. */
.tpl-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
.tpl {
  text-align: left; padding: 10px 12px; cursor: pointer;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface-0);
}
.tpl:hover { border-color: var(--brand-soft); background: var(--brand-subtle); }
.tpl.is-picked { border-color: var(--brand); background: var(--brand-subtle); }
.tpl__name { font-weight: 550; font-size: 13.5px; }
.tpl__url { color: var(--fg-muted); font-size: 12px; margin-top: 1px;
            overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The live preview of what will be created. Shown because the whole point of the
   flow is the resulting address, and a person should never have to assemble it in
   their head from two fields. */
.preview {
  font-family: var(--mono); font-size: 13px;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 10px 12px; margin-top: 6px;
  word-break: break-all;
}
.preview__host { color: var(--fg-muted); }

/* ---------------------------------------------------------------- misc */
.empty { padding: 34px 18px; text-align: center; color: var(--fg-muted); }
.toast {
  position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
  background: var(--fg); color: var(--fg-invert);
  padding: 9px 16px; border-radius: var(--radius-sm); z-index: 200; font-size: 13px;
}
.toast--bad { background: var(--danger); }
.mono { font-family: var(--mono); }
.muted { color: var(--fg-muted); }
.faint { color: var(--fg-faint); }
.nowrap { white-space: nowrap; }

/* Below 768 the sidebar is a drawer, because a fixed 232px column on a phone
   leaves the table 130px wide. */
@media (max-width: 768px) {
  .shell { grid-template-columns: 1fr; }
  .sidebar {
    position: fixed; z-index: 90; width: var(--sidebar-w);
    transform: translateX(-100%); transition: transform .16s ease;
  }
  .sidebar.is-open { transform: none; }
  .page-head { flex-wrap: wrap; }
  .page-head__actions { margin-left: 0; width: 100%; }
  .tpl-grid { grid-template-columns: 1fr; }
}
/* Dashboard stat cards, as the reference leads with.
   Border-only, no shadow: the same rule as every card. */
.statgrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px;
  margin-bottom: 18px;
}
.statcard {
  background: var(--surface-0);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
}
.statcard__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 8px;
}
.statcard__title { color: var(--fg-muted); font-size: 12.5px; }
.statcard__value {
  font-size: 30px;
  font-weight: 650;
  letter-spacing: -.02em;
  line-height: 1.1;
}
/* A zero in the reference's cards is large and near-black. Here a zero that
   means "capture is off" must not look identical to a zero that means "nothing
   captured", so the value follows the same absent-vs-zero rule the counts use. */
.statcard__value .faint { font-size: 30px; font-weight: 650; }
.statcard__sub { color: var(--fg-muted); font-size: 12.5px; margin-top: 5px; }

/* Mammoth detail page. A page, not a modal: it holds five sections and needs to be
   linkable, so "look at that capture" is a URL rather than something read out. */
.detail__crumb { font-size: 13px; margin-bottom: 12px; }
.detail__crumb a { color: inherit; }

/* The shipped spec, shown as source. Monospace at a readable measure, because
   that is what the document is -- and a markdown renderer would add a dependency
   for no gain. */
pre.doc {
  font: 13px/1.65 var(--mono);
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0;
  color: var(--fg);
}
:root {
  /* Aliases the React views use by name. app.css names the foregrounds --fg-muted,
     --fg-faint, --fg-subtle -- and the React side grew its own names for the same
     three values. These are the translation, declared once. */
  --muted: var(--fg-muted);
  --faint: var(--fg-faint);
  --subtle: var(--fg-subtle);
  --bg: var(--surface-0);
  --bg-subtle: var(--surface-1);
  --text: var(--fg);
  --ok: var(--ok-fg);
  --warn: var(--warn-fg);
  /* Dark-scheme quarantine.
   *
   * main.jsx imports the older index.html.css first for its base components,
   * and that sheet honours prefers-color-scheme: dark -- right for the page it
   * was written for, wrong for this one. The aliases above already pin light
   * values and win the cascade as the later stylesheet, but only for tokens
   * both sheets define. Any token defined ONLY over there keeps its dark value
   * while the shell stays light: cards and rows went dark navy with near-black
   * text on them, which is the exact failure this block exists to prevent.
   *
   * So every remaining color token that sheet owns is pinned here to the light
   * palette, by alias rather than by value: a second copy of a hex is a second
   * palette, which is what js/palette_parity.test.mjs exists to catch.
   * js/darkmode.test.mjs resolves the whole chain under a dark scheme and fails
   * if any surface ever computes dark again.
   *
   * Deliberately not a full dark theme: until one exists the console is light in
   * every color scheme, which is honest rather than half-dark. Shadows and the
   * sidebar tokens are untouched -- shadows carry no text, and nothing in these
   * views reads the sidebar tokens. */
  --surface: var(--surface-0);
  --surface-2: var(--surface-1);
  --bg-elev: var(--surface-0);
  --text-2: var(--fg-muted);
  --brand-strong: var(--brand-hover);
  --brand-ring: var(--brand-border);
  --brand-soft: var(--brand-subtle);
  --ok-soft: var(--ok-bg);
  --warn-soft: var(--warn-bg);
  --danger-soft: var(--danger-bg);
}

html, body, #root { height: 100%; }
body { margin: 0; background: var(--bg); color: var(--text); font-family: var(--sans); font-size: 14px; }

/* ------------------------------------------------------------------ shell */
.app { display: flex; min-height: 100vh; }

.sidebar {
  width: var(--sidebar-w);
  flex: 0 0 var(--sidebar-w);
  background: var(--surface-1);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  padding: 14px 10px;
  gap: 2px;
  overflow-y: auto;
}
.brand { display: flex; align-items: center; gap: 9px; font-weight: 600; font-size: 15px; padding: 4px 8px 14px; }
.mark {
  width: 22px; height: 22px; border-radius: 6px;
  background: var(--brand); color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 700;
}

.nav { display: flex; flex-direction: column; gap: 1px; flex: 1; }
.nav a {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 9px; border-radius: var(--radius-sm);
  color: var(--text); text-decoration: none; font-size: 13.5px;
}
.nav a:hover { background: var(--surface-2); }
.nav a.active { background: var(--brand-subtle); color: var(--brand); font-weight: 500; }
.nav .glyph { width: 16px; text-align: center; opacity: .7; flex: 0 0 16px; }
.nav .label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.groupHead {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--faint); font-weight: 600;
  padding: 14px 9px 5px;
}

.side-foot { border-top: 1px solid var(--border); padding: 10px 8px 2px; font-size: 12px; display: flex; flex-direction: column; gap: 5px; }
.side-foot .who { color: var(--muted); font-family: var(--mono); font-size: 11px; }
.side-foot a { color: var(--muted); cursor: pointer; text-decoration: none; }
.side-foot a:hover { color: var(--text); }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.topbar {
  height: var(--topbar-h);
  display: flex; align-items: center; gap: 12px;
  padding: 0 20px; border-bottom: 1px solid var(--border);
  background: var(--bg);
}
.topbar h1 { font-size: 15px; font-weight: 600; margin: 0; }
.topbar .sub { font-size: 12px; color: var(--muted); }
.topbar__spacer { flex: 1; }
.wrap { padding: 20px; display: flex; flex-direction: column; gap: 16px; max-width: 1180px; }

/* ------------------------------------------------------------------- page */
.page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.page-head h2 { margin: 0 0 4px; font-size: 19px; }
.page-head p { margin: 0; font-size: 13px; max-width: 70ch; }
.page-head__actions { display: flex; gap: 8px; flex: 0 0 auto; }
.breadcrumb { font-size: 12px; color: var(--muted); margin-bottom: 6px; }
.breadcrumb a { color: var(--brand); text-decoration: none; }

.card {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.card.flush { padding: 0; }
.card__head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 14px; border-bottom: 1px solid var(--border);
}
.card__head h3 { margin: 0; font-size: 13.5px; font-weight: 600; }
.card__head h3 small { font-weight: 400; }
.card__body { padding: 14px; }
.card__foot { padding: 12px 14px; border-top: 1px solid var(--border); display: flex; gap: 8px; justify-content: flex-end; }

/* ------------------------------------------------------------------- rows */
.row {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 14px; border-bottom: 1px solid var(--border);
}
.row:last-child { border-bottom: 0; }
.row:hover { background: var(--surface-1); }
.row .meta { flex: 1; min-width: 0; }
.row .title { font-size: 13.5px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.row .sub { font-size: 12px; color: var(--muted); margin-top: 2px; overflow-wrap: anywhere; }
.row .actions { display: flex; gap: 6px; flex: 0 0 auto; }

.siteLink { color: var(--brand); text-decoration: none; }
.siteLink:hover { text-decoration: underline; }
.rowcode { font-family: var(--mono); font-size: 12px; }

/* ------------------------------------------------------------------ chips */
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11.5px; font-weight: 500;
  padding: 2px 8px; border-radius: 999px;
  background: var(--neutral-bg); color: var(--neutral-fg);
  border: 1px solid var(--neutral-border);
}
.chip .dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.chip.ok { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-border); }
.chip.warn { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-border); }
.chip.error { background: var(--bad-bg); color: var(--bad-fg); border-color: var(--bad-border); }
.chip.idle { background: var(--neutral-bg); color: var(--neutral-fg); border-color: var(--neutral-border); }
.chip.info { background: var(--brand-subtle); color: var(--brand); border-color: var(--brand-border); }

.pill {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11.5px; padding: 2px 8px; border-radius: 999px;
  background: var(--neutral-bg); color: var(--neutral-fg); border: 1px solid var(--neutral-border);
}
.pill__dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.pill--ok { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-border); }
.pill--warn { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-border); }
.pill--bad { background: var(--bad-bg); color: var(--bad-fg); border-color: var(--bad-border); }
.pill--pending { background: var(--pending-bg); color: var(--pending-fg); border-color: var(--pending-border); }
.pill--info { background: var(--brand-subtle); color: var(--brand); border-color: var(--brand-border); }
.pill--neutral { background: var(--neutral-bg); color: var(--neutral-fg); border-color: var(--neutral-border); }

.badge {
  font-size: 11px; padding: 2px 8px; border-radius: 999px;
  background: var(--surface-2); color: var(--muted); border: 1px solid var(--border);
}

/* ------------------------------------------------------------------ health */
.health { display: flex; gap: 18px; align-items: center; flex-wrap: wrap; }
.healthState { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 500; }
.healthState .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.healthState.ok, .healthState.info { color: var(--ok-fg); }
.healthState.warn { color: var(--warn-fg); }
.healthState.error { color: var(--bad-fg); }
.healthState.idle { color: var(--muted); }
.metric { display: flex; flex-direction: column; gap: 1px; }
.metric b { font-size: 15px; font-weight: 600; }
.metric span { font-size: 11px; color: var(--muted); }
.healthErr {
  margin-top: 10px; padding: 9px 11px; border-radius: var(--radius-sm);
  background: var(--bad-bg); border: 1px solid var(--bad-border); color: var(--bad-fg); font-size: 12.5px;
}

/* ----------------------------------------------------------------- tables */
.table-wrap { overflow-x: auto; }
.tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
.tbl th {
  text-align: left; font-weight: 500; font-size: 11.5px;
  color: var(--faint); padding: 8px 10px;
  border-bottom: 1px solid var(--border); white-space: nowrap;
}
.tbl td { padding: 9px 10px; border-bottom: 1px solid var(--border); vertical-align: top; }
.tbl tbody tr:last-child td { border-bottom: 0; }
.tbl tbody tr:hover { background: var(--surface-1); }
.tbl__num { text-align: right; font-variant-numeric: tabular-nums; }
.tbl__actions { text-align: right; white-space: nowrap; }
.tbl__id { font-family: var(--mono); font-size: 12px; }

/* ------------------------------------------------------------------ forms */
.field { display: flex; flex-direction: column; gap: 5px; margin-bottom: 12px; }
.field__label { font-size: 12px; font-weight: 500; color: var(--muted); }
input, select, textarea {
  font: inherit; font-size: 13px;
  padding: 7px 9px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--bg); color: var(--text);
  width: 100%;
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--border-focus);
  box-shadow: 0 0 0 3px var(--brand-subtle);
}
input::placeholder { color: var(--subtle); }
.form2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }

.search { position: relative; display: flex; align-items: center; }
.search span { position: absolute; left: 9px; color: var(--faint); font-size: 14px; }
.search input { padding-left: 26px; }

.secret { display: flex; align-items: center; gap: 8px; padding: 5px 0; }
.secret__label { font-size: 12px; color: var(--muted); width: 90px; flex: 0 0 90px; }
.secret__value { flex: 1; min-width: 0; overflow-wrap: anywhere; }

.setupBlock, .doc, .log {
  font-family: var(--mono); font-size: 12px; line-height: 1.5;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 10px 12px;
  margin: 6px 0; white-space: pre-wrap; overflow-wrap: anywhere; max-height: 420px; overflow: auto;
}
.doc { font-family: var(--sans); font-size: 13px; }

/* ----------------------------------------------------------------- buttons */
button, .btn {
  font: inherit; font-size: 13px; font-weight: 500;
  padding: 7px 13px; border-radius: var(--radius-sm);
  border: 1px solid var(--brand); background: var(--brand); color: #fff;
  cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  width: auto;
}
button:hover:not(:disabled) { background: var(--brand-hover); border-color: var(--brand-hover); }
button:disabled { opacity: .55; cursor: not-allowed; }
button.ghost, .btn.ghost {
  background: transparent; color: var(--text); border-color: var(--border);
}
button.ghost:hover:not(:disabled) { background: var(--surface-2); border-color: var(--border-strong); }
button.danger, .btn--danger { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-border); }
button.danger:hover:not(:disabled), .btn--danger:hover:not(:disabled) { background: var(--danger); color: #fff; }
.btn--primary { background: var(--brand); color: #fff; }
.small, .btn--sm { font-size: 12px; padding: 4px 9px; }

/* ----------------------------------------------------------------- modals */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(10, 10, 10, .45);
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
.modal {
  background: var(--bg); border-radius: var(--radius);
  width: min(440px, 100%); max-height: 88vh; overflow-y: auto;
  box-shadow: 0 12px 40px rgba(0, 0, 0, .18);
}
.modal__head { padding: 16px 18px 0; }
.modal__head h3 { margin: 0; font-size: 15px; }
.modal__body { padding: 14px 18px; }
.modal__foot { padding: 12px 18px; border-top: 1px solid var(--border); display: flex; gap: 8px; justify-content: flex-end; }

/* ------------------------------------------------------------------ misc */
.empty { padding: 26px 16px; text-align: center; color: var(--muted); font-size: 13px; }
.muted { color: var(--muted); }
.faint { color: var(--faint); }
.mono { font-family: var(--mono); }
.nowrap { white-space: nowrap; }
.small { font-size: 12px; }

.spinner {
  display: inline-block; width: 11px; height: 11px;
  border: 2px solid var(--border-strong); border-top-color: var(--brand);
  border-radius: 50%; animation: spin .7s linear infinite; vertical-align: -1px;
}
@keyframes spin { to { transform: rotate(360deg); } }

.statgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; }
.statcard {
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px; background: var(--bg);
}
.statcard__title { font-size: 12px; color: var(--muted); }
.statcard__value { font-size: 26px; font-weight: 600; margin: 6px 0 2px; }
.statcard__sub { font-size: 11.5px; color: var(--faint); }

.steps { display: flex; align-items: center; gap: 8px; margin-bottom: 14px; }
.step { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
.step__dot {
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--surface-2); color: var(--muted);
  display: inline-flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 600;
}
.step.on .step__dot { background: var(--brand); color: #fff; }
.step__sep { width: 18px; height: 1px; background: var(--border-strong); }

.login-wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
.login { width: min(380px, 100%); }

@media (max-width: 860px) {
  .app { flex-direction: column; }
  .sidebar { width: 100%; flex: none; flex-direction: row; overflow-x: auto; }
  .nav { flex-direction: row; }
  .groupHead { display: none; }
}