/* ===========================================================================
   Guinevere admin — design system.

   Visual language borrowed from the Mel5 operator console: near-black ground,
   hairline borders, a single green accent, and no rounded corners anywhere.
   Depth comes from a background ladder plus 1px rules — never from shadow.

   Three deliberate departures from the source:
     1. The fonts are actually loaded. Mel5 names Inter, IBM Plex Mono and
        Space Grotesk but ships none of them, so it silently falls back to
        system faces. Copying that stylesheet verbatim reproduces the bug
        rather than the design. They are self-hosted here, not pulled from a
        CDN — this panel should not report every page view to a third party.
     2. There is a responsive layer. The source has no viewport tag and not one
        media query. The owner will open this on a phone while pairing WhatsApp.
     3. Focus is visible and the accent passes contrast on buttons (dark text on
        accent, not white). The source fails both.
   ======================================================================== */

/* --- fonts ------------------------------------------------------------- */
@font-face { font-family: "Inter"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("/admin/static/fonts/inter-400.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 500;
  font-display: swap; src: url("/admin/static/fonts/inter-500.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 600;
  font-display: swap; src: url("/admin/static/fonts/inter-600.woff2") format("woff2"); }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("/admin/static/fonts/plexmono-400.woff2") format("woff2"); }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500;
  font-display: swap; src: url("/admin/static/fonts/plexmono-500.woff2") format("woff2"); }
@font-face { font-family: "Space Grotesk"; font-style: normal; font-weight: 500;
  font-display: swap; src: url("/admin/static/fonts/grotesk-500.woff2") format("woff2"); }

/* --- tokens ------------------------------------------------------------ */
:root {
  --bg:            #0b0b0c;
  --panel:         #15151a;
  --surface-alt:   #101015;
  --recessed:      #08080a;
  --border:        #262630;
  --border-strong: #34343f;
  --text:          #e8e6e3;
  --muted:         #9aa1a8;
  --accent:        #1d9e75;
  --accent-hover:  #0f6e56;
  --warn:          #e8b84e;
  --err:           #e87474;
  --in:            #7aa6e8;   /* inbound  */
  --out:           #9be8a8;   /* outbound */

  --ui:   "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --disp: "Space Grotesk", "Inter", sans-serif;

  --gap: 1rem;
  --max: 1400px;
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--ui);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

/* The three geometry rules that carry most of the character. */
*, button, input, select, textarea { border-radius: 0; }
* { box-shadow: none; }
@media (prefers-reduced-motion: no-preference) { * { transition: none; } }

h1, h2, h3 { font-family: var(--disp); font-weight: 500; letter-spacing: -0.01em; margin: 0 0 .5rem; }
h1 { font-size: 1.5rem; }
h2 { font-size: 1.15rem; }
h3 { font-size: 1rem; }
p  { margin: 0 0 .75rem; }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); text-decoration: underline; }

code, .mono, kbd { font-family: var(--mono); font-size: .86em; }

/* Every ID, timestamp and path is monospaced — the instrument-panel tell. */
.muted { color: var(--muted); }
.small { font-size: .82rem; }

/* The label tier. One rule, applied everywhere, does most of the work. */
.label, th, .kpi-label, .eyebrow {
  font-family: var(--mono);
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  font-weight: 500;
  color: var(--muted);
}

/* --- focus (absent in the source) --------------------------------------- */
:where(a, button, input, select, textarea, summary):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* --- top bar ------------------------------------------------------------ */
.topbar {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  border-bottom: 1px solid var(--border);
  background: var(--surface-alt);
  padding: 0 1.25rem;
  min-height: 52px;
  flex-wrap: wrap;
}
.topbar .brand {
  font-family: var(--disp);
  font-size: 1.05rem;
  font-weight: 500;
  color: var(--text);
  letter-spacing: .02em;
  padding: .85rem 0;
}
.topbar .brand:hover { color: var(--accent); text-decoration: none; }
.topbar .brand .dot { color: var(--accent); }

.topbar nav { display: flex; gap: .25rem; flex: 1; flex-wrap: wrap; }
.topbar nav a {
  color: var(--muted);
  font-size: .82rem;
  padding: .5rem .7rem;
  border: 1px solid transparent;
}
.topbar nav a:hover { color: var(--text); background: var(--panel); text-decoration: none; }
.topbar nav a.active {
  color: var(--accent);
  border-color: var(--border);
  background: var(--panel);
}

.topbar .session { display: flex; align-items: center; gap: .6rem; }
.topbar .session .who { font-family: var(--mono); font-size: .74rem; color: var(--muted); }

/* --- layout ------------------------------------------------------------- */
main { max-width: var(--max); margin: 0 auto; padding: 1.5rem 1.25rem 4rem; }

.page-head { margin-bottom: 1.25rem; }
.page-head .eyebrow { display: block; margin-bottom: .25rem; }

.row { display: flex; gap: var(--gap); flex-wrap: wrap; }
.col { flex: 1; min-width: 0; }
.grid { display: grid; gap: var(--gap); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }

/* --- panel -------------------------------------------------------------- */
.panel {
  background: var(--panel);
  border: 1px solid var(--border);
  padding: 1.1rem 1.25rem;
  margin-bottom: var(--gap);
}
.panel > :last-child { margin-bottom: 0; }
.panel-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; margin-bottom: .85rem; flex-wrap: wrap;
}
.panel-head h2, .panel-head h3 { margin: 0; }
/* Tables sit flush inside their panel — the panel's own border is the frame. */
.panel.flush { padding: 0; }
.panel.flush .panel-head { padding: 1rem 1.25rem 0; margin-bottom: .75rem; }

/* --- KPI ---------------------------------------------------------------- */
.kpi {
  background: var(--panel);
  border: 1px solid var(--border);
  padding: .9rem 1rem;
}
.kpi-value {
  font-family: var(--disp);
  font-size: 1.6rem;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  display: block;
  margin-bottom: .3rem;
}
.kpi-value.ok   { color: var(--accent); }
.kpi-value.warn { color: var(--warn); }
.kpi-value.err  { color: var(--err); }
.kpi-label { display: block; }

/* --- tables ------------------------------------------------------------- */
/* Wide tables scroll in their own container so the page body never does. */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table { border-collapse: collapse; width: 100%; font-size: .86rem; }
th {
  text-align: left;
  background: var(--surface-alt);
  padding: .55rem .8rem;
  border-bottom: 1px solid var(--border-strong);
  white-space: nowrap;
}
td {
  padding: .6rem .8rem;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: var(--surface-alt); }
td.mono, td.num { font-family: var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Empty states name the next action rather than saying "no data". */
.empty { color: var(--muted); text-align: right; font-size: .84rem; padding: 1rem .8rem; }

/* --- tags --------------------------------------------------------------- */
/* Semantics are explicit. The source overloaded one "open" class to mean
   thread-open, exit-ok, systemd-active and port-open all at once. */
.tag {
  display: inline-block;
  font-family: var(--mono);
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  padding: .12rem .45rem;
  border: 1px solid var(--border-strong);
  color: var(--muted);
  white-space: nowrap;
}
.tag-ok      { color: var(--accent); border-color: var(--accent); }
.tag-warn    { color: var(--warn);   border-color: var(--warn); }
.tag-err     { color: var(--err);    border-color: var(--err); }
.tag-pending { color: var(--muted);  border-color: var(--border-strong); }
.tag-in      { color: var(--in);     border-color: var(--in); }
.tag-out     { color: var(--out);    border-color: var(--out); }

.dot-status { display: inline-block; width: .5rem; height: .5rem; margin-right: .4rem; background: var(--muted); }
.dot-status.ok  { background: var(--accent); }
.dot-status.err { background: var(--err); }

/* --- forms -------------------------------------------------------------- */
label { display: block; font-size: .84rem; margin-bottom: .9rem; }
label > .label { display: block; margin-bottom: .3rem; }

input[type=text], input[type=email], input[type=password], input[type=number],
select, textarea {
  width: 100%;
  background: var(--recessed);
  border: 1px solid var(--border-strong);
  color: var(--text);
  font-family: var(--mono);
  font-size: .86rem;
  padding: .5rem .6rem;
}
input:focus, select:focus, textarea:focus { border-color: var(--accent); }
input::placeholder { color: #5c6268; }
input[readonly] { color: var(--muted); }

.hint { font-size: .78rem; color: var(--muted); margin-top: .3rem; }

.btn {
  display: inline-block;
  font-family: var(--ui);
  font-size: .84rem;
  font-weight: 500;
  padding: .5rem 1rem;
  border: 1px solid var(--accent);
  background: var(--accent);
  /* Dark text on the accent, not white — white fails contrast at this hue. */
  color: #0b0b0c;
  cursor: pointer;
}
.btn:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: #fff; text-decoration: none; }
.btn-ghost { background: transparent; color: var(--text); border-color: var(--border-strong); }
.btn-ghost:hover { background: var(--panel); color: var(--text); border-color: var(--accent); }
.btn-danger { background: transparent; color: var(--err); border-color: var(--err); }
.btn-danger:hover { background: var(--err); color: #0b0b0c; }
.btn:disabled { opacity: .45; cursor: not-allowed; }

.form-actions { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; margin-top: .25rem; }

/* --- alerts ------------------------------------------------------------- */
/* In the content flow, after a write. Never floating, never auto-dismissing. */
.alert {
  border: 1px solid var(--border-strong);
  border-left-width: 3px;
  background: var(--panel);
  padding: .8rem 1rem;
  margin-bottom: var(--gap);
  font-size: .88rem;
}
.alert-ok   { border-left-color: var(--accent); }
.alert-warn { border-left-color: var(--warn); }
.alert-err  { border-left-color: var(--err); color: var(--err); }
.alert pre { margin: .5rem 0 0; }

/* --- secrets ------------------------------------------------------------ */
/* A configured secret is never rendered, masked or revealed — only its
   presence is reported. The backend must never serialise the value either,
   or this is merely cosmetic. */
.secret-state { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.secret-name { font-family: var(--mono); font-size: .84rem; }

/* --- code / logs -------------------------------------------------------- */
pre, .code-block {
  background: var(--recessed);
  border: 1px solid var(--border);
  padding: .7rem .8rem;
  font-family: var(--mono);
  font-size: .78rem;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 50vh;
  overflow: auto;
  margin: 0;
}

/* --- numbered steps ----------------------------------------------------- */
/* Square counters, not circles — a radius here would break the language. */
.steps { list-style: none; padding: 0; margin: 0; display: grid; gap: .9rem; }
.step { display: grid; grid-template-columns: 1.65rem 1fr; gap: .75rem; align-items: start; }
.step-num {
  width: 1.65rem; height: 1.65rem;
  display: grid; place-items: center;
  border: 1px solid var(--accent);
  color: var(--accent);
  font-family: var(--mono);
  font-size: .78rem;
}
.step-done .step-num { background: var(--accent); color: #0b0b0c; }
.step-body { padding-top: .1rem; }
.step-body strong { font-weight: 600; display: block; margin-bottom: .1rem; }

/* --- QR / pairing ------------------------------------------------------- */
.qr-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--gap); }
.qr-box {
  background: #fff;           /* a QR must be dark-on-light to scan reliably */
  padding: 1rem;
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
}
.qr-box img { display: block; width: 100%; max-width: 300px; height: auto; }

/* --- auth --------------------------------------------------------------- */
.auth {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 1.25rem;
}
.auth-card { width: 100%; max-width: 340px; }
.auth-card .brand {
  font-family: var(--disp);
  font-size: 1.35rem;
  display: block;
  margin-bottom: .25rem;
}
.auth-card .brand .dot { color: var(--accent); }
.auth-card .btn { width: 100%; text-align: center; }

/* --- responsive (absent in the source) ---------------------------------- */
@media (max-width: 720px) {
  body { font-size: 14px; }
  main { padding: 1rem .85rem 3rem; }
  .topbar { padding: 0 .85rem; gap: .5rem; }
  .topbar nav { order: 3; width: 100%; border-top: 1px solid var(--border); padding: .35rem 0; }
  .topbar nav a { padding: .4rem .55rem; }
  .topbar .session { margin-left: auto; }
  .topbar .session .who { display: none; }
  .kpi-value { font-size: 1.35rem; }
  .step { grid-template-columns: 1.4rem 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ===========================================================================
   Legacy class bridge.

   The views carried over from upstream use an older vocabulary (.card, .compact,
   .state-*, .cta ...). Rather than leave those pages half-styled, each legacy
   name is mapped onto the primitive it corresponds to. New markup should use the
   primitives above; this block exists so nothing renders unstyled in the interim.
   ======================================================================== */

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  padding: 1.1rem 1.25rem;
  margin-bottom: var(--gap);
}
.card > :last-child { margin-bottom: 0; }
.card h2 { font-size: 1rem; margin-bottom: .7rem; }

.grid.two { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid.full { grid-template-columns: 1fr; }

/* Dense label/value tables inside cards: no header row, so the first cell
   carries the label tier instead. */
table.compact { font-size: .84rem; }
table.compact td { padding: .35rem 0; border-bottom: 1px solid var(--border); }
table.compact tr:last-child td { border-bottom: 0; }
table.compact tr:hover td { background: transparent; }
table.compact td:first-child {
  color: var(--muted);
  font-family: var(--mono);
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  white-space: nowrap;
  padding-right: 1rem;
}

/* State words rendered as bare text upstream. Give them the tag treatment so
   status reads at a glance instead of being one more grey string. */
[class*="state-"] {
  font-family: var(--mono);
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .05em;
}
.state-open, .state-active, .state-running, .state-sent, .state-done, .state-ok { color: var(--accent); }
.state-qr, .state-reconnecting, .state-pending, .state-locked, .state-activating { color: var(--warn); }
.state-disconnected, .state-failed, .state-error, .state-inactive, .state-dead { color: var(--err); }
.state-init, .state-loading, .state-unknown { color: var(--muted); }

/* Message direction, mirroring the inbound/outbound tokens. */
.role-user, .role-in { color: var(--in); }
.role-assistant, .role-out { color: var(--out); }
.role-tag {
  font-family: var(--mono);
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .05em;
}

.error { color: var(--err); border-left: 3px solid var(--err); background: var(--panel);
         padding: .7rem .9rem; margin-bottom: var(--gap); font-size: .88rem; }
.warn  { color: var(--warn); }

/* Upstream's call-to-action link. Same affordance as .btn. */
.cta {
  display: inline-block;
  font-size: .84rem;
  font-weight: 500;
  padding: .45rem .9rem;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: #0b0b0c;
}
.cta:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: #fff; text-decoration: none; }

.tabs { display: flex; gap: .25rem; border-bottom: 1px solid var(--border); margin-bottom: var(--gap); flex-wrap: wrap; }
.tabs a { padding: .45rem .8rem; color: var(--muted); font-size: .84rem; border: 1px solid transparent; border-bottom: 0; }
.tabs a:hover { color: var(--text); text-decoration: none; }
.tabs a.active { color: var(--accent); border-color: var(--border); background: var(--panel); }

.inline-form { display: inline; }
.inline-form button { font-size: .74rem; padding: .2rem .5rem; background: transparent;
                      border: 1px solid var(--border-strong); color: var(--muted); cursor: pointer; }
.inline-form button:hover { border-color: var(--accent); color: var(--accent); }

.preview { color: var(--muted); font-size: .82rem; }
.thread, .msg, .content { min-width: 0; }
.msg { border-bottom: 1px solid var(--border); padding: .6rem 0; }
.msg:last-child { border-bottom: 0; }

/* --- auth pages --------------------------------------------------------- */
/* The layout renders no wrapper when there is no session, so the card centres
   itself. Radius stays 0 here — the upstream login screen used rounded corners,
   which contradicted every other surface in the product. */
.auth-card {
  width: 100%;
  max-width: 340px;
  margin: 12vh auto 0;
  background: var(--panel);
  border: 1px solid var(--border);
  padding: 1.5rem;
}
.auth-card h1 { font-size: 1.25rem; margin-bottom: 1rem; }
.auth-card button, .auth-card .btn { width: 100%; }
.auth-card > :last-child { margin-bottom: 0; }

/* Any button not explicitly given .btn still reads as a control. */
button:not(.btn):not(.inline-form button) {
  font-family: var(--ui);
  font-size: .84rem;
  font-weight: 500;
  padding: .5rem 1rem;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: #0b0b0c;
  cursor: pointer;
}
button:not(.btn):not(.inline-form button):hover {
  background: var(--accent-hover); border-color: var(--accent-hover); color: #fff;
}

/* Collapsible reference blocks (the Google console checklist). Sharp, no marker
   rotation tricks — just a clear affordance. */
details > summary {
  cursor: pointer;
  list-style: none;
  padding: .2rem 0;
  color: var(--text);
  font-size: .95rem;
}
details > summary::-webkit-details-marker { display: none; }
details > summary::before { content: "▸ "; color: var(--accent); }
details[open] > summary::before { content: "▾ "; }
details > summary:hover { color: var(--accent); }

/* --- contacts ------------------------------------------------------------ */
/* The access tier is the only thing on this page worth colour. Everything else
   is an address, and addresses are for reading, not for ranking. */
.tier-operator { color: var(--in);     border-color: var(--in); }
.tier-trusted  { color: var(--accent); border-color: var(--accent); }
.tier-unknown  { color: var(--muted);  border-color: var(--border-strong); }
.tier-blocked  { color: var(--err);    border-color: var(--err); }

.filters {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--gap); flex-wrap: wrap; margin-bottom: .85rem;
}
.tier-filter { display: flex; gap: .4rem; flex-wrap: wrap; }
.pill {
  font-family: var(--mono); font-size: .7rem; text-transform: uppercase;
  letter-spacing: .05em; padding: .25rem .55rem;
  border: 1px solid var(--border); color: var(--muted);
}
.pill:hover { border-color: var(--border-strong); text-decoration: none; }
/* The selected filter is filled, not merely tinted — at this size a border
   colour alone is not a strong enough "you are here". */
.pill.on { background: var(--surface-alt); border-color: currentColor; }

.count { opacity: .6; margin-left: .3rem; font-variant-numeric: tabular-nums; }

.search { display: flex; gap: .5rem; align-items: center; margin: 0; }
.search input[type=search] {
  width: 16rem; background: var(--recessed); border: 1px solid var(--border-strong);
  color: var(--text); font-family: var(--mono); font-size: .82rem; padding: .4rem .6rem;
}
.search input[type=search]:focus { border-color: var(--accent); }
.search .btn { padding: .4rem .8rem; }

.contacts td { vertical-align: top; }
.contacts .empty { text-align: left; }
.contacts .cname { font-weight: 500; }
.contacts .addr { font-family: var(--mono); font-size: .8rem; word-break: break-all; }
.contacts .num { text-align: right; font-variant-numeric: tabular-nums; }
.contacts .preview { max-width: 22rem; }
/* A blocked row should read as switched off without becoming unreadable — this
   is a list you scan to find the one you want to unblock. */
.contacts .row-blocked .cname, .contacts .row-blocked .addr { color: var(--muted); }

.contacts .actions { white-space: nowrap; text-align: right; }
.contacts .actions form { display: inline; }
.btn.tiny { padding: .2rem .5rem; font-size: .74rem; }

.contact-form { max-width: 900px; }
.contact-form textarea { font-family: var(--ui); }

/* The one question the panel cannot answer for itself. Marked with the accent
   rather than a warning colour: nothing is broken, something is unanswered. */
.setup-prompt { border-left: 3px solid var(--accent); }
.setup-prompt table { margin-top: .6rem; }
.setup-prompt .actions { text-align: right; white-space: nowrap; }
.setup-prompt .actions form { display: inline; }
.setup-prompt .actions .btn { padding: .3rem .7rem; font-size: .8rem; }

/* --- model page ---------------------------------------------------------- */
/* Six model dropdowns is a lot of form. They are gridded so the page reads as
   one decision made six times, not as six unrelated settings. */
.model-grid { margin-bottom: 1rem; }
.model-field { margin-bottom: .4rem; }
.model-field > .label { display: block; margin-bottom: .3rem; }
.model-field .model-other { margin-top: .4rem; }

/* The env var name each control writes. Present because the operator reads the
   file by hand and greps the worker's journal for these exact strings — but
   dimmed, because it is a footnote to the plain-English label above it. */
.envkey { opacity: .55; font-size: .72rem; }

/* A checkbox is the one control that reads left-to-right, so it opts out of the
   block layout every other label uses. */
.check { display: flex; align-items: flex-start; gap: .55rem; margin: 1rem 0; }
.check input[type=checkbox] { width: auto; margin-top: .2rem; flex: none; }
.check > span { display: block; }
.check .hint { display: block; }

/* The secondary backend. One step down the background ladder rather than
   greyed out — it is not disabled, just not the one in use. */
.panel.secondary { background: var(--surface-alt); }
.panel.secondary > summary { font-family: var(--disp); font-size: 1.05rem; }
.panel.secondary > summary .tag { margin-left: .5rem; }
.panel.secondary[open] > summary { margin-bottom: .85rem; }

.rule { border: 0; border-top: 1px solid var(--border); margin: 1.1rem 0; }
