/* ─────────────────────────────────────────────────────────────────────────────
   shell.css — the shared chrome and components for the rebuilt pages.

   Self-contained: it declares its own tokens, and a page that loads only this
   file gets everything it needs. Page files used to carry 100+ lines of
   near-duplicate CSS each; the components they were re-declaring live here
   once.
   ──────────────────────────────────────────────────────────────────────────── */

/* ── Design tokens ───────────────────────────────────────────────────────────
   Copied from style.css so a page built on the shell needs nothing else. The
   values are identical, so a page loading both sheets is unaffected; style.css
   stays the source for the older pages that still depend on its components. */
:root {
  --accent: #ff3e00;
  --accent-dim: rgba(255, 62, 0, 0.12);
  --accent-hover: #ff5a26;
  --bg: #f5f5f5;
  --surface: #ffffff;
  --surface-2: #f0f0f0;
  --border: #e0e0e0;
  --border-focus: #ff3e00;
  --text: #0c0c0c;
  --muted: #6b7280;
  --success: #22c55e;
  --danger: #ef4444;
  --method-get: #2563eb;
  --method-post: #16a34a;
  --method-delete: #dc2626;
  --method-put: #ea580c;
  --method-patch: #9333ea;
  --sidebar-width: 280px;
  --radius: 6px;
  --radius-lg: 10px;
  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.06);
  --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06);
  --transition: 0.18s ease;
  font-family:
    "Inter",
    system-ui,
    -apple-system,
    sans-serif;
}


/* ── Dark theme ──────────────────────────────────────────────────────────────
   Follows the operating system. Only tokens change; components read tokens, so
   a component that hard-codes a colour is the bug, not this block. Chip colours
   are tokens too, because a pale pastel chip on a dark surface is unreadable. */
:root {
  --chip-blue-bg: #e0f2fe;   --chip-blue-fg: #075985;
  --chip-violet-bg: #ede9fe; --chip-violet-fg: #5b21b6;
  --chip-amber-bg: #fef3c7;  --chip-amber-fg: #92400e;
  --chip-red-bg: #fee2e2;    --chip-red-fg: #991b1b;
  --chip-green-bg: #d1fae5;  --chip-green-fg: #065f46;
  --chip-indigo-bg: #e0e7ff; --chip-indigo-fg: #3730a3;
  --ok-fg: #15803d; --warn-fg: #c2410c; --warn-line: #ea580c;
  --danger-fg: #b91c1c;
  color-scheme: light;
}
/* Light only: the app is designed for the light theme. */

/* Every component below sets its own `display`, which silently beats the
   user-agent's [hidden] rule — so `el.hidden = true` did nothing, and the page
   body showed for a frame before the auth check had run. */
[hidden] { display: none !important; }

body.app {
  background: var(--bg);
  color: var(--text);
  margin: 0;
  min-height: 100vh;
}

/* ── Topbar ──────────────────────────────────────────────────────────────────
   Always present, at every width. The old sidebar disappeared under 900px and
   took navigation and sign-out with it. */

.topbar {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 0 22px;
  height: 56px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 40;
  flex-wrap: wrap;
}

.brand {
  display: flex;
  align-items: center;
  gap: 9px;
  text-decoration: none;
  color: var(--text);
  font-weight: 700;
  font-size: 14px;
  flex-shrink: 0;
}
.brand-mark { font-size: 17px; }

.nav {
  display: flex;
  gap: 2px;
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.nav::-webkit-scrollbar { display: none; }

.nav-link {
  padding: 7px 12px;
  border-radius: var(--radius);
  font-size: 13px;
  font-weight: 500;
  color: var(--muted);
  text-decoration: none;
  white-space: nowrap;
  transition: var(--transition);
}
.nav-link:hover { color: var(--text); background: var(--surface-2); }
.nav-link.is-current { color: var(--accent); background: var(--accent-dim); font-weight: 600; }

.topbar-end { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.whoami {
  font-size: 12px;
  color: var(--muted);
  max-width: 190px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
@media (max-width: 640px) { .whoami { display: none; } }

/* Live updates (live.js): a dot, the same size whatever it says, so a status
   change never moves the bar. Its words are a tooltip on hover and on focus. */
.live-dot {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  flex-shrink: 0;
  cursor: default;
}
.live-dot::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--muted);
  box-shadow: 0 0 0 3px var(--surface-2);
  transition: background-color .2s;
}
.live-dot[data-status="live"]::before { background: var(--ok-fg); box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.18); }
.live-dot[data-status="connecting"]::before { background: var(--warn-line); box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.16); }
.live-dot[data-status="offline"]::before { background: transparent; box-shadow: inset 0 0 0 1.5px var(--muted); }
/* Capped: the feed is working, just not here — a hollow dot like offline, but
   filled in enough to read as "somewhere else" rather than "nowhere". */
.live-dot[data-status="capped"]::before { background: var(--surface-2); box-shadow: inset 0 0 0 1.5px var(--warn-line); }
/* Paused by the plan: a hollow dot in the accent colour — not an error state
   (nothing is wrong), and not the live green either (nothing is arriving). */
.live-dot[data-status="spent"]::before { background: transparent; box-shadow: inset 0 0 0 1.5px var(--accent); }
.live-dot[data-status="spent"]:hover::before { background: var(--accent); }
.live-dot:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.live-dot::after {
  content: attr(data-tip);
  position: absolute;
  top: calc(100% + 6px);
  right: -6px;
  z-index: 60;
  width: max-content;
  max-width: 240px;
  padding: 6px 9px;
  border-radius: 6px;
  background: var(--text);
  color: var(--surface);
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.4;
  white-space: normal;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity .12s;
}
.live-dot:hover::after, .live-dot:focus-visible::after { opacity: 1; visibility: visible; }
@media (prefers-reduced-motion: no-preference) {
  .live-dot[data-status="connecting"]::before { animation: live-pulse 1.4s ease-in-out infinite; }
}
@keyframes live-pulse { 50% { opacity: .35; } }

/* Inside a dialog: something changed elsewhere while it was open. */
.live-note {
  margin-top: 4px;
  padding: 8px 12px;
  border-radius: 6px;
  border-left: 3px solid var(--muted);
  background: var(--surface-2);
  color: var(--muted);
  font-size: 12px;
  line-height: 1.5;
}
.live-note--removed { border-left-color: var(--warn-line); color: var(--text); font-weight: 600; }

/* The signed-in person's menu (Your model keys). */
.usermenu { position: relative; }
.usermenu-summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; gap: 4px;
  padding: 4px 8px; border-radius: var(--radius); border: 1px solid transparent;
}
.usermenu-summary::-webkit-details-marker { display: none; }
.usermenu-summary::after { content: "▾"; font-size: 10px; color: var(--muted); }
.usermenu-summary:hover, .usermenu[open] .usermenu-summary { border-color: var(--border); background: var(--surface-2); }
.usermenu-short { display: none; font-size: 12px; color: var(--muted); }
@media (max-width: 640px) { .usermenu-short { display: inline; } }
.usermenu-list {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 50; min-width: 180px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-md); padding: 4px; display: flex; flex-direction: column;
}
/* Both a <button> and an <a> — /mfa.html is a link because it is its own page,
   reachable while every /api route is refusing the session. They have to look
   the same in the menu either way. */
.usermenu-item {
  font: inherit; font-size: 13px; text-align: left; background: none; border: 0; color: var(--text);
  padding: 7px 10px; border-radius: var(--radius); cursor: pointer; text-decoration: none;
  display: block;
}
.usermenu-item:hover, .usermenu-item:focus-visible { background: var(--surface-2); }

/* Model picker (servers' model calls, personal model calls). */
.mp { display: flex; flex-direction: column; gap: 6px; }
.mp-chosen { display: flex; flex-direction: column; gap: 4px; }
.mp-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.mp-default { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; min-width: 0; overflow-wrap: anywhere; }
.mp-label { font-size: 12px; font-weight: 600; margin-top: 4px; }
.mp-results { display: flex; flex-direction: column; gap: 2px; max-height: 240px; overflow-y: auto; }
.mp-hit { display: flex; align-items: center; gap: 6px; padding: 5px 6px; border-radius: var(--radius); }
.mp-hit:hover { background: var(--surface-2); }
.mp-hit-main { flex: 1; min-width: 0; font-size: 12.5px; overflow-wrap: anywhere; }
.mp-hit-main .field-hint { display: block; }
.mp-add { display: flex; gap: 6px; }
.mp-add input { flex: 1; min-width: 0; font: inherit; font-size: 13px; padding: 6px 9px;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text); }
.mk-status { font-size: 13px; margin-bottom: 8px; }
#model-keys .fold-actions { display: flex; justify-content: flex-end; gap: 8px; align-items: center; }
#model-keys .mk-actions { justify-content: space-between; }
.fold-body input[type="password"] { font: inherit; padding: 7px 10px; border: 1px solid var(--border);
  border-radius: var(--radius); background: var(--surface); color: var(--text); }

/* ── Page head + layout ──────────────────────────────────────────────────── */

.pagehead { border-bottom: 1px solid var(--border); background: var(--surface); }
.pagehead-in { max-width: 1100px; margin: 0 auto; padding: 26px 22px 24px; }
.pagehead h1 { margin: 0; font-size: 23px; font-weight: 700; letter-spacing: -0.01em; }
.pagehead p {
  margin: 7px 0 0;
  color: var(--muted);
  font-size: 14px;
  line-height: 1.6;
  max-width: 68ch;
}

main {
  max-width: 1100px;
  margin: 0 auto;
  padding: 26px 22px 72px;
  display: flex;
  flex-direction: column;
  gap: 26px;
}

.section { display: flex; flex-direction: column; gap: 12px; }
.section-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.section-head h2 { margin: 0; font-size: 15px; font-weight: 700; }
.section-head p { margin: 0; font-size: 13px; color: var(--muted); flex: 1; min-width: 220px; }
.section-actions { display: flex; gap: 8px; margin-left: auto; }

/* ── Cards ───────────────────────────────────────────────────────────────── */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.card-body { padding: 18px 20px; display: flex; flex-direction: column; gap: 12px; }
.card-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 20px;
  border-bottom: 1px solid var(--border);
}
.card-head h2, .card-head h3 { margin: 0; font-size: 14px; font-weight: 700; }
.card-head .section-actions { margin-left: auto; }

/* ── Buttons ─────────────────────────────────────────────────────────────── */

.btn {
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 8px 14px;
  border-radius: var(--radius);
  border: 1px solid transparent;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  text-decoration: none;
  transition: var(--transition);
  white-space: nowrap;
}
.btn--primary { background: var(--accent); color: #fff; }
.btn--primary:hover { background: var(--accent-hover); }
.btn--ghost { background: none; border-color: var(--border); color: var(--text); }
.btn--ghost:hover { border-color: var(--accent); color: var(--accent); }
.btn--danger { background: var(--danger); color: #fff; }
.btn--danger:hover { filter: brightness(1.08); }
/* A destructive action that sits among ordinary ones: quiet until pointed at. */
.btn--danger-ghost { background: none; border-color: var(--border); color: var(--danger-fg); }
.btn--danger-ghost:hover { border-color: var(--danger); background: rgba(239, 68, 68, 0.08); }
.btn--sm { font-size: 12px; padding: 5px 10px; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* ── Rows ────────────────────────────────────────────────────────────────── */

.row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--border);
}
.row:last-child { border-bottom: none; }
.row-main { flex: 1; min-width: 0; }
.row-title { font-size: 13.5px; font-weight: 650; display: flex; align-items: center; gap: 8px; }
.row-sub {
  font-size: 12px;
  color: var(--muted);
  margin-top: 3px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.row-actions { display: flex; gap: 6px; flex-shrink: 0; }
.row-meta {
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}

/* ── Badges ──────────────────────────────────────────────────────────────── */

.badge {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 2.5px 7px;
  border-radius: 4px;
  background: var(--surface-2);
  color: var(--muted);
  white-space: nowrap;
}
.badge--api { background: var(--accent-dim); color: var(--accent); }
.badge--mcp { background: var(--chip-indigo-bg); color: var(--chip-indigo-fg); }
.badge--ok { background: rgba(34, 197, 94, 0.13); color: var(--ok-fg); }
.badge--warn { background: rgba(234, 88, 12, 0.13); color: var(--warn-fg); }
.badge--off { background: var(--surface-2); color: var(--muted); }
/* For values that are not labels — an email address, a group's name. Upper-
   casing them changed what they said. */
/* Outcomes and decisions. A refusal never reads as a failure upstream, and a
   call waiting for a person never reads as either. */
.chip--refused, .chip--denied, .chip--failed { background: var(--chip-red-bg); color: var(--chip-red-fg); }
.chip--loop    { background: var(--chip-violet-bg); color: var(--chip-violet-fg); }
.chip--held, .chip--pending { background: var(--chip-blue-bg); color: var(--chip-blue-fg); }
/* A rehearsal (inline/practice.py): amber, because nothing went wrong and
   nothing happened either — the reader has to notice which. */
.chip--practice { background: var(--chip-amber-bg); color: var(--chip-amber-fg); }
.badge--plain { text-transform: none; letter-spacing: 0; font-weight: 600; font-size: 11px; }

/* What a tool does to data (ui.js behaviourChip). A colour per step, warmer as
   it gets harder to undo; unclassified is grey, not red — it is not known to
   delete, only treated as if it might. */
.beh-chip {
  display: inline-block; margin-left: 6px; vertical-align: middle;
  font-size: 9px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
  padding: 1px 6px; border-radius: 99px; white-space: nowrap; cursor: help;
}
.beh-chip--read    { background: var(--chip-blue-bg); color: var(--chip-blue-fg); }
.beh-chip--create  { background: var(--chip-violet-bg); color: var(--chip-violet-fg); }
.beh-chip--update  { background: var(--chip-amber-bg); color: var(--chip-amber-fg); }
.beh-chip--delete  { background: var(--chip-red-bg); color: var(--chip-red-fg); }
.beh-chip--unknown { background: var(--surface-2); color: var(--muted); }

/* ── Addresses ───────────────────────────────────────────────────────────── */

.addr { display: flex; gap: 8px; align-items: stretch; flex-wrap: wrap; }
.addr-url {
  flex: 1;
  min-width: 240px;
  white-space: pre-wrap;   /* multi-line values (a client config) keep their shape */
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 12.5px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 9px 11px;
  word-break: break-all;
  line-height: 1.5;
}

/* ── Empty / error states ────────────────────────────────────────────────── */

.empty {
  padding: 30px 20px;
  text-align: center;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.6;
}
.empty--error { color: var(--danger); }
.empty strong { color: var(--text); display: block; margin-bottom: 4px; font-size: 13.5px; }

.fatal {
  max-width: 460px;
  margin: 90px auto;
  padding: 22px;
  border: 1px solid var(--danger);
  border-radius: var(--radius-lg);
  background: var(--surface);
  font-size: 14px;
  line-height: 1.7;
}
.fatal a { display: block; margin-top: 12px; color: var(--accent); }

/* ── Stats ───────────────────────────────────────────────────────────────── */

.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
@media (max-width: 760px) { .stats { grid-template-columns: repeat(2, 1fr); } }

.stat {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 15px 17px;
  text-decoration: none;
  color: inherit;
  display: block;
  transition: var(--transition);
}
a.stat:hover { border-color: var(--accent); }
.stat-num { font-size: 25px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.stat-label { font-size: 12px; color: var(--muted); margin-top: 4px; }

/* ── Dialog ──────────────────────────────────────────────────────────────── */

.dlg {
  margin: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 0;
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-md);
  max-width: 560px;
  width: calc(100vw - 40px);
}
.dlg::backdrop { background: rgba(0, 0, 0, 0.42); }
.dlg--small { max-width: 420px; }
.dlg--wide { max-width: 680px; }
.dlg-error {
  border: 1px solid var(--danger);
  border-radius: var(--radius);
  background: rgba(239, 68, 68, 0.08);
  color: var(--danger-fg);
  font-size: 13px;
  line-height: 1.5;
  padding: 9px 12px;
}
.dlg-form { padding: 22px; display: flex; flex-direction: column; gap: 14px; }
.dlg-title { margin: 0; font-size: 16px; font-weight: 700; }
.dlg-body { margin: 0; font-size: 13.5px; color: var(--muted); line-height: 1.6; }
.dlg-actions { display: flex; justify-content: flex-end; gap: 8px; }

/* ── Forms ───────────────────────────────────────────────────────────────── */

.field { display: flex; flex-direction: column; gap: 5px; }
.field label { font-size: 12.5px; font-weight: 600; }
.field input[type="text"],
.field input[type="url"],
.field input[type="password"],
.field select {
  font: inherit;
  font-size: 13.5px;
  padding: 9px 11px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
}
.field input:focus, .field select:focus {
  outline: none;
  border-color: var(--border-focus);
  box-shadow: 0 0 0 3px var(--accent-dim);
}
.field-hint { font-size: 11.5px; color: var(--muted); line-height: 1.5; }
.field input[type="number"], .field textarea {
  font: inherit; font-size: 13.5px; padding: 8px 10px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text);
}
.field textarea:focus, .field input[type="number"]:focus {
  outline: none; border-color: var(--border-focus); box-shadow: 0 0 0 3px var(--accent-dim);
}
/* The reason a control is unavailable, next to it rather than in a tooltip
   nobody hovers over — and that a keyboard or a phone never shows. */
.why-not { font-size: 11.5px; color: var(--muted); line-height: 1.5; }

/* A group of settings that can be folded away, each with its own Save. */
.fold { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.fold > summary {
  cursor: pointer; list-style: none; display: flex; align-items: center; gap: 10px;
  padding: 11px 14px; font-size: 13.5px; font-weight: 650;
}
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary::before { content: "▸"; color: var(--muted); font-size: 11px; transition: transform .15s; }
.fold[open] > summary::before { transform: rotate(90deg); }
.fold > summary .fold-sub { font-weight: 400; color: var(--muted); font-size: 12px; margin-left: auto; text-align: right; }
.fold-body { padding: 4px 14px 14px; display: flex; flex-direction: column; gap: 10px; }

/* A short set of mutually exclusive answers. Stacked bordered rows rather than
   a bare radio list because the answers here are sentences, not words: beside
   an unboxed control a sentence wraps back under its own radio and the group
   stops reading as a group. Scoped under `.choice` so it outweighs the
   `.field label` type rule these labels would otherwise inherit. */
.choice { display: flex; flex-direction: column; gap: 6px; }
.choice .choice-item {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 8px 11px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: 13px;
  font-weight: 500;
  line-height: 1.4;
  cursor: pointer;
  transition: var(--transition);
}
.choice .choice-item:hover { border-color: var(--accent); }
.choice .choice-item input { margin: 1px 0 0; flex-shrink: 0; accent-color: var(--accent); }
/* The checked row carries the same accent wash as an active nav link. Purely
   additive: without :has() the radio itself still shows which one is on. */
.choice .choice-item:has(input:checked) { border-color: var(--accent); background: var(--accent-dim); }

/* A standing caveat, not an error: something that will not work the way the
   surrounding controls imply, and is worth reading before configuring them. */
.note {
  border: 1px solid var(--border);
  border-left: 3px solid var(--muted);
  border-radius: 6px;
  padding: 12px 14px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--muted);
  background: var(--surface-2);
}
.note strong { color: var(--text); }
.note p { margin: 8px 0 0; }
.note p:last-child { margin-bottom: 0; }
/* Same orange as .badge--warn, which is the only other 'read this' signal here. */
.note--warn { border-left-color: var(--warn-line); }

/* ── Tool picker (virtual server composer) ───────────────────────────────── */

.picker {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  max-height: 330px;
  overflow-y: auto;
  background: var(--surface-2);
}
.picker-group {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 9px 12px 5px;
  position: sticky;
  top: 0;
  background: var(--surface-2);
}
.picker-item {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  padding: 6px 12px;
  font-size: 12.5px;
  cursor: pointer;
}
.picker-item:hover { background: var(--surface); }
.picker-item input { margin-top: 2px; flex-shrink: 0; }
.picker-name { font-family: "JetBrains Mono", ui-monospace, monospace; font-size: 11.5px; }
.picker-desc { color: var(--muted); font-size: 11.5px; line-height: 1.45; }

/* ── Toast ───────────────────────────────────────────────────────────────── */

#toast {
  position: fixed;
  bottom: 22px;
  left: 50%;
  transform: translate(-50%, 90px);
  background: var(--text);
  color: var(--bg);
  font-size: 13px;
  font-weight: 500;
  padding: 11px 18px;
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  opacity: 0;
  transition: 0.22s ease;
  z-index: 100;
  pointer-events: none;
  max-width: min(520px, calc(100vw - 32px));
}
#toast.show { opacity: 1; transform: translate(-50%, 0); }
#toast.error { background: var(--danger); color: #fff; }
#toast.warn { background: #c2410c; color: #fff; }
#toast.error { background: #b91c1c; }

/* ── Flow diagram (Overview) ─────────────────────────────────────────────── */

.flow { width: 100%; height: auto; display: block; }
.flow text { font-family: "Inter", system-ui, sans-serif; }
.flow-box { fill: var(--surface); stroke: var(--border); }
.flow-box--accent { fill: var(--accent-dim); stroke: var(--accent); }
.flow-label { fill: var(--text); font-size: 12px; font-weight: 650; }
.flow-sub { fill: var(--muted); font-size: 10.5px; }
.flow-arrow { stroke: var(--muted); stroke-width: 1.5; fill: none; }
.flow-cap {
  fill: var(--muted);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

/* ── Narrow screens ──────────────────────────────────────────────────────────
   Rows are a single flex line by default, which is right until the badge, the
   count and two buttons all want the same 375px. Below 640 they stack, and the
   nav drops to its own line instead of being squeezed against Sign out. */

@media (max-width: 640px) {
  .topbar { height: auto; padding: 10px 16px; gap: 10px; }
  .brand { order: 1; }
  .topbar-end { order: 2; margin-left: auto; }
  .nav { order: 3; flex-basis: 100%; }

  .pagehead-in { padding: 20px 16px 18px; }
  main { padding: 20px 16px 56px; gap: 20px; }

  .row { flex-wrap: wrap; gap: 8px 12px; padding: 14px 16px; }
  .row-main { flex-basis: 100%; }
  .row-title { flex-wrap: wrap; }
  .row-sub { white-space: normal; }
  .row-actions { margin-left: auto; flex-wrap: wrap; justify-content: flex-end; flex-shrink: 1; min-width: 0; max-width: 100%; }

  .section-head { align-items: flex-start; }
  .section-actions { margin-left: 0; flex-basis: 100%; }
  .section-actions .btn { flex: 1; justify-content: center; }

  .card-body { padding: 16px; }
  /* Search + count + two buttons do not fit one 375px line. */
  .card-head { flex-wrap: wrap; padding: 12px 16px; }
  .card-head .section-actions { margin-left: 0; flex-basis: 100%; }
  .card-head .section-actions .btn { flex: 1; justify-content: center; }
  .stat { padding: 13px 15px; }
  .dlg-form { padding: 18px; }
}

/* A button that reads as inline text — a count that opens a detail. Underlined
   at rest, because "3 tools" that only reveals itself on hover is not
   something anyone thinks to hover over. */
.linkish {
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; color: var(--text); cursor: pointer;
  text-decoration: underline dotted; text-underline-offset: 3px;
  text-decoration-color: var(--muted);
  transition: text-decoration-color .15s, color .15s;
}
.linkish:hover, .linkish:focus-visible { color: var(--accent); text-decoration-style: solid; text-decoration-color: currentColor; }

/* ── Keyboard focus ──────────────────────────────────────────────────────────
   One visible ring for everything focusable, drawn only for the keyboard. */
:where(a, button, summary, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
}
.skip-link {
  position: absolute; left: 12px; top: -60px; z-index: 200;
  background: var(--surface); color: var(--text); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 8px 12px; font-size: 13px;
}
.skip-link:focus { top: 10px; }

/* A row that opens a detail: a real button, so it is reached with Tab and
   opened with Enter or Space. */
button.row {
  width: 100%; font: inherit; color: inherit; text-align: left;
  background: none; border: 0; border-bottom: 1px solid var(--border); cursor: pointer;
}
button.row:hover { background: var(--surface-2); }

/* ── Phone navigation ────────────────────────────────────────────────────────
   Wraps instead of scrolling sideways: a scrolling strip with its scrollbar
   hidden put Approvals and People — and their badges — out of sight with
   nothing to say they were there. */
@media (max-width: 640px) {
  .nav { flex-wrap: wrap; overflow: visible; row-gap: 4px; }
  .nav-link { padding: 6px 10px; }
  .topbar-end .btn--sm { padding: 5px 8px; }
}

/* ── Organization model keys (Sources) and a server's key (Settings) ─────── */
.okey { padding: 16px 20px; border-bottom: 1px solid var(--border); display: flex; flex-direction: column; gap: 8px; }
.okey:last-child { border-bottom: none; }
.okey-head { display: flex; align-items: flex-start; gap: 10px 14px; flex-wrap: wrap; }
.okey-title { flex: 1; min-width: 200px; font-size: 13.5px; font-weight: 650; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; overflow-wrap: anywhere; }
.okey-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.okey-facts { display: flex; flex-wrap: wrap; gap: 4px 16px; font-size: 12.5px; color: var(--muted); line-height: 1.5; }
.okey-facts strong { color: var(--text); font-weight: 600; }
.okey-meter { height: 6px; border-radius: 99px; background: var(--surface-2); overflow: hidden; max-width: 320px; }
.okey-meter > span { display: block; height: 100%; background: var(--accent); border-radius: 99px; }
.okey-meter.is-over > span { background: var(--danger); }
.okey-servers { display: flex; flex-direction: column; gap: 0; border: 1px solid var(--border); border-radius: var(--radius); }
.okey-server { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; padding: 7px 11px; font-size: 12.5px; border-bottom: 1px solid var(--border); }
.okey-server:last-child { border-bottom: none; }
.okey-server-name { flex: 1; min-width: 140px; font-weight: 600; overflow-wrap: anywhere; }
.okey-server-spend { color: var(--muted); font-variant-numeric: tabular-nums; }
.okey-empty { font-size: 12.5px; color: var(--muted); }
.okey-assign { display: flex; flex-direction: column; gap: 6px; }
.okey-assign .choice-item { flex-wrap: wrap; align-items: center; }
.okey-assign .okey-assign-name { flex: 1; min-width: 150px; overflow-wrap: anywhere; }
.okey-cap { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
.okey-cap input { width: 92px; font: inherit; font-size: 13px; padding: 5px 8px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text); }
.okey-caps { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 520px) { .okey-caps { grid-template-columns: 1fr; } }
.okey-table { display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: var(--radius); }
.okey-trow { display: flex; flex-wrap: wrap; gap: 2px 12px; padding: 7px 11px; font-size: 12.5px; border-bottom: 1px solid var(--border); }
.okey-trow:last-child { border-bottom: none; }
.okey-trow > :first-child { flex: 1; min-width: 150px; overflow-wrap: anywhere; font-weight: 600; }
.okey-trow > :not(:first-child) { color: var(--muted); font-variant-numeric: tabular-nums; }
.okey-subhead { margin: 4px 0 0; font-size: 12.5px; font-weight: 650; }
.okey-total { font-size: 14px; }
.okey-total strong { font-size: 18px; }
/* ── Call limits (static/caps.js) ──
   One block per window: what applies, what has been used, and the number this
   level sets. A window under pressure is tinted rather than only badged, so a
   glance down the four rows finds it. */
.caps { display: flex; flex-direction: column; gap: 10px; }
.caps-row { display: flex; flex-direction: column; gap: 6px; padding: 10px 12px;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.caps-row.is-near { border-left: 3px solid var(--warn-line); background: rgba(234, 88, 12, 0.05); }
.caps-row.is-at   { border-left: 3px solid var(--danger);    background: rgba(239, 68, 68, 0.06); }
.caps-row-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.caps-row-title { font-size: 13px; font-weight: 650; }
.caps-row-says { font-size: 12.5px; color: var(--text); line-height: 1.5; overflow-wrap: anywhere; }
.caps-row .okey-cap input { width: 110px; }
/* A phone: the label and its input stack rather than squeezing the number. */
@media (max-width: 420px) {
  .caps-row { padding: 10px; }
  .caps-row .okey-cap { align-items: flex-start; flex-direction: column; gap: 4px; }
  .caps-row .okey-cap input { width: 100%; box-sizing: border-box; }
}

/* Model calls in a server's Settings: the source, its key, then models and privacy. */
#c-model-config, #c-orgkey, #c-model-choice, #c-openrouter { display: flex; flex-direction: column; gap: 10px; }
