/* ZeroLog — the whole visual system, matched to the classes the js/ views render.
   Sections: tokens · base · utilities · scrollbars · markdown · buttons · forms
   · cards/tables/badges/chips · toasts · banners · modal · login · shell/sidebar
   · chat · model picker · dashboard · vault · settings · responsive · keyframes */

/* ---------- tokens — "Industry" design system, ZeroLog cut ----------
   Delivered as a design spec and implemented here by hand. The ground is a near-black technical
   surface, the accent a single lavender, and structure is drawn with hairlines rather than filled
   panels — the product is an instrument panel, not a set of soft cards.

   THE ACCENT IS ONE VARIABLE. Every accent-derived value below is a color-mix() against --accent, so
   the runtime accent picker still re-tints the entire product; the design's own frames do exactly the
   same thing (color-mix(var(--acc) …)). Never hard-code the accent colour anywhere. */
@import url('/fonts/barlow.css');   /* self-hosted: CSP is font-src 'self', so no Google Fonts link */

:root {
  /* Ground: four steps of the same near-black, so depth reads as light, not as boxes. */
  --bg: #0d0f11;                /* the board */
  --surface: #131518;           /* panels, sidebar, header */
  --surface2: #1a1d21;          /* controls */
  --surface3: #22262b;          /* hover / raised */
  --deep: #0b0d0f;              /* wells: code blocks, terminal, inputs */
  --border: #23272c;            /* structural hairline */
  --border-soft: rgba(255, 255, 255, .06);
  --text: #e7e9ec;
  --muted: #8b9199;

  --accent: #b39ddb;            /* replaced at runtime from settings — never hard-code it */
  --amber: #c9a871;
  --green: #86b892;
  --red: #c98181;
  --link: #8fb2d9;

  /* Accent-derived, all mixed from --accent so a user's colour propagates everywhere. */
  --accent-wash: color-mix(in srgb, var(--accent) 10%, var(--bg));
  --accent-tint: color-mix(in srgb, var(--accent) 14%, transparent);
  --accent-line: color-mix(in srgb, var(--accent) 42%, transparent);
  --accent-ink:  color-mix(in srgb, var(--accent) 82%, #fff);

  /* Squared, not rounded: this system frames things, it does not pillow them. */
  --radius: 4px;
  --radius-sm: 2px;

  --shadow-1: 0 1px 2px rgba(0, 0, 0, .45);
  --shadow-2: 0 6px 22px -8px rgba(0, 0, 0, .6), 0 1px 2px rgba(0, 0, 0, .4);
  --shadow-3: 0 24px 60px -16px rgba(0, 0, 0, .78), 0 2px 10px rgba(0, 0, 0, .5);
  --glow: 0 0 0 1px var(--accent-line), 0 4px 22px -6px color-mix(in srgb, var(--accent) 38%, transparent);
  --ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent-line);

  /* Condensed headings over a normal-width body — the system's voice. */
  --font-heading: 'Barlow Condensed', 'Barlow', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font: 'Barlow', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --mono: ui-monospace, 'Cascadia Code', 'JetBrains Mono', 'SF Mono', monospace;

  /* Motion primitives — one easing and three durations, so nothing invents its own timing. */
  --ease: cubic-bezier(.2, 0, 0, 1);
  --t-fast: .14s;
  --t-mid: .26s;
  --t-slow: .6s;
}

/* ---------- base ---------- */
* { box-sizing: border-box; margin: 0; padding: 0; }
/* Android Chrome "font boosting" (text autosizing) rewrites font sizes on its own: it looks at a block
   container's width against the viewport and scales the text up so a desktop-width column stays
   readable. A viewport meta does NOT switch it off. Measured on a real phone: the layout was correct —
   drawer collapsed, cards stacked, no sideways scroll — but body text rendered ~1.3x larger than
   specified and only six or seven words fit a line, which is exactly what boosting does. 100% (rather
   than `none`) pins the sizes we actually wrote WITHOUT taking pinch-zoom away from the reader. */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
html, body { height: 100%; }
body {
  background: var(--bg);
  color: var(--text);
  font: 14px/1.55 var(--font);
  -webkit-font-smoothing: antialiased;
}
#app { height: 100%; }

/* ---------- §Design background layer (js/backgrounds.js) ----------
   The layer (#mh-bg) is inserted as body's FIRST child. Without these rules it sat in normal document
   flow as a full-viewport-tall block and pushed the entire app below the fold — you saw only the
   canvas + the fixed pet, nothing else. Fixed + inset:0 takes it out of flow and pins it to the
   viewport; z-index:-1 puts it behind the app; body.has-bg drops the body's own opaque fill so the
   backdrop shows through. pointer-events stay off — the interactive renderers read the pointer from
   `window`, not from this element, so nothing needs to receive events here. */
#mh-bg { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
#mh-bg .mh-bg-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: none; }
#mh-bg .mh-bg-scrim  { position: absolute; inset: 0; background: rgba(6, 8, 12, .34); }
#mh-bg .mh-bg-video  { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
#mh-bg.mh-bg-media   { background-size: cover; background-position: center; background-repeat: no-repeat; }
/* Only transparent WHILE a background shows; 'off' keeps the app's own opaque surfaces. */
body.has-bg { background: transparent; }
body.has-bg .main, body.has-bg .page, body.has-bg .shell { background: transparent; }
/* Gradient presets (aurora/dusk/mono) have no canvas renderer — the look IS this CSS on the layer. */
#mh-bg.bg-dusk   { background: radial-gradient(120% 120% at 20% 0%, #2a1a3e 0%, #14101f 45%, #0b0e14 100%); }
#mh-bg.bg-mono   { background: linear-gradient(160deg, #1a1f27 0%, #0d1117 55%, #090b10 100%); }
#mh-bg.bg-aurora {
  background: linear-gradient(120deg, #06121a 0%, #0a2a2f 25%, #123a52 50%, #0a2a2f 75%, #06121a 100%);
  background-size: 300% 300%;
  animation: mhAurora 24s ease-in-out infinite;
}
@keyframes mhAurora { 0%,100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }
@media (prefers-reduced-motion: reduce) { #mh-bg.bg-aurora { animation: none; } }
::selection { background: color-mix(in srgb, var(--accent) 35%, transparent); }
:focus-visible { outline: 2px solid color-mix(in srgb, var(--accent) 60%, transparent); outline-offset: 2px; }

/* ---------- utilities ---------- */
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
.muted { color: var(--muted); }
.mono { font-family: var(--mono); }
.num { font-variant-numeric: tabular-nums; }
.hidden { display: none !important; }
.spacer { flex: 1; }

/* ---------- scrollbars ---------- */
* { scrollbar-width: thin; scrollbar-color: #2a3240 transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #242c38; border-radius: 8px; border: 2px solid var(--bg); }
::-webkit-scrollbar-thumb:hover { background: #303a49; }

/* ---------- rendered markdown (assistant bubbles, .msg-body.md) ---------- */
.md { font-size: 15px; line-height: 1.65; overflow-wrap: break-word; }
.md > * + * { margin-top: 0.75em; }
.md h1, .md h2, .md h3, .md h4 { line-height: 1.3; margin-top: 1.1em; font-weight: 650; }
.md h1 { font-size: 1.35em; } .md h2 { font-size: 1.2em; } .md h3 { font-size: 1.08em; } .md h4 { font-size: 1em; }
.md ul, .md ol { padding-left: 1.5em; }
.md li + li { margin-top: 0.3em; }
.md code {
  font-family: var(--mono); font-size: 0.88em;
  background: var(--surface2); border: 1px solid var(--border);
  border-radius: 5px; padding: 1px 6px;
}
.md pre {
  background: #0d1117; border: 1px solid var(--border); border-radius: 10px;
  padding: 12px 14px; overflow-x: auto;
}
.md pre code { background: transparent; border: none; padding: 0; font-size: 13px; line-height: 1.55; }
.md blockquote {
  border-left: 3px solid color-mix(in srgb, var(--accent) 60%, transparent);
  padding: 2px 0 2px 14px; color: var(--muted);
}
.md table { border-collapse: collapse; width: 100%; font-size: 14px; }
.md th, .md td { border: 1px solid var(--border); padding: 6px 10px; text-align: left; }
.md th { background: var(--surface2); }
.md hr { border: none; border-top: 1px solid var(--border); }
.md img { max-width: 100%; border-radius: 8px; }

/* ---------- buttons (both naming styles used by the views: .btn-ghost / .btn.ghost …) ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 9px 16px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--surface2); color: var(--text);
  font: inherit; font-size: 14px; font-weight: 500; white-space: nowrap;
  cursor: pointer; user-select: none;
  transition: background .15s ease, border-color .15s ease, filter .15s ease, color .15s ease;
}
.btn:hover { background: #212836; border-color: #2e3745; }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }
.btn svg { flex: none; }
.btn-primary, .btn.primary { background: var(--accent); border-color: transparent; color: #fff; }
.btn-primary:hover, .btn.primary:hover { background: var(--accent); filter: brightness(1.12); }
.btn-danger, .btn.danger {
  background: color-mix(in srgb, var(--red) 13%, transparent);
  border-color: color-mix(in srgb, var(--red) 40%, transparent);
  color: var(--red);
}
.btn-danger:hover, .btn.danger:hover { background: color-mix(in srgb, var(--red) 22%, transparent); }
.btn-ghost, .btn.ghost { background: transparent; border-color: transparent; color: var(--muted); }
.btn-ghost:hover, .btn.ghost:hover { background: var(--surface2); color: var(--text); }
.btn.ghost.danger { background: transparent; border-color: transparent; }
.btn.ghost.danger:hover { background: color-mix(in srgb, var(--red) 15%, transparent); }
.btn-sm, .btn.small { padding: 5px 11px; font-size: 13px; border-radius: 8px; }
.btn-block { width: 100%; }

/* ---------- forms ---------- */
.input, textarea.input {
  width: 100%; padding: 10px 12px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--bg); color: var(--text);
  font: inherit; font-size: 14px;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.input::placeholder { color: color-mix(in srgb, var(--muted) 65%, transparent); }
.input:focus, textarea.input:focus {
  outline: none;
  border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent);
}
/* labeled fields (vault + settings forms): label/div.field with text, then control */
.field { display: block; font-size: 13px; color: var(--muted); font-weight: 500; }
.field > .input, .field > .picker, .field > .set-accent-row { margin-top: 6px; }
.field .input { color: var(--text); font-weight: 400; }
.field + .field, .vault-form .field + .field { margin-top: 14px; }
.hint { display: block; font-size: 12px; color: var(--muted); font-weight: 400; margin-top: 5px; }
.check {
  display: flex; align-items: flex-start; gap: 10px;
  margin-top: 14px; font-size: 13.5px; cursor: pointer;
}
.check input { margin-top: 3px; accent-color: var(--accent); flex: none; }

/* ---------- cards, tables, badges, chips, misc ---------- */
.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; }
.card + .card { margin-top: 16px; }
.card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 14px; }
.card-head h2, .set-section h2, .modal h2 { font-size: 15px; font-weight: 600; }

.table-wrap { overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; font-size: 14px; }
.table th {
  text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--muted); font-weight: 600; padding: 8px 10px; border-bottom: 1px solid var(--border);
}
.table td { padding: 9px 10px; border-bottom: 1px solid color-mix(in srgb, var(--border) 60%, transparent); }
.table tr:last-child td { border-bottom: none; }
.table tbody tr:hover td { background: color-mix(in srgb, var(--surface2) 55%, transparent); }
.table th.num, .table td.num { text-align: right; font-variant-numeric: tabular-nums; }

/* badges (vault "model access", FREE markers) */
.badge, .badge-free {
  display: inline-block; padding: 1px 7px; border-radius: 999px;
  font-size: 10px; font-weight: 700; letter-spacing: .06em;
  background: var(--surface2); border: 1px solid var(--border); color: var(--muted);
}
.badge-green, .badge-free {
  background: color-mix(in srgb, var(--green) 17%, transparent);
  border-color: color-mix(in srgb, var(--green) 45%, transparent); color: var(--green);
}
.badge-amber {
  background: color-mix(in srgb, var(--amber) 17%, transparent);
  border-color: color-mix(in srgb, var(--amber) 45%, transparent); color: var(--amber);
}

/* chips: composer vault attachments + dashboard status pills */
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 9px; border-radius: 999px;
  font-size: 12px; font-weight: 600; letter-spacing: .02em;
  background: var(--surface2); border: 1px solid var(--border); color: var(--muted);
}
.chip-x { background: none; border: none; color: inherit; cursor: pointer; font: inherit; font-size: 14px; line-height: 1; padding: 0; }
.chip-x:hover { color: var(--red); }
.chip-red   { background: color-mix(in srgb, var(--red) 15%, transparent);   border-color: color-mix(in srgb, var(--red) 40%, transparent);   color: var(--red); }
.chip-amber { background: color-mix(in srgb, var(--amber) 15%, transparent); border-color: color-mix(in srgb, var(--amber) 40%, transparent); color: var(--amber); }
.chip-green { background: color-mix(in srgb, var(--green) 15%, transparent); border-color: color-mix(in srgb, var(--green) 40%, transparent); color: var(--green); }
.chip-accent { background: color-mix(in srgb, var(--accent) 15%, transparent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); color: var(--accent); }
.chip-muted { }

.empty { text-align: center; color: var(--muted); padding: 60px 20px; font-size: 14px; }
.page-loading { text-align: center; padding: 60px 20px; }

/* ---------- toasts ---------- */
#toasts {
  position: fixed; right: 20px; bottom: 20px; z-index: 300;
  display: flex; flex-direction: column; gap: 10px; max-width: 340px;
}
.toast {
  background: var(--surface2); border: 1px solid var(--border); border-left: 3px solid var(--accent);
  border-radius: 10px; padding: 11px 14px; font-size: 14px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .45);
  opacity: 0; transform: translateY(8px);
  transition: opacity .25s ease, transform .25s ease;
}
.toast.show { opacity: 1; transform: none; }
.toast-error { border-left-color: var(--red); }
.toast-warn { border-left-color: var(--amber); }

/* ---------- banners ---------- */
/* connection-lost strip (created by api.js) */
.conn-banner {
  position: fixed; top: 0; left: 0; right: 0; z-index: 400;
  text-align: center; font-size: 13px; font-weight: 500; padding: 6px 12px;
  background: color-mix(in srgb, var(--amber) 16%, var(--bg));
  border-bottom: 1px solid color-mix(in srgb, var(--amber) 40%, transparent);
  color: var(--amber);
}

/* governor banner — .amber while active, .red for hard stop */
.gov-banner {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  max-width: 900px; margin: 0 auto; padding: 12px 16px;
  border-radius: 10px; font-size: 13.5px;
  background: color-mix(in srgb, var(--amber) 10%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--amber) 40%, transparent);
  border-left: 3px solid var(--amber);
}
.gov-banner.red {
  background: color-mix(in srgb, var(--red) 10%, var(--surface));
  border-color: color-mix(in srgb, var(--red) 40%, transparent);
  border-left-color: var(--red);
}
.gov-banner .gov-actions { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.gov-armed { color: var(--amber); font-weight: 600; font-size: 12.5px; }

/* ---------- modal (overlay created by chat.js / vault.js) ---------- */
.modal-overlay {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(4, 6, 10, .65); backdrop-filter: blur(3px);
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
.modal {
  background: var(--surface); border: 1px solid var(--border); border-radius: 16px;
  width: 100%; max-width: 520px; max-height: 85vh; overflow-y: auto;
  padding: 22px;
  box-shadow: 0 24px 64px rgba(0, 0, 0, .5);
  animation: pop .18s ease;
}
.modal h2 { margin-bottom: 14px; }
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }

/* ---------- login ---------- */
.login-wrap {
  height: 100%; display: flex; align-items: center; justify-content: center; padding: 20px;
  background: radial-gradient(640px 420px at 50% 28%, color-mix(in srgb, var(--accent) 9%, transparent), transparent), var(--bg);
}
.login-card {
  width: 100%; max-width: 360px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 16px;
  padding: 34px 30px 28px; display: flex; flex-direction: column; gap: 14px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
}
.login-card .brand { justify-content: center; font-size: 19px; }
.login-sub { text-align: center; color: var(--muted); font-size: 13.5px; margin-bottom: 6px; }
.login-err { min-height: 18px; text-align: center; color: var(--red); font-size: 13px; }
.shake { animation: shake .45s ease; }

/* brand mark (shared login + sidebar) */
.brand { display: flex; align-items: center; gap: 10px; font-weight: 650; letter-spacing: -.01em; }
.brand-dot {
  width: 10px; height: 10px; flex: none; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 14px color-mix(in srgb, var(--accent) 70%, transparent);
}

/* ---------- shell & sidebar ---------- */
.shell { display: flex; height: 100vh; height: 100dvh; }
.sidebar {
  width: 280px; flex: none; display: flex; flex-direction: column;
  background: var(--surface); border-right: 1px solid var(--border);
}
.side-brand { padding: 20px 20px 14px; font-size: 16px; }
.side-new { padding: 4px 16px 12px; }
.side-nav { padding: 2px 10px 6px; display: flex; flex-direction: column; gap: 2px; }
.side-nav a {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px;
  border-radius: 8px; color: var(--muted); font-size: 14px; font-weight: 500;
  transition: background .15s ease, color .15s ease;
}
.side-nav a:hover { background: var(--surface2); color: var(--text); text-decoration: none; }
.side-nav a.active { background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--text); }
.side-nav a.active svg { color: var(--accent); }
.side-nav svg { flex: none; color: var(--muted); transition: color .15s ease; }

.chat-list-label {
  padding: 14px 20px 6px; font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .08em; color: var(--muted);
}
/* The sidebar used to be a fixed-height flex column whose sections competed for the same pixels,
   and the chat list lost that fight badly enough to vanish: `overflow-y: auto` gives a flex item an
   implied `min-height: 0`, so the list was the one section able to shrink while .side-nav (twelve
   links on an admin account, ~460 px) could not shrink at all. Giving the list a floor helped but
   only traded "invisible" for "five rows in a cramped scroller".

   So the competition is removed rather than refereed. The sidebar itself is the ONE scroll
   container; the chat list takes its natural height and can never be compressed, because nothing
   is dividing a fixed budget any more. New chat stays pinned at the top and the account/credits
   footer at the bottom, so the two things you always need are always reachable. */
.sidebar { overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; }
.side-brand { flex: none; }
.side-new {
  flex: none; position: sticky; top: 0; z-index: 2;
  background: var(--surface); padding-top: 8px;
}
.side-nav { flex: none; }
/* Tighter rows: twelve nav links at the old padding ate most of a laptop viewport on their own. */
.side-nav a { padding: 6px 10px; font-size: 13.5px; }
.chat-list-label { flex: none; padding-top: 12px; }
.chat-list {
  flex: none; min-height: 0; overflow: visible;
  padding: 0 10px 10px; display: flex; flex-direction: column; gap: 2px;
}
.side-foot {
  position: sticky; bottom: 0; z-index: 2;
  background: var(--surface); margin-top: auto;
}

.chat-item {
  padding: 8px 10px; border-radius: 8px; font-size: 14px; cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: color-mix(in srgb, var(--text) 82%, var(--muted));
  transition: background .15s ease, color .15s ease;
}
.chat-item:hover { background: var(--surface2); color: var(--text); }
.chat-item.active { background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--text); }
.chat-empty { padding: 8px 10px; color: var(--muted); font-size: 13px; }

/* generating indicator on a sidebar chat (a background send is streaming for this chat) */
.chat-item .streaming-dot {
  display: inline-block; width: 7px; height: 7px; flex: none; vertical-align: middle;
  margin-right: 7px; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent);
  animation: streamPulse 1.4s ease-in-out infinite;
}

.side-foot {
  border-top: 1px solid var(--border); padding: 12px 16px;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.credits { display: flex; flex-direction: column; line-height: 1.35; min-width: 0; }
.credits-label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; }
.credits-value { font-size: 14px; font-weight: 600; color: var(--green); font-variant-numeric: tabular-nums; }

.side-backdrop { display: none; }
.hamburger {
  display: none; position: fixed; top: 12px; left: 12px; z-index: 130;
  width: 40px; height: 40px; border-radius: 10px; align-items: center; justify-content: center;
  background: var(--surface2); border: 1px solid var(--border); color: var(--text); cursor: pointer;
}

/* main column; non-chat views use .page (own scroll, centered content) */
/* position:relative is load-bearing: .builder-page is `position:absolute; inset:0`, and without a
   positioned ancestor it resolves against the VIEWPORT — covering the sidebar, so there was no
   way back to Dashboard from the Builder. It should fill this pane, not the screen. */
.main {
  position: relative;
  flex: 1; min-width: 0; height: 100vh; height: 100dvh;
  display: flex; flex-direction: column; overflow: hidden;
}
.page { flex: 1; overflow-y: auto; padding: 32px 40px; }
.page > * { max-width: 1000px; margin-left: auto; margin-right: auto; }
.page-head { display: flex; align-items: baseline; gap: 14px; margin-bottom: 22px; flex-wrap: wrap; }
.page-head h1 { font-size: 22px; font-weight: 650; letter-spacing: -.01em; }
.page-head p { font-size: 13.5px; }

/* ---------- chat view ----------
   .chat-view is a flex column inside .main (which has overflow:hidden).
   overflow:hidden here is essential — it constrains the flex children so that
   .chat-scroll (flex:1, min-height:0) gets a definite height and its
   overflow-y:auto actually scrolls. Without it, content can overflow past the
   flex bounds and get clipped by .main, hiding the composer and last messages.
   No height:100% — it's redundant with flex:1 and can interfere with flex
   sizing in some browsers, causing the scroll area to not get a proper height. */
.chat-view { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
.chat-header {
  flex: none; display: flex; align-items: center; gap: 8px;
  padding: 12px 24px; border-bottom: 1px solid var(--border); background: var(--surface);
}
input.chat-title {
  flex: 1; min-width: 0; padding: 6px 10px; border-radius: 8px;
  border: 1px solid transparent; background: transparent; color: var(--text);
  font: inherit; font-weight: 600; font-size: 15px; text-overflow: ellipsis; outline: none;
  transition: border-color .15s ease, background .15s ease;
}
input.chat-title:hover { border-color: var(--border); }
input.chat-title:focus { border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); background: var(--bg); }
.model-btn { max-width: 300px; overflow: hidden; font-family: var(--mono); font-size: 12.5px; color: var(--muted); }
.model-btn:hover { color: var(--text); }

.gov-slot { flex: none; padding: 10px 24px 0; }
.gov-slot:empty { padding: 0; }

.sysprompt { flex: none; width: 100%; max-width: 900px; margin: 0 auto; padding: 8px 24px 0; }
.sys-toggle {
  display: inline-flex; align-items: center; gap: 6px; padding: 2px 0;
  background: none; border: none; cursor: pointer;
  color: var(--muted); font: 12.5px var(--font); font-weight: 500;
}
.sys-toggle:hover { color: var(--text); }
.sys-dot { color: var(--accent); font-size: 9px; }
.sys-body { margin-top: 8px; display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }
.sys-text {
  width: 100%; padding: 10px 12px; border-radius: 10px; resize: vertical;
  border: 1px solid var(--border); background: var(--bg); color: var(--text);
  font: 13.5px/1.5 var(--font);
}
.sys-text:focus { outline: none; border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); }

/* min-height:0 is essential — without it the flex item won't shrink below its
   content height, so the scroll area overflows past the viewport and gets
   clipped by .main's overflow:hidden, hiding the composer and last messages. */
.chat-scroll { flex: 1 1 0%; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.chat-msgs {
  max-width: 900px; margin: 0 auto; padding: 28px 24px 40px;
  display: flex; flex-direction: column; gap: 24px;
}
.chat-msgs .chat-empty { text-align: center; padding: 60px 20px; font-size: 14px; }

.msg { display: flex; flex-direction: column; gap: 6px; animation: rise .2s ease; }
.msg-user { align-items: flex-end; }
.msg-user .msg-body {
  max-width: 80%; padding: 10px 14px;
  background: color-mix(in srgb, var(--accent) 15%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 28%, var(--border));
  border-radius: var(--radius); border-bottom-right-radius: 4px;
  white-space: pre-wrap; overflow-wrap: break-word; font-size: 15px;
}
.msg-assistant .msg-body { max-width: 100%; min-width: 0; }
.msg.streaming .msg-body::after {
  content: '▍'; color: var(--accent); margin-left: 2px;
  animation: blink 1s steps(2, start) infinite;
}
.msg-meta { font-size: 12px; color: var(--muted); display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.msg-error .msg-meta { color: var(--red); }

/* Failure cause + what to do about it, kept in the transcript instead of a toast that vanishes. */
.msg-errbox {
  border: 1px solid color-mix(in srgb, var(--red) 40%, transparent);
  border-left: 3px solid var(--red);
  background: color-mix(in srgb, var(--red) 9%, var(--surface));
  border-radius: 8px; padding: 10px 12px; font-size: 13px; max-width: 100%;
}
.msg-errbox-msg { color: var(--red); font-weight: 500; overflow-wrap: anywhere; }
.msg-errbox-hint { margin-top: 6px; color: var(--muted); overflow-wrap: anywhere; }

details.msg-reasoning {
  border: 1px solid var(--border); border-radius: 8px; background: var(--surface);
  font-size: 13px; color: var(--muted); max-width: 100%;
}
details.msg-reasoning summary { padding: 6px 12px; cursor: pointer; user-select: none; font-weight: 500; }
details.msg-reasoning pre {
  padding: 2px 14px 10px; white-space: pre-wrap; overflow-wrap: break-word;
  font: 12px/1.55 var(--mono);
}

/* composer (footer.composer > .chips-row + .controls-row + .input-row) */
.composer {
  flex: none; width: calc(100% - 48px); max-width: 900px; margin: 10px auto 20px;
  padding: 10px 12px 10px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.composer:focus-within {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 10%, transparent);
}
.chips-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.chips-row:not(:empty) { padding-bottom: 6px; }
.zdr-note { font-size: 11.5px; color: var(--green); }
.controls-row { position: relative; display: flex; align-items: center; gap: 12px; padding: 2px 0 6px; }
.ctl { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--muted); }
.temp-in, .reason-sel {
  padding: 4px 8px; border-radius: 8px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font: 12.5px var(--font);
}
.temp-in { width: 64px; }
.input-row { display: flex; align-items: flex-end; gap: 8px; }
.chat-input {
  flex: 1; max-height: 200px; padding: 6px 4px;
  border: none; background: transparent; resize: none; outline: none;
  color: var(--text); font: 15px/1.5 var(--font);
}
.send-btn { flex: none; min-width: 84px; }

/* vault-attach dropdown (anchored to .controls-row) */
.menu {
  position: absolute; bottom: calc(100% + 8px); left: 0; z-index: 60;
  min-width: 260px; max-width: 100%; max-height: 40vh; overflow-y: auto;
  background: var(--surface2); border: 1px solid var(--border); border-radius: 10px;
  padding: 6px; box-shadow: 0 12px 32px rgba(0, 0, 0, .45);
}
.menu-item {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  width: 100%; padding: 8px 10px; border: none; border-radius: 8px;
  background: none; color: var(--text); font: 13.5px var(--font);
  cursor: pointer; text-align: left;
}
.menu-item:hover:not(:disabled) { background: color-mix(in srgb, var(--accent) 14%, transparent); }
.menu-item:disabled { opacity: .45; cursor: not-allowed; }
.menu-sub { color: var(--muted); font-size: 12px; flex: none; }
.menu-empty { padding: 10px 12px; color: var(--muted); font-size: 13px; }

/* ---------- model picker modal (.modal.model-picker) ---------- */
.model-picker { max-width: 680px; padding: 0; display: flex; flex-direction: column; overflow: hidden; }
.mp-head { flex: none; display: flex; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--border); }
.mp-search {
  flex: 1; padding: 9px 12px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--bg); color: var(--text); font: inherit;
}
.mp-search:focus { outline: none; border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); }
.mp-provider {
  width: 160px; padding: 8px 10px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--bg); color: var(--text); font: 13.5px var(--font);
}
.mp-close { flex: none; font-size: 17px; padding: 4px 12px; }
.mp-list { overflow-y: auto; min-height: 200px; max-height: 55vh; padding: 8px; }
.mp-row {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 12px; border-radius: 10px; cursor: pointer;
}
.mp-row:hover, .mp-row.selected { background: var(--surface2); }
.mp-row.selected { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 45%, transparent); }
.mp-star {
  flex: none; background: none; border: none; padding: 2px;
  font-size: 16px; line-height: 1; cursor: pointer;
  color: color-mix(in srgb, var(--muted) 55%, transparent); transition: color .15s ease;
}
.mp-star:hover, .mp-star.faved { color: var(--amber); }
.mp-main { flex: 1; min-width: 0; }
.mp-name { font-size: 14px; font-weight: 550; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mp-id { font: 11.5px var(--mono); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mp-price, .mp-ctx {
  flex: none; text-align: right; font-size: 12px; color: var(--muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.mp-ctx { width: 64px; }
.mp-empty { padding: 40px 20px; text-align: center; color: var(--muted); }

/* ---------- dashboard ---------- */
.dash-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; margin-bottom: 16px; }
.dash-cards > .card { margin-top: 0; }
.card.stat { padding: 18px 20px; }
.stat-label { font-size: 11.5px; text-transform: uppercase; letter-spacing: .07em; font-weight: 600; margin-bottom: 8px; }
.stat-value { font-size: 26px; font-weight: 650; letter-spacing: -.01em; font-variant-numeric: tabular-nums; }
.stat-sub { font-size: 12px; margin-top: 6px; }

.dash-chart-card { margin-bottom: 16px; }
.dash-chart { width: 100%; }
.dash-chart svg { display: block; width: 100%; height: auto; }
.dash-bar { transition: fill-opacity .15s ease; }
.dash-hit { outline: none; }
.chart-tip {
  background: var(--surface2); border: 1px solid var(--border); border-radius: 8px;
  padding: 6px 10px; font-size: 12px; text-align: center; white-space: nowrap;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .4);
}
.chart-tip-value { font-weight: 600; font-variant-numeric: tabular-nums; }

.dash-cols { display: grid; grid-template-columns: 3fr 2fr; gap: 16px; align-items: start; }
.dash-cols > .card { margin-top: 0; }
.dash-error { display: flex; flex-direction: column; align-items: center; gap: 14px; }

.meter { margin: 2px 0 12px; }
.meter-track {
  position: relative; height: 10px; border-radius: 999px;
  background: var(--surface2); border: 1px solid var(--border);
}
.meter-fill { height: 100%; border-radius: 999px; max-width: 100%; }
.meter-green { background: var(--green); }
.meter-amber { background: var(--amber); }
.meter-red { background: var(--red); }
.meter-tick { position: absolute; top: -3px; bottom: -3px; width: 2px; background: var(--text); opacity: .45; }
.meter-label { font-size: 12px; margin-top: 6px; }
.dash-gov-note { font-size: 13px; margin-top: 8px; }

.dash-log-title {
  font-size: 11.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .07em;
  color: var(--muted); margin: 18px 0 6px;
}
.dash-log { list-style: none; font-size: 13px; }
.dash-log li {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  padding: 7px 0; border-top: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
}
.dash-log li:first-child { border-top: none; }
.dot { width: 8px; height: 8px; border-radius: 50%; flex: none; align-self: center; background: var(--muted); }
.dot-amber { background: var(--amber); }
.dot-red { background: var(--red); }
.dot-green { background: var(--green); }
.dot-accent { background: var(--accent); }
.dot-muted { background: var(--muted); }
.dash-log-ts { font-variant-numeric: tabular-nums; white-space: nowrap; font-size: 12px; }
.dash-log-event { font-weight: 500; }
.dash-log-detail { font-size: 12px; overflow-wrap: anywhere; }

/* ---------- vault ---------- */
.vault-notice {
  border: 1px solid color-mix(in srgb, var(--amber) 38%, transparent);
  border-left: 3px solid var(--amber);
  background: color-mix(in srgb, var(--amber) 8%, var(--surface));
  border-radius: 10px; padding: 13px 16px; font-size: 13px; color: var(--muted);
  margin-bottom: 22px;
}
.vault-notice strong { color: var(--amber); }
.toolbar { display: flex; gap: 10px; margin-bottom: 20px; }
.toolbar .vault-search { flex: 1; max-width: 420px; }
.vault-group { margin-bottom: 26px; }
.vault-cat {
  margin-bottom: 10px; font-size: 11.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .08em; color: var(--muted);
}
.vault-row {
  display: flex; align-items: center; gap: 14px; padding: 13px 16px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  margin-bottom: 8px;
}
.vault-main { min-width: 0; flex: 1; }
.vault-name { font-weight: 600; font-size: 14px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.vault-user { font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vault-secret { display: flex; align-items: center; gap: 6px; margin-top: 4px; min-width: 0; }
.vault-mask { font-size: 13px; color: var(--muted); overflow-wrap: anywhere; }
.vault-notes { font-size: 12.5px; margin-top: 4px; overflow-wrap: anywhere; }
.vault-actions { display: flex; gap: 4px; flex: none; }

/* ---------- settings ---------- */
.set-section h2 { margin-bottom: 14px; }
.field-row { display: flex; gap: 16px; }
.field-row .field { flex: 1; margin-top: 0; }
.set-accent-row { display: flex; align-items: center; gap: 10px; }
input[type="color"].set-accent {
  width: 44px; height: 32px; padding: 2px; flex: none;
  border: 1px solid var(--border); border-radius: 8px; background: var(--bg); cursor: pointer;
}

/* searchable model dropdown (settings) */
.picker { position: relative; }
.picker-btn {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  text-align: left; cursor: pointer;
}
.picker-btn > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker-drop {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 70;
  background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
  padding: 8px; box-shadow: 0 16px 40px rgba(0, 0, 0, .5);
}
.picker-search { margin-bottom: 8px; }
.picker-list { max-height: 300px; overflow-y: auto; }
.picker-row {
  display: flex; align-items: baseline; gap: 10px; width: 100%;
  padding: 8px 10px; border: none; border-radius: 8px;
  background: none; color: var(--text); font: 13.5px var(--font);
  cursor: pointer; text-align: left;
}
.picker-row:hover { background: var(--surface2); }
.picker-row.selected { background: color-mix(in srgb, var(--accent) 14%, transparent); }
.picker-name { flex: none; font-weight: 550; }
.picker-id { flex: 1; min-width: 0; font-size: 11.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker-meta { flex: none; font-size: 12px; white-space: nowrap; }

/* ---------- responsive (<768px: overlay sidebar) ---------- */
@media (max-width: 767px) {
  .sidebar {
    position: fixed; top: 0; bottom: 0; left: 0; z-index: 120; width: 280px;
    transform: translateX(-100%); transition: transform .22s ease;
    box-shadow: 24px 0 60px rgba(0, 0, 0, .45);
  }
  body.nav-open .sidebar { transform: none; }
  .side-backdrop { position: fixed; inset: 0; z-index: 110; background: rgba(4, 6, 10, .55); }
  body:not(.nav-open) .side-backdrop { display: none; }
  body.nav-open .side-backdrop { display: block; }
  .hamburger { display: inline-flex; }
  .chat-header { padding-left: 62px; }
  .page { padding: 24px 16px 32px; padding-top: 64px; }
  .chat-msgs { padding-left: 14px; padding-right: 14px; }
  .composer { width: calc(100% - 28px); }
  .msg-user .msg-body { max-width: 92%; }
  .dash-cols, .field-row { display: block; }
  .dash-cols > .card + .card { margin-top: 16px; }
  .field-row .field + .field { margin-top: 14px; }
}

/* ============================================================
   v2 — multi-user, admin, slash commands, per-chat sandbox
   Classes shared with the other frontend workstreams (chat.js,
   app.js) are defined here per CONTRACT-v2 §G; those files USE
   them and do not redefine CSS.
   ============================================================ */

/* ---------- segmented tabs (admin tabs + dashboard my/all toggle) ---------- */
.tabs {
  display: inline-flex; gap: 2px; padding: 3px;
  background: var(--surface2); border: 1px solid var(--border); border-radius: 10px;
}
.tab {
  padding: 6px 14px; border: none; border-radius: 7px; background: none;
  color: var(--muted); font: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.tab:hover { color: var(--text); }
.tab.active { background: var(--surface); color: var(--text); box-shadow: 0 1px 2px rgba(0, 0, 0, .3); }
.page-head .tabs { align-self: center; }

/* ---------- role badge (admin.js + app.js account menu) ---------- */
.role-badge {
  display: inline-block; padding: 1px 7px; border-radius: 999px; vertical-align: middle;
  font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  background: var(--surface2); border: 1px solid var(--border); color: var(--muted);
}
.role-badge.admin {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent); color: var(--accent);
}

/* ---------- inline notice (modals + settings sandbox warning) ---------- */
.notice-inline {
  border: 1px solid var(--border); border-radius: 8px; background: var(--surface2);
  padding: 8px 11px; margin-top: 10px; font-size: 12.5px; line-height: 1.5; color: var(--muted);
}
.notice-inline.warn {
  border-color: color-mix(in srgb, var(--amber) 40%, transparent);
  border-left: 3px solid var(--amber);
  background: color-mix(in srgb, var(--amber) 8%, var(--surface));
}
.notice-inline strong { color: var(--text); }
.notice-inline.warn strong { color: var(--amber); }

/* ---------- admin page: users table + usage ---------- */
.admin-count { font-size: 13px; }
.admin-table-card { padding: 8px 10px; }
.admin-table td { vertical-align: middle; }
.admin-user { font-weight: 600; }
.admin-seen { white-space: nowrap; font-size: 12.5px; }
.admin-table tr.is-disabled td { opacity: .55; }
.admin-role {
  width: auto; padding: 5px 9px; font-size: 13px; border-radius: 8px;
  font-family: var(--font); cursor: pointer;
}
.admin-acts { display: flex; gap: 4px; justify-content: flex-end; flex-wrap: wrap; }
.admin-form .field + .field { margin-top: 14px; }

/* one-time generated-password reveal */
.gen-pw {
  display: flex; align-items: center; gap: 10px; margin-top: 6px;
  padding: 10px 12px; border-radius: 10px;
  background: var(--bg); border: 1px dashed color-mix(in srgb, var(--accent) 45%, var(--border));
}
.gen-pw-value {
  flex: 1; min-width: 0; font-size: 15px; letter-spacing: .04em;
  color: var(--text); overflow-wrap: anywhere; user-select: all;
}
.gen-pw-copy { flex: none; }

/* ---------- settings: sandbox section ---------- */
.set-global-note { margin: -6px 0 14px; }
.set-field-gap { margin-top: 16px; }
.sandbox-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(184px, 1fr));
  gap: 14px; margin-top: 14px;
}
.sandbox-grid .field { margin-top: 0; }

/* ---------- vault: sandbox exfiltration notice ---------- */
.vault-notice-sandbox {
  border-color: color-mix(in srgb, var(--red) 38%, transparent);
  border-left: 3px solid var(--red);
  background: color-mix(in srgb, var(--red) 8%, var(--surface));
  margin-top: -12px; margin-bottom: 22px;
}
.vault-notice-sandbox strong { color: var(--red); }

/* ============================================================
   Classes consumed by chat.js (sandbox tool loop + slash) and
   app.js (account menu, change-password modal).
   ============================================================ */

/* ---------- mounted-credential chips (chat.js) ---------- */
.cred-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 10px; border-radius: 999px;
  font: 12px var(--mono);
  background: color-mix(in srgb, var(--amber) 12%, var(--surface2));
  border: 1px solid color-mix(in srgb, var(--amber) 35%, transparent); color: var(--amber);
}
.cred-chip .cred-path { color: color-mix(in srgb, var(--amber) 70%, var(--text)); }

/* ---------- slash-command echo (ephemeral, non-persisted) ---------- */
.slash-note {
  align-self: stretch; max-width: 900px; width: 100%; margin: 0 auto;
  padding: 3px 2px 3px 11px; font-size: 12.5px; color: var(--muted);
  border-left: 2px solid color-mix(in srgb, var(--accent) 55%, transparent);
}
.slash-note .cmd { font-family: var(--mono); color: var(--text); }
.slash-note.err { border-left-color: var(--red); }

/* ---------- tool activity blocks (agentic sandbox loop) ---------- */
.tool-block {
  max-width: 100%; overflow: hidden;
  border: 1px solid var(--border); border-radius: 10px; background: var(--surface);
  font-size: 13px;
}
.tool-block + .tool-block { margin-top: 8px; }
.tool-block > summary, .tool-block .tool-head {
  display: flex; align-items: center; gap: 8px; padding: 8px 12px;
  cursor: pointer; user-select: none; list-style: none; font-weight: 500;
}
.tool-block > summary::-webkit-details-marker { display: none; }
.tool-block > summary::before {
  content: '▸'; color: var(--muted); font-size: 11px; transition: transform .15s ease;
}
.tool-block[open] > summary::before { transform: rotate(90deg); }
.tool-name { font-weight: 600; color: var(--text); }
.tool-cmd {
  font-family: var(--mono); font-size: 12.5px; color: var(--text);
  white-space: pre-wrap; overflow-wrap: anywhere; min-width: 0;
}
.tool-block > summary .tool-cmd {
  flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tool-out, .tool-err {
  margin: 0; padding: 10px 12px; border-top: 1px solid var(--border);
  background: #0d1117; color: var(--text); font: 12px/1.55 var(--mono);
  white-space: pre-wrap; overflow-wrap: anywhere;
  max-height: 60vh; overflow: auto;
}
.tool-err { color: color-mix(in srgb, var(--red) 78%, var(--text)); }
.tool-out:empty, .tool-err:empty { display: none; }
.tool-exit {
  flex: none; margin-left: auto;
  padding: 1px 8px; border-radius: 999px; font: 11px var(--mono);
  background: var(--surface2); border: 1px solid var(--border); color: var(--muted);
}
.tool-exit.ok { color: var(--green); border-color: color-mix(in srgb, var(--green) 40%, transparent); }
.tool-exit.bad { color: var(--red); border-color: color-mix(in srgb, var(--red) 40%, transparent); }
.tool-dur { flex: none; font-size: 11.5px; color: var(--muted); font-variant-numeric: tabular-nums; }

/* ---------- approval bar (approval-mode tool request) ---------- */
.approval-bar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 10px 12px; border-radius: 10px; font-size: 13px;
  border: 1px solid color-mix(in srgb, var(--amber) 40%, transparent);
  border-left: 3px solid var(--amber);
  background: color-mix(in srgb, var(--amber) 8%, var(--surface));
}
.approval-bar .approval-label { color: var(--amber); font-weight: 600; flex: none; }
.approval-bar .approval-cmd, .approval-bar input.approval-edit {
  flex: 1; min-width: 160px;
  padding: 6px 10px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--bg); color: var(--text);
  font: 12.5px var(--mono);
}
.approval-bar input.approval-edit:focus {
  outline: none; border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
}
.approval-bar .approval-acts { display: flex; gap: 6px; flex: none; margin-left: auto; }

/* ---------- account menu (app.js): .account-menu anchors, .account-drop floats ---------- */
.account-menu { position: relative; }
.account-btn { width: 100%; justify-content: flex-start; gap: 8px; }
.account-drop {
  position: absolute; bottom: calc(100% + 6px); left: 0; right: 0; z-index: 90; min-width: 210px;
  background: var(--surface2); border: 1px solid var(--border); border-radius: 10px;
  padding: 6px; box-shadow: 0 12px 32px rgba(0, 0, 0, .45);
}
.account-drop[hidden] { display: none; }
.account-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.account-drop button, .account-drop a {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 8px 10px; border: none; border-radius: 8px; background: none;
  color: var(--text); font: inherit; font-size: 13.5px; text-align: left; cursor: pointer;
}
.account-drop button:hover, .account-drop a:hover {
  background: color-mix(in srgb, var(--accent) 14%, transparent); text-decoration: none;
}

/* ---------- change-password modal (app.js) ---------- */
.change-pw-modal .field + .field { margin-top: 14px; }
.change-pw-modal .modal-actions { margin-top: 20px; }
.change-pw-modal .pw-err { min-height: 16px; margin-top: 10px; color: var(--red); font-size: 13px; }
.change-pw-modal .pw-forced { color: var(--muted); font-size: 13px; margin-bottom: 14px; }

/* ---------- tool status + indicators + cred manifest (consumed by chat.js) ---------- */
.tool-status { flex: none; font-size: 11.5px; color: var(--muted); }
.tool-status.running { color: var(--muted); }
.tool-status.pending { color: var(--amber); }
.tool-status.ok { color: var(--green); }
.tool-status.bad { color: var(--red); }
.tool-metaline { display: flex; align-items: center; gap: 8px; padding: 4px 12px; }
.tool-metaline:empty { display: none; }

.chat-indicators { display: flex; flex-wrap: wrap; gap: 6px; }
.chat-indicators:empty { display: none; }
.indicator {
  font-size: 11.5px; color: var(--muted);
  padding: 2px 8px; border-radius: 999px;
  background: var(--surface2); border: 1px solid var(--border);
}
.indicator.ind-sandbox { color: color-mix(in srgb, var(--accent) 70%, var(--text)); }

.cred-manifest { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0; }
.cred-manifest:empty { display: none; }
.cred-manifest-head { flex-basis: 100%; font-size: 12px; color: var(--muted); }
.cred-warn {
  flex-basis: 100%; font-size: 11.5px;
  color: color-mix(in srgb, var(--amber) 70%, var(--text));
}
.zdr-note { font-size: 11.5px; color: var(--muted); align-self: center; }

/* ---------- v2 responsive ---------- */
@media (max-width: 767px) {
  .page-head .tabs { align-self: flex-start; }
  .sandbox-grid { grid-template-columns: 1fr 1fr; }
  .approval-bar .approval-acts { margin-left: 0; }
}

/* ---------- keyframes ---------- */
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes blink { 50% { opacity: 0; } }
@keyframes streamPulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent); opacity: 1; }
  70%  { box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent) 0%, transparent); opacity: .55; }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 0%, transparent); opacity: 1; }
}
@keyframes pop { from { opacity: 0; transform: scale(.96); } }
@keyframes rise { from { opacity: 0; transform: translateY(6px); } }
@keyframes shake {
  10%, 90% { transform: translateX(-2px); }
  20%, 80% { transform: translateX(4px); }
  30%, 50%, 70% { transform: translateX(-6px); }
  40%, 60% { transform: translateX(6px); }
}

/* ===== v2 surfaces (billing, catalogue, jobs, imports, admin) ===== */
.page-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:16px}
.page-head h1{margin:0;font-size:1.4rem}
.card{background:var(--panel,#1b1930);border:1px solid var(--border,#2a2740);border-radius:12px;padding:16px;margin-bottom:16px}
.card h2{margin:0 0 12px;font-size:1.05rem}
.tbl{width:100%;border-collapse:collapse;font-size:.9rem}
.tbl th,.tbl td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--border,#2a2740)}
.tbl th{color:var(--muted,#8b88a8);font-weight:600;font-size:.8rem}
.badge{display:inline-block;padding:1px 8px;border-radius:20px;background:#2a2740;font-size:.72rem;color:#cfc9ff}
.badge.warn{background:#4a2a1a;color:#ffc196}.badge.danger{background:#4a1a24;color:#ff9db0}
.chip{display:inline-block;padding:1px 7px;border-radius:6px;background:#26243c;font-size:.68rem;color:#a9a4d0;margin:2px 3px 0 0}
.btn-xs{padding:2px 8px;font-size:.72rem}.btn-sm{padding:4px 10px;font-size:.82rem}
.danger{color:#ff9db0}
.row-actions{display:flex;gap:4px;flex-wrap:wrap}
.hint{font-size:.8rem;margin-top:8px}.field{display:flex;flex-direction:column;gap:4px;margin-bottom:10px;font-size:.85rem}
.check{display:flex;align-items:center;gap:8px;padding:4px 0;font-size:.85rem}
.tabs{display:flex;gap:4px;margin-bottom:16px;border-bottom:1px solid var(--border,#2a2740)}
.tab{background:none;border:none;color:var(--muted,#8b88a8);padding:8px 14px;cursor:pointer;text-transform:capitalize;border-bottom:2px solid transparent}
.tab.active{color:var(--accent,#7c6cf2);border-bottom-color:var(--accent,#7c6cf2)}
/* billing */
.bill-balance .bal-value{font-size:2rem;font-weight:700;margin:4px 0}
.bal-bar{height:8px;background:#26243c;border-radius:6px;overflow:hidden;margin:8px 0 4px}
.bal-bar-fill{height:100%;background:var(--accent,#7c6cf2)}
.topup-row{display:flex;gap:16px;align-items:flex-end;flex-wrap:wrap}.topup-quote{font-size:1.05rem;padding-bottom:10px}
.pool-card .pool-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:14px}
.pool-num{font-size:1.4rem;font-weight:700}.pool-alert{margin-top:12px;padding:8px 12px;border-radius:8px;background:#4a2a1a;color:#ffc196}
.pool-danger{border-color:#7a2a3a}.pool-danger .pool-alert{background:#4a1a24;color:#ff9db0}.pool-warn{border-color:#7a5a2a}
/* catalogue */
.tier-head{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}.tier-reason{font-size:.85rem}
.model-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:12px;margin-top:10px}
.model-card{border:1px solid var(--border,#2a2740);border-radius:10px;padding:12px}
.model-card.recommended{border-color:var(--accent,#7c6cf2);box-shadow:0 0 0 1px var(--accent,#7c6cf2) inset}
.mc-name{font-weight:600;display:flex;justify-content:space-between;gap:6px;align-items:center}
.mc-id{font-size:.72rem;word-break:break-all;margin:2px 0}.mc-meta{font-size:.75rem;margin:8px 0}
/* jobs */
.jobs-split{display:grid;grid-template-columns:280px 1fr;gap:16px}
.job-row{padding:10px;border:1px solid var(--border,#2a2740);border-radius:8px;margin-bottom:8px;cursor:pointer}
.job-row:hover{border-color:var(--accent,#7c6cf2)}.job-row-top{display:flex;justify-content:space-between;gap:6px}
.job-log{background:#0f0e1a;border-radius:8px;padding:12px;max-height:60vh;overflow:auto;font-size:.8rem;white-space:pre-wrap}
.job-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}
.job-head-actions{display:flex;gap:8px;align-items:center}
.state-running{background:#1a3a4a;color:#96d9ff}.state-done{background:#1a4a2a;color:#9dffb0}
/* A run ModelHub only WATCHES: the goal as typed, then the facts about it. */
.cc-facts{margin:10px 0;padding:10px 12px;border:1px solid var(--border,#2a2740);border-radius:8px;display:flex;flex-direction:column;gap:6px}
.cc-facts .cc-goal{font-weight:600;white-space:pre-wrap;word-break:break-word}
.cc-facts .cc-last{font-family:var(--mono,monospace);font-size:.78rem;opacity:.85;white-space:pre-wrap;word-break:break-word;max-height:9em;overflow:auto}
.cc-facts code{font-family:var(--mono,monospace)}
.state-failed{background:#4a1a24;color:#ff9db0}.state-paused_no_funds{background:#4a2a1a;color:#ffc196}
/* imports */
.dropzone{border:2px dashed var(--border,#3a3658);border-radius:12px;padding:28px;text-align:center;transition:.15s}
.dropzone.over{border-color:var(--accent,#7c6cf2);background:#211f38}.dz-inner{font-size:1rem;margin-bottom:6px}
.link{color:var(--accent,#7c6cf2);cursor:pointer;text-decoration:underline}
.imp-row{padding:10px 0;border-bottom:1px solid var(--border,#2a2740)}
.imp-actions{display:flex;gap:6px;flex-wrap:wrap;margin-top:6px}.tag-imported{color:#8b88a8;font-style:italic}
.preview-box{margin-top:14px;padding:12px;background:#211f38;border-radius:8px}
.modal-wide{max-width:min(760px,92vw);max-height:86vh;overflow:auto}
.imp-view{max-height:56vh;overflow:auto;margin:8px 0}
.imp-msg{padding:8px 10px;border-radius:8px;margin-bottom:6px;background:#1b1930}.imp-role{font-size:.72rem;text-transform:uppercase}
.imp-assistant{border-left:3px solid #10b981}.imp-user{border-left:3px solid #3b82f6}
.eg-add{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px}
/* mobile */
@media(max-width:760px){.jobs-split{grid-template-columns:1fr}.model-grid{grid-template-columns:1fr 1fr}.topup-row{flex-direction:column;align-items:stretch}}

/* ===== slash-command palette (§E3) ===== */
.input-row { position: relative; }
.cmd-palette { position:absolute; bottom:calc(100% + 6px); left:0; right:0; background:#1b1930; border:1px solid #2a2740; border-radius:10px; box-shadow:0 10px 30px rgba(0,0,0,.45); max-height:44vh; overflow-y:auto; z-index:50; padding:4px; }
.cmd-palette.hidden { display:none; }
.cmd-item { display:grid; grid-template-columns:auto auto 1fr; gap:10px; align-items:baseline; padding:7px 10px; border-radius:7px; cursor:pointer; }
.cmd-item.sel, .cmd-item:hover { background:#262340; }
.cmd-name { color:var(--accent,#7c6cf2); font-weight:600; font-family:ui-monospace,monospace; }
.cmd-args { color:#8b88a8; font-size:.78rem; font-family:ui-monospace,monospace; white-space:nowrap; }
.cmd-desc { color:#cfc9df; font-size:.82rem; text-align:right; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* ===== chat file/photo attachments ===== */
.attach-row { display:flex; flex-wrap:wrap; gap:8px; }
.attach-row.has { margin:6px 0; }
.att-chip { position:relative; display:inline-flex; align-items:center; border:1px solid #2a2740; border-radius:8px; overflow:hidden; background:#1b1930; }
.att-chip img { width:52px; height:52px; object-fit:cover; display:block; }
.att-chip-file { padding:6px 26px 6px 10px; font-size:.8rem; color:#cfc9df; }
.att-x { position:absolute; top:2px; right:2px; width:18px; height:18px; line-height:15px; text-align:center; border:none; border-radius:50%; background:rgba(0,0,0,.6); color:#fff; cursor:pointer; font-size:13px; padding:0; }
.msg-atts { display:flex; flex-wrap:wrap; gap:6px; margin-bottom:6px; }
.att-thumb { max-width:240px; max-height:240px; border-radius:8px; display:block; }
.att-file { display:inline-block; padding:4px 10px; border:1px solid #2a2740; border-radius:8px; font-size:.8rem; color:#cfc9df; }
@media(max-width:760px){ .cmd-desc{ display:none } .att-thumb{ max-width:160px; max-height:160px } }

/* ===== BYO agent accounts ===== */
.acct-row{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-bottom:10px}
.login-steps{margin:8px 0;padding-left:20px;line-height:2.1}
.login-url{word-break:break-all}
.agent-msg{width:100%;margin-bottom:8px}
.agent-out{background:#0f0e1a;border-radius:8px;padding:12px;min-height:40px;white-space:pre-wrap;margin-top:10px;font-size:.85rem;overflow:auto;max-height:50vh}
.login-code{min-width:260px;margin-right:8px}

/* ===== mobile Chrome: uncramp the chat (dynamic viewport, roomier bubbles, wrapping header) ===== */
@media (max-width: 767px) {
  /* 100vh is wrong on mobile Chrome (URL bar) — dvh keeps the composer above the keyboard/toolbar */
  .shell, .main { height: 100dvh; }

  /* header controls wrap instead of squeezing into one overflowing row */
  .chat-header { flex-wrap: wrap; row-gap: 8px; padding: 10px 12px 10px 58px; }
  .chat-header .chat-title { flex: 1 1 100%; min-width: 0; font-size: 15px; }
  .chat-header .spacer { display: none; }
  .chat-header .btn, .chat-header .engine-sel, .chat-header .sandbox-mode-sel { font-size: 12px; padding: 5px 9px; }

  /* breathing room + readable line length; bubbles no longer hug the edges */
  .chat-msgs { padding: 14px 12px 18px; gap: 18px; }
  .msg { gap: 8px; }
  .msg-user .msg-body { max-width: 88%; padding: 10px 13px; border-radius: 14px; }
  .msg-assistant .msg-body { font-size: 15px; line-height: 1.6; }
  .msg-body pre { font-size: 12.5px; }
  .msg-body pre, .msg-body table { overflow-x: auto; max-width: 100%; }
  .msg-meta { font-size: 11px; gap: 8px; }

  /* composer: full-width, thumb-friendly targets, no zoom-on-focus (needs >=16px) */
  .composer { width: 100%; border-radius: 14px 14px 0 0; padding: 10px 12px calc(10px + env(safe-area-inset-bottom)); }
  .chat-input { font-size: 16px; }
  .composer .btn, .controls-row .btn { min-height: 44px; }
  .controls-row { flex-wrap: wrap; row-gap: 8px; }
  .send-btn { min-width: 76px; }
}

/* ===== skills page ===== */
.skill-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}
.skill-tile{border:1px solid var(--border,#2a2740);border-radius:10px;padding:12px;display:flex;flex-direction:column;gap:6px}
.skill-tile.on{border-color:var(--accent,#7c6cf2);box-shadow:0 0 0 1px var(--accent,#7c6cf2) inset}
.st-head{display:flex;justify-content:space-between;align-items:center;gap:8px}
.st-desc{font-size:.8rem;line-height:1.45}
.st-del{align-self:flex-start;margin-top:4px}
@media(max-width:767px){.skill-grid{grid-template-columns:1fr}}

/* ===== ultracode live tree (in-chat) ===== */
.uc-box{border:1px solid var(--accent,#7c6cf2);border-radius:10px;padding:10px 12px}
.uc-box.uc-done{border-color:#2a7a4a}.uc-box.uc-err{border-color:#7a2a3a}
.uc-head{font-weight:600;margin-bottom:6px}
.uc-state{font-weight:400;color:var(--muted,#8b88a8);font-size:.85em}
.uc-node{font-size:.85rem;padding:2px 0;color:#cfc9df}
.uc-node.uc-queued{opacity:.6}
.uc-node.uc-done{color:#9dffb0}
.uc-head{display:flex;align-items:center;gap:8px}
.uc-head .uc-stop{margin-left:auto}
.uc-node.uc-verifying,.uc-node.uc-fixing{color:#ffc196}

/* ---- /buddy: the collectible terminal pet ---- */
.buddy-widget{position:fixed;right:16px;bottom:16px;z-index:60;width:170px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;pointer-events:none}
.buddy-widget.hidden{display:none}
.buddy-widget>*{pointer-events:auto}
.buddy-body{background:var(--surface2);border:1px solid var(--border);border-radius:12px;padding:8px 10px;box-shadow:0 8px 24px rgba(0,0,0,.35)}
.buddy-widget.shiny .buddy-body{border-color:color-mix(in srgb,#ffd76a 55%,var(--border));box-shadow:0 0 0 1px color-mix(in srgb,#ffd76a 30%,transparent),0 8px 24px rgba(0,0,0,.4)}
.buddy-art{margin:0;font-size:13px;line-height:1.15;white-space:pre;text-align:center;color:var(--text);cursor:pointer;user-select:none;animation:buddy-bob 3.2s ease-in-out infinite}
@keyframes buddy-bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-2px)}}
.buddy-meta{display:flex;align-items:center;gap:6px;margin-top:6px;font-size:11px}
.buddy-name{font-weight:700;color:var(--text);max-width:74px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.buddy-rarity{font-size:10px;padding:1px 5px;border-radius:6px;border:1px solid var(--border);color:var(--muted)}
.buddy-rarity.r-uncommon{color:#7fe08a;border-color:#2f5f37}
.buddy-rarity.r-rare{color:#6db3ff;border-color:#28517f}
.buddy-rarity.r-epic{color:#c79bff;border-color:#5a3f86}
.buddy-rarity.r-legendary{color:#ffd76a;border-color:#7a6320}
.buddy-actions{margin-left:auto;display:flex;gap:2px}
.buddy-actions button{background:transparent;border:none;color:var(--muted);cursor:pointer;font-size:12px;padding:1px 4px;border-radius:5px;line-height:1}
.buddy-actions button:hover{background:var(--bg);color:var(--text)}
.buddy-bubble{background:var(--text);color:#0b0e14;border-radius:12px;padding:7px 10px;font-family:inherit;font-size:12px;line-height:1.35;margin-bottom:8px;max-width:100%;box-shadow:0 6px 18px rgba(0,0,0,.35);position:relative}
.buddy-bubble.hidden{display:none}
.buddy-bubble::after{content:"";position:absolute;left:22px;bottom:-6px;border:6px solid transparent;border-top-color:var(--text);border-bottom:0}
.buddy-card{margin-top:8px;background:var(--surface2);border:1px solid var(--border);border-radius:10px;padding:8px 10px;font-size:11px}
.buddy-card.hidden{display:none}
.bcard-head{font-weight:700;margin-bottom:6px;font-size:11px}
.bstat{display:flex;align-items:center;gap:6px;margin:3px 0}
.bstat span{width:74px;color:var(--muted);font-size:10px}
.bstat i{flex:1;height:5px;border-radius:3px;background:linear-gradient(90deg,var(--accent),color-mix(in srgb,var(--accent) 40%,#6db3ff));display:block}
.bstat b{width:22px;text-align:right;font-weight:600}
.bcard-soul{margin-top:6px;color:var(--muted);font-style:italic;font-size:11px}
.buddy-heart{position:absolute;bottom:40px;color:#ff7ab6;font-size:14px;pointer-events:none;animation:buddy-heart 1.3s ease-out forwards}
@keyframes buddy-heart{0%{opacity:1;transform:translateY(0) scale(.8)}100%{opacity:0;transform:translateY(-46px) scale(1.2)}}
.slash-note.buddy-line{color:#ff9ecb;border-left:3px solid #ff7ab6}
@media (max-width:640px){.buddy-widget{width:132px;right:8px;bottom:8px}.buddy-art{font-size:11px}}

/* ---- embedded Builder panel ---- */
.builder-page{position:absolute;inset:0;display:flex;flex-direction:column}
.builder-note{padding:8px 14px;border-bottom:1px solid var(--border);font-size:12px}
.builder-frame{flex:1;width:100%;border:0;background:#0b0e14}
/* ================= §F2 compacting + §F13 chat search / anonymous chats ================= */

/* context meter button in the chat header */
.ctx-btn{font-variant-numeric:tabular-nums}
.ctx-btn.ctx-warm{color:var(--amber);border-color:color-mix(in srgb,var(--amber) 45%,var(--border))}
.ctx-btn.ctx-hot{color:var(--red);border-color:color-mix(in srgb,var(--red) 50%,var(--border))}

.compact-menu{position:absolute;right:12px;left:auto;top:46px;bottom:auto;z-index:40;width:340px;max-width:calc(100vw - 24px);
  background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:12px;
  box-shadow:0 18px 44px rgba(0,0,0,.5);display:flex;flex-direction:column;gap:8px}
.cmpt-head{font-weight:700;font-size:12px;letter-spacing:.02em;text-transform:uppercase;color:var(--muted)}
.cmpt-bar{position:relative;height:8px;border-radius:5px;background:var(--surface2);overflow:hidden}
.cmpt-bar i{display:block;height:100%;background:linear-gradient(90deg,var(--accent),color-mix(in srgb,var(--accent) 40%,#6db3ff))}
.cmpt-bar u{position:absolute;top:-2px;width:2px;height:12px;background:var(--amber);text-decoration:none}
.cmpt-nums{font-size:12px;font-variant-numeric:tabular-nums}
.cmpt-note{font-size:11px;color:var(--muted);line-height:1.45}
.cmpt-note code{font-family:var(--mono);font-size:10px}
.cmpt-toggle{display:flex;align-items:center;gap:8px;font-size:12px;cursor:pointer}
.cmpt-now{align-self:flex-start}

/* the "Compacted X messages" marker — visible, collapsible, originals inside */
.compact-marker{border:1px dashed color-mix(in srgb,var(--accent) 45%,var(--border));border-radius:var(--radius);
  background:color-mix(in srgb,var(--accent) 6%,transparent);margin:14px 0;padding:0}
.compact-marker>summary{cursor:pointer;list-style:none;padding:10px 12px;display:flex;align-items:center;gap:8px;
  flex-wrap:wrap;font-size:12px}
.compact-marker>summary::-webkit-details-marker{display:none}
.compact-marker>summary::before{content:"\25B8";color:var(--muted);font-size:11px}
.compact-marker[open]>summary::before{content:"\25BE"}
.cm-badge{background:color-mix(in srgb,var(--accent) 25%,transparent);color:var(--text);border-radius:999px;
  padding:1px 8px;font-size:10px;text-transform:uppercase;letter-spacing:.04em}
.cm-meta{color:var(--muted);font-size:11px;font-variant-numeric:tabular-nums}
.cm-summary{padding:0 12px 10px 12px;font-size:13px;border-bottom:1px solid var(--border)}
.cm-origlabel{padding:8px 12px 0;font-size:11px;color:var(--muted)}
.cm-orig{padding:4px 12px 12px;opacity:.72}
.cm-orig .msg{margin:8px 0}

/* chat search modal */
.chat-search{width:min(760px,94vw);max-width:min(760px,94vw);max-height:86vh;display:flex;flex-direction:column;gap:10px;padding:14px}
.cs-head{display:flex;gap:8px;align-items:center}
.cs-q{flex:1}
.cs-filters{display:flex;gap:12px;flex-wrap:wrap;font-size:12px;color:var(--muted)}
.cs-filters label{display:flex;align-items:center;gap:6px}
.cs-results{flex:1;overflow:auto;min-height:120px;display:flex;flex-direction:column;gap:6px}
.cs-row{text-align:left;background:var(--surface2);border:1px solid var(--border);border-radius:10px;
  padding:8px 10px;color:var(--text);cursor:pointer;font:inherit;display:block;width:100%}
.cs-row:hover{border-color:color-mix(in srgb,var(--accent) 55%,var(--border))}
.cs-row-head{font-size:12px;font-weight:600;margin-bottom:3px}
.cs-snip{font-size:12px;color:var(--muted);line-height:1.45;word-break:break-word}
.cs-snip mark{background:color-mix(in srgb,var(--accent) 40%,transparent);color:var(--text);border-radius:3px;padding:0 2px}
.cs-empty{color:var(--muted);font-size:12px;padding:14px;text-align:center}
.cs-ai{display:flex;align-items:center;gap:8px;font-size:13px;padding-top:8px;border-top:1px solid var(--border);cursor:pointer}
.cs-ainote{font-size:11px;color:var(--muted);line-height:1.45}

/* anonymous chats */
.anon-btn.anon-on{color:var(--green);border-color:color-mix(in srgb,var(--green) 50%,var(--border))}
.anon-menu{position:absolute;right:12px;left:auto;top:46px;bottom:auto;z-index:40;width:400px;max-width:calc(100vw - 24px);
  background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:12px;
  box-shadow:0 18px 44px rgba(0,0,0,.5);display:flex;flex-direction:column;gap:8px;max-height:70vh;overflow:auto}
.anon-menu-body p{font-size:11px;color:var(--muted);line-height:1.5;margin-bottom:7px}
.anon-menu-body b{color:var(--text)}
.anon-modal{width:min(560px,94vw);max-width:min(560px,94vw);display:flex;flex-direction:column;gap:10px;padding:16px}
.anon-modal h3{font-size:15px}
.anon-body p{font-size:12px;color:var(--muted);line-height:1.5;margin-bottom:8px}
.anon-body b{color:var(--text)}
.anon-modal input[type=password]{width:100%}
.anon-err{color:var(--red);font-size:12px;min-height:16px}
.anon-actions{display:flex;gap:8px;justify-content:flex-end}
.anon-lockpanel{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
  padding:18px;margin:18px auto;max-width:560px;text-align:center;display:flex;flex-direction:column;gap:10px;align-items:center}
.anon-lockpanel h3{font-size:15px}
.anon-lockpanel p{font-size:12px;color:var(--muted);line-height:1.5}

/* inline panels for /vm, /tailscale, /enabler */
.info-panel{display:block}
.ip-head{font-weight:700;font-size:12px;margin-bottom:6px}
.ip-row{font-size:12px;padding:3px 0;border-bottom:1px solid var(--border)}
.ip-row:last-child{border-bottom:0}
.ip-note{font-size:11px;color:var(--muted);line-height:1.45;margin-top:6px}
.ip-warn{font-size:12px;color:var(--amber);margin-bottom:4px}
.info-panel code{font-family:var(--mono);font-size:10px}

/* the header is the containing block for the ctx / anon popovers */
.chat-header{position:relative}

/* §F13 terminal view. Sibling of .chat-scroll so switching is a class flip rather than a re-render,
   which keeps the transcript's scroll position and any in-flight stream intact underneath. */
.chat-term { display: flex; flex-direction: column; min-height: 0; flex: 1; }
.term-bar {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  padding: 8px 12px; border-bottom: 1px solid var(--border); background: var(--surface);
}
.term-bar select { font-size: 12.5px; }
.term-note { font-size: 12px; }
/* xterm measures its own cell grid, so the host needs a real height to fit against. */
.term-host { flex: 1; min-height: 0; padding: 8px 10px; background: var(--bg-elev, #11131a); overflow: hidden; }
.term-host .xterm { height: 100%; }
.btn.active { border-color: color-mix(in srgb, var(--accent) 55%, transparent); color: var(--accent); }

/* ============================================================
   §L mobile-first pass. The existing 767px rules already collapse the sidebar; this targets the
   spec's real bar — "chat list and dashboard readable at 380px", tap targets >= 44px, a composer
   that survives the on-screen keyboard, and a terminal usable on a phone.
   ============================================================ */

/* The keyboard is the whole problem on mobile: 100vh does not shrink when it opens, so a composer
   pinned to the viewport ends up underneath it. dvh tracks the visible viewport instead. */
@supports (height: 100dvh) {
  @media (max-width: 767px) {
    .app, .chat-view { height: 100dvh; }
  }
}

@media (max-width: 767px) {
  /* iOS zooms the page when a focused input is under 16px. Not a preference — a layout bug. */
  .composer textarea, input[type="text"], input[type="password"], input[type="number"], select, textarea { font-size: 16px; }

  /* >= 44px targets, per §L. Applied to the controls a thumb actually hits. */
  .btn, .nav-item, .chip, .hamburger, .cmd-btn, .term-bar select, .term-restart { min-height: 44px; }
  .btn.ghost, .btn { padding-inline: 14px; }
  .chat-list li a { min-height: 44px; display: flex; align-items: center; }

  /* Sticky composer: it rides above the keyboard rather than being scrolled away with the thread. */
  .composer { position: sticky; bottom: 0; z-index: 20; padding-bottom: max(10px, env(safe-area-inset-bottom)); }
  .chat-scroll { -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }

  /* The terminal has to be usable, not merely present: a legible cell and no horizontal page scroll. */
  .chat-term { min-height: 0; }
  .term-host { padding: 4px 6px; }
  .term-bar { padding: 6px 8px; gap: 6px; }
  .term-bar .term-note { flex-basis: 100%; }
}

/* 380px is the stated floor. Below ~420px the header is the thing that breaks first — it holds a
   title input, two selects and six buttons — so it wraps instead of overflowing. */
@media (max-width: 420px) {
  .chat-header { flex-wrap: wrap; row-gap: 6px; padding: 8px 10px 8px 56px; }
  .chat-header .chat-title { flex: 1 1 100%; min-width: 0; }
  .chat-header .spacer { display: none; }
  .chat-header .btn { padding-inline: 10px; font-size: 12.5px; }
  .engine-sel, .model-btn { max-width: 44vw; overflow: hidden; text-overflow: ellipsis; }

  .page { padding: 16px 12px 24px; padding-top: 60px; }
  .card { padding: 14px; }
  /* Stat rows and model grids go single-column rather than squeezing two unreadable columns. */
  .model-grid, .dash-cols, .stat-row, .skill-grid { grid-template-columns: 1fr !important; }
  .msg-user .msg-body { max-width: 100%; }
  .msg-body { font-size: 15px; }
  table { display: block; overflow-x: auto; }   /* tier/pricing tables scroll themselves, not the page */
}

/* Nothing may make the page itself scroll sideways — that is the tell that a mobile layout is broken. */
@media (max-width: 767px) {
  html, body { overflow-x: hidden; }
  pre, code { overflow-x: auto; max-width: 100%; }
}

/* §F2 live compaction progress in the chat header. A compaction used to be invisible once queued;
   this is the one place that says what it is doing, how far along it is, and how it ended. */
.cmpt-progress {
  display: inline-flex; align-items: center; gap: 6px; margin-left: 10px;
  padding: 3px 9px; border-radius: 999px; font-size: 12px; font-weight: 500;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--text); border: 1px solid color-mix(in srgb, var(--accent) 34%, transparent);
  white-space: nowrap;
}
.cmpt-progress.good { background: color-mix(in srgb, var(--green) 16%, transparent); border-color: color-mix(in srgb, var(--green) 38%, transparent); }
.cmpt-progress.warn { background: color-mix(in srgb, #d9a032 18%, transparent); border-color: color-mix(in srgb, #d9a032 40%, transparent); }
.cmpt-progress.bad  { background: color-mix(in srgb, var(--red, #e05555) 18%, transparent); border-color: color-mix(in srgb, var(--red, #e05555) 42%, transparent); }

/* Right-click menu on a sidebar chat (pause / delete). Positioned against the pointer in JS. */
/* .menu is an anchored dropdown: `position:absolute; bottom:calc(100% + 8px); left:0`. Reusing it
   for a pointer-positioned menu meant inheriting that `bottom`, and on a FIXED element 100%
   resolves against the viewport - so bottom became viewportHeight+8px and the menu was pushed
   entirely off the top of the screen. It rendered, it just was not anywhere you could see it.
   Reset the anchoring explicitly; JS then sets left/top from the pointer. */
.chat-ctx {
  position: fixed; inset: auto; bottom: auto; right: auto;
  z-index: 300; min-width: 210px; max-width: 280px; padding: 6px;
}
.chat-ctx-title {
  padding: 6px 10px 8px; font-size: 12px; font-weight: 600; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  border-bottom: 1px solid var(--border); margin-bottom: 4px;
}
.chat-ctx .menu-item { width: 100%; text-align: left; }
.chat-ctx .menu-item.danger { color: var(--red, #e05555); }
.chat-ctx-note { padding: 8px 10px 4px; font-size: 11px; color: var(--muted); line-height: 1.4; }
/* A paused chat stays legible but visibly parked — it is not disabled, just not running. */
.chat-item.is-paused { opacity: .62; }
.chat-paused {
  display: inline-block; margin-right: 6px; font-size: 9px; font-weight: 700;
  letter-spacing: .5px; color: var(--muted); vertical-align: middle;
}

/* §J VM view: quota bars. Shown before creation because the server checks count, vCPU, RAM and disk
   separately — refusing after the fact is a worse experience than showing the ceiling up front. */
.vm-quotas { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 14px; }
.vm-quota-head { display: flex; justify-content: space-between; font-size: 12.5px; margin-bottom: 5px; }
.vm-quota-bar { height: 6px; border-radius: 999px; background: var(--surface2); overflow: hidden; }
.vm-quota-bar i { display: block; height: 100%; background: var(--accent); transition: width .2s ease; }
.vm-quota-bar i.full { background: var(--red, #e05555); }
.vm-table code { font-size: 12.5px; }
.vm-actions { display: flex; gap: 6px; justify-content: flex-end; }
.vm-actions .btn.danger { color: var(--red, #e05555); }

/* §F4 the per-chat usage strip. One line under the chat header: tokens, cost, badges, context fill,
   remaining balance. It sits between the header and the governor banner rather than inside the
   header because the header is already a wrap-prone row of controls, and a number that reflows
   somewhere different on every model change is a number nobody trusts. */
.chat-usage {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 5px 12px; font-size: 12px; color: var(--muted);
  border-bottom: 1px solid var(--border); background: var(--surface2);
}
.chat-usage .spacer { flex: 1 1 auto; }
.cu-item { white-space: nowrap; }
.cu-cost { color: var(--text); font-variant-numeric: tabular-nums; }
.cu-bal { color: var(--text); font-variant-numeric: tabular-nums; }
.cu-byok, .cu-imported {
  padding: 1px 7px; border-radius: 999px; font-size: 11px;
  border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--border));
  color: color-mix(in srgb, var(--accent) 75%, var(--text));
}
.cu-imported { border-color: var(--border); color: var(--muted); }
.cu-bar {
  display: inline-block; width: 90px; height: 5px; border-radius: 999px;
  background: color-mix(in srgb, var(--text) 12%, transparent); overflow: hidden;
}
.cu-bar > i { display: block; height: 100%; background: var(--accent); transition: width .25s ease; }
/* The per-message cost lives in a title, so mark which rows actually have one — otherwise the only
   way to discover the hover is to try every message. */
.msg-meta.has-cost { cursor: help; text-decoration: underline dotted color-mix(in srgb, var(--text) 25%, transparent); text-underline-offset: 3px; }

/* §F7 the ultracode/swarm confirmation gate. It exists to make a spending decision reviewable, so
   the money is the largest thing on it and the plan is legible rather than a wall of prose. */
.uc-gate h3 { font-size: 16px; font-weight: 600; margin-bottom: 12px; }
.uc-gate-cost { display: flex; align-items: baseline; gap: 10px; margin-bottom: 12px; }
.uc-gate-cost b { font-size: 22px; font-variant-numeric: tabular-nums; }
.uc-gate-warn {
  padding: 8px 10px; border-radius: 8px; font-size: 12.5px; margin-bottom: 12px;
  color: var(--amber); border: 1px solid color-mix(in srgb, var(--amber) 40%, var(--border));
  background: color-mix(in srgb, var(--amber) 8%, transparent);
}
.uc-gate-label { font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin-bottom: 6px; }
.uc-gate-plan ol { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 8px; }
.uc-gate-plan li { font-size: 13.5px; }
.uc-gate-models { font-size: 12px; margin-top: 3px; line-height: 1.5; }
.uc-gate-role { opacity: .7; }
.uc-gate-why { font-size: 11.5px; opacity: .8; font-style: italic; }
.uc-gate-workers { font-size: 12.5px; margin-top: 12px; }
.uc-gate-note { font-size: 11.5px; margin-top: 10px; line-height: 1.5; }
/* The per-node model label in the live run view. */
.uc-node-model { font-size: 11px; }

/* §F6 the durable /goal run view. It reports a server-side job, so it has to say what the job is
   doing rather than what this tab is doing. */
.goal-box { border-left: 3px solid var(--accent); }
.goal-head { display: flex; align-items: center; gap: 8px; font-weight: 600; }
.goal-state { font-weight: 400; color: var(--muted); }
.goal-head .btn { margin-left: auto; }
.goal-check { font-size: 12px; margin: 4px 0 6px; }
.goal-iter { display: flex; gap: 8px; align-items: baseline; font-size: 12.5px; padding: 3px 0; border-top: 1px solid var(--border); }
.goal-n { flex: 0 0 auto; color: var(--muted); font-variant-numeric: tabular-nums; }
.goal-text { flex: 1 1 auto; overflow-wrap: anywhere; }
.goal-verdict { font-size: 11.5px; margin-top: 2px; }
/* The guardrail report is composed by the worker and shown verbatim — it is the one thing the user
   needs to read in full, so it does not get truncated or reflowed. */
.goal-report {
  margin-top: 10px; padding: 10px; border-radius: 8px; font-size: 12px; line-height: 1.6;
  white-space: pre-wrap; background: var(--surface2); border: 1px solid var(--border);
}
.goal-done .goal-state { color: var(--green); }
.goal-paused .goal-state { color: var(--amber); }
.goal-err .goal-state { color: var(--red); }

/* §A3 the plan cards. The point of the layout is comparison, so every card shows the same rows in
   the same order and the current plan is marked rather than reordered. */
.plan-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; margin-top: 12px; }
.plan {
  border: 1px solid var(--border); border-radius: var(--radius); padding: 14px;
  display: flex; flex-direction: column; gap: 8px; background: var(--surface2);
}
.plan-now { border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); }
.plan-name { font-weight: 600; display: flex; align-items: center; gap: 6px; }
.plan-price { font-size: 20px; font-variant-numeric: tabular-nums; }
.plan-price .muted { font-size: 12px; }
.plan-specs { list-style: none; margin: 0; padding: 0; font-size: 12.5px; color: var(--muted); display: flex; flex-direction: column; gap: 3px; }
.plan .btn { margin-top: auto; }
.plan-note { font-size: 11.5px; }
.plan-sub { margin-top: 14px; font-size: 13px; display: flex; flex-direction: column; gap: 6px; }
.plan-sub .btn { align-self: flex-start; }

/* §C2 the chat container picker. A checklist rather than a dropdown, because a chat may use several
   containers at once and the access level for each has to be visible while choosing. */
.container-pick.has-containers { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); }
.ctp { max-width: 560px; }
.ctp h3 { font-size: 16px; font-weight: 600; margin-bottom: 6px; }
.ctp-list { display: flex; flex-direction: column; gap: 2px; margin: 12px 0; max-height: 42vh; overflow-y: auto; }
.ctp-row {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 8px;
  font-size: 13px; cursor: pointer; border: 1px solid transparent;
}
.ctp-row:hover { background: var(--surface2); }
.ctp-row input { cursor: pointer; }
.ctp-name { font-weight: 500; }
.ctp-spec { margin-left: auto; font-size: 11.5px; white-space: nowrap; }
/* Read-only is the state most likely to be misread as "broken", so it is marked rather than implied. */
.ctp-ro { border-color: color-mix(in srgb, var(--amber) 30%, transparent); }
.ctp-ro .ctp-name::after { content: " (read only)"; color: var(--amber); font-weight: 400; font-size: 11.5px; }
.ctp-note { font-size: 11.5px; line-height: 1.5; }

/* §H Lawyer view. The disclaimer is styled to be read, not to be dismissed: it sits above the input,
   in amber rather than red, because it is a standing condition of the feature and not an error. */
.legal-warn {
  display: flex; gap: 10px; flex-wrap: wrap; align-items: baseline;
  margin: 0 0 16px; padding: 12px 14px; border-radius: var(--radius);
  border: 1px solid color-mix(in srgb, var(--amber) 45%, transparent);
  background: color-mix(in srgb, var(--amber) 9%, transparent);
  font-size: 13px; line-height: 1.55;
}
.legal-warn b { color: var(--amber); white-space: nowrap; }

.legal-jur { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 12px; }
.legal-jur .spacer { flex: 1 1 auto; }
.legal-jur .btn .muted { margin-left: 6px; font-size: 11.5px; }

.legal-page textarea {
  width: 100%; resize: vertical; font: inherit; font-size: 14px; line-height: 1.5;
  padding: 10px 12px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text);
}
.legal-page textarea:disabled { opacity: .55; cursor: not-allowed; }
.legal-actions { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-top: 10px; }

.legal-body { font-size: 14.5px; line-height: 1.65; }
/* A citation is the load-bearing part of an answer here, so it reads as a link and lands on the
   source it names rather than being decorative superscript. */
.legal-cite {
  color: var(--accent); text-decoration: none; font-weight: 600;
  padding: 0 1px; scroll-margin-top: 20px;
}
.legal-cite:hover { text-decoration: underline; }
.legal-srcs { margin: 8px 0 0; padding-left: 22px; font-size: 13px; line-height: 1.6; }
.legal-srcs li { margin-bottom: 6px; }
.legal-srcs li:target { background: color-mix(in srgb, var(--accent) 14%, transparent); border-radius: 6px; }
.legal-srcs a { color: var(--text); }
.legal-srcs .muted { display: block; font-size: 11.5px; word-break: break-all; }

/* Container resource fields. Narrow on purpose: a core count is one or two digits, and a full-width
   input next to a name field reads as though it wants a sentence. */
.ct-num { flex: 0 0 auto; width: 8.5rem; }
.ct-num input { text-align: right; font-variant-numeric: tabular-nums; }
.ct-note-inline { display: inline-block; margin-left: 10px; font-size: 12.5px; }
/* The OS picker sits in the same flex row as the name. A <select> is as wide as its longest option
   and "Linux — Ubuntu 24.04 (ZeroLog toolchain)" is long, so without min-width:0 it wins the row and
   squeezes the name field to nothing. */
.ct-os-field { flex: 1 1 13rem; min-width: 0; }
.ct-os { width: 100%; }

/* Sidebar groups. Account and Administration are real destinations that are simply not daily, so
   they collapse rather than competing with Dashboard and Containers for the first thing you see. */
.side-group { margin-top: 6px; }
.side-group > summary {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 10px; margin: 0; border-radius: 8px;
  font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--muted); cursor: pointer; list-style: none; user-select: none;
}
.side-group > summary::-webkit-details-marker { display: none; }
/* A caret that turns, so "there is more behind this" is visible before you click it. */
.side-group > summary::after {
  content: ''; margin-left: auto; width: 6px; height: 6px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg); transition: transform .18s ease;
}
.side-group[open] > summary::after { transform: rotate(45deg); }
.side-group > summary:hover { color: var(--text); background: var(--surface2); }
.side-group > a { padding-left: 26px; }

@media (prefers-reduced-motion: reduce) {
  .side-group > summary::after { transition: none; }
}

/* ============================================================================
   Form controls
   ----------------------------------------------------------------------------
   Nothing here styled a bare <select>, so every dropdown in the app — effort
   level, sandbox mode, the model tier filters — rendered as the browser's own
   widget: a light-grey box with a system caret, sitting in a dark UI. That is
   the "outdated" look, and it was one missing selector rather than a theme.

   Everything below matches the existing .input treatment (same radius, border,
   focus ring) so controls read as one family instead of per-widget defaults.
   ========================================================================== */

select {
  appearance: none; -webkit-appearance: none;
  padding: 8px 30px 8px 11px;
  border-radius: 9px;
  border: 1px solid var(--border);
  background-color: var(--bg);
  color: var(--text);
  font: inherit; font-size: 13.5px; line-height: 1.3;
  cursor: pointer;
  /* Caret drawn as a background SVG: no extra element, and it inherits the theme. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5L6 8l3.5-3.5' fill='none' stroke='%238b94a7' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 9px center;
  background-size: 12px;
  transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
}
select:hover { border-color: #2e3745; }
select:focus-visible {
  outline: none;
  border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent);
}
select:disabled { opacity: .5; cursor: not-allowed; }
/* The dropdown list itself is drawn by the OS; naming the colours is the only
   influence available, and without it the open list stays light-on-light. */
select option { background: var(--surface); color: var(--text); }

/* Checkboxes and radios: accent-color is the one-line way to theme the native
   control while keeping its built-in keyboard and screen-reader behaviour. */
input[type="checkbox"], input[type="radio"] {
  accent-color: var(--accent);
  width: 15px; height: 15px;
  cursor: pointer;
}
input[type="checkbox"]:focus-visible, input[type="radio"]:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--accent) 70%, transparent);
  outline-offset: 2px;
}

/* Number inputs: the spinners are tiny, mis-themed and easy to hit by accident
   on a core count. Removed in favour of typing, with tabular figures so digits
   do not shift width as they change. */
input[type="number"] { font-variant-numeric: tabular-nums; }
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
input[type="number"] { -moz-appearance: textfield; }

/* Range inputs, themed rather than left as the platform default. */
input[type="range"] {
  appearance: none; -webkit-appearance: none;
  width: 100%; height: 4px; border-radius: 999px;
  background: var(--border); cursor: pointer;
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; width: 15px; height: 15px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--bg);
  box-shadow: 0 0 0 1px var(--border);
}
input[type="range"]::-moz-range-thumb {
  width: 15px; height: 15px; border-radius: 50%; border: 2px solid var(--bg);
  background: var(--accent); box-shadow: 0 0 0 1px var(--border);
}

/* The compact controls in the chat composer share the family, one size down. */
.ctl select, .ctl input { font-size: 12.5px; }
.temp-in, .reason-sel { padding: 5px 9px; }
.reason-sel { padding-right: 26px; background-position: right 7px center; }

/* A keyboard focus ring on everything interactive. Several controls had none,
   which makes the app unusable without a mouse rather than merely plain. */
a:focus-visible, button:focus-visible, summary:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--accent) 70%, transparent);
  outline-offset: 2px;
  border-radius: 6px;
}

@media (prefers-reduced-motion: reduce) {
  select { transition: none; }
}


/* ---------- the thinking indicator (js/reasoning-anim.js) ----------------------
   A reasoning model shows nothing for ten to ninety seconds; without a sign of life that reads as a
   hang. Both styles carry the same three facts — alive, how long, how much reasoning has arrived —
   and differ only in voice. Neither ever occupies more than one line: this sits inside the answer,
   and an indicator that pushes the conversation around is worse than none. */
.msg-think { padding: 1px 0 5px; }
.msg-think.hidden { display: none; }
/* While a turn is live, pin the indicator to the bottom of the chat scroller so it stays visible
   no matter how long the message grows (it used to scroll off the top). Sticky keeps it in normal
   flow — below the streaming text — but glued to the viewport's bottom edge; the small backdrop stops
   text bleeding through the semi-transparent gap. */
.msg.streaming .msg-think:not(.hidden) {
  position: sticky;
  bottom: 0;
  z-index: 3;
  background: linear-gradient(to top, var(--bg, #0b0d10) 55%, transparent);
  padding-top: 6px;
}

.rzn {
  display: inline-flex; align-items: center; gap: 9px; max-width: 100%;
  font: 12.5px/1.5 var(--mono); color: var(--muted);
}
.rzn-meta { font-variant-numeric: tabular-nums; opacity: .75; white-space: nowrap; }

/* -- the Claude Code hand: rotating glyph, a gerund that changes, (elapsed - tokens - esc) -- */
.rzn-glyph { display: inline-block; width: 1.1em; text-align: center; color: var(--accent); }
.rzn-claude .rzn-word {
  background: linear-gradient(90deg, var(--muted) 0 38%, var(--text) 50%, var(--muted) 62% 100%);
  background-size: 280% 100%;
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
  animation: rzn-shimmer 2.8s linear infinite;
}
@keyframes rzn-shimmer { from { background-position: 175% 0; } to { background-position: -75% 0; } }

/* -- ours: a ring that turns, and the real reasoning shredding behind itself -- */
.rzn-orb {
  width: 13px; height: 13px; flex: none; border-radius: 50%;
  background: conic-gradient(from calc(var(--rzn-spin, 0) * 360deg),
              transparent 0 52%, color-mix(in srgb, var(--accent) 45%, transparent) 72%, var(--accent) 96%, transparent 100%);
  -webkit-mask: radial-gradient(circle, transparent 0 52%, #000 56%);
  mask: radial-gradient(circle, transparent 0 52%, #000 56%);
}
.rzn-zerolog .rzn-label { color: var(--text); }
.rzn-tape {
  white-space: pre; overflow: hidden; min-width: 34ch; max-width: 34ch;
  color: color-mix(in srgb, var(--accent) 50%, var(--muted));
  -webkit-mask-image: linear-gradient(to right, transparent, #000 9%, #000 100%);
  mask-image: linear-gradient(to right, transparent, #000 9%, #000 100%);
}
/* Reduced motion keeps the facts and drops the movement: elapsed time is the part people need. */
.rzn.is-still .rzn-tape, .rzn.is-still .rzn-orb { display: none; }
.rzn.is-still .rzn-word { animation: none; -webkit-text-fill-color: currentColor; color: var(--muted); }
/* On a phone the tape would eat the line the answer needs. The facts stay. */
@media (max-width: 760px) { .rzn-tape { display: none; } }

/* the live preview beside the picker in Settings */
.set-rzn-preview { margin-top: 10px; min-height: 22px; }

/* ---------- §C2 Verbindungen zwischen Containern ---------- */
.ct-links { margin: 10px 0 14px; }
.ct-link-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px 12px; border: 1px solid var(--border); border-radius: 9px;
  background: var(--surface2);
}
.ct-link-row + .ct-link-row { margin-top: 7px; }
.ct-link-row .badge { font-family: var(--mono); font-size: 11.5px; }
/* §AI-sandbox surfacing: a chat's sandbox shown in the Containers view, badged so it never reads as a
   managed workspace. Colour hints for the AI badge + running/stopped state; a soft accent rail marks
   the whole row as the AI kind. */
.badge.ct-ai { background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent); }
.badge.ct-running { background: color-mix(in srgb, #22c55e 22%, transparent); color: #22c55e; }
.badge.ct-stopped { background: color-mix(in srgb, #9aa4b2 20%, transparent); color: #9aa4b2; }
/* The container/sandbox rows were never given layout rules at all, so on a narrow screen a row of
   seven buttons plus the spec line ("6 vCPU · 15 GB · 40 GB · Linux — Ubuntu 24.04 … on gl-mh-w1")
   ran off the side and the whole page scrolled sideways. They WRAP now, and the long spec string is
   allowed to break, so nothing forces the page wider than the screen. min-width:0 is what actually
   lets a flex child shrink below its content width — without it wrapping alone does not help. */
.ct-row { min-width: 0; }
.ct-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; min-width: 0; }
.ct-head > .muted { min-width: 0; overflow-wrap: anywhere; }
.ct-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
/* A file/port panel inside a row must scroll ITSELF rather than widening the page. */
.ct-files, .ct-ports, .ct-snaps, .ct-dbs, .ct-share, .sb-files { min-width: 0; overflow-x: auto; }
.ct-row.ct-sandbox { border-left: 2px solid color-mix(in srgb, var(--accent) 45%, transparent); }
.sb-files { margin-top: 8px; }
.ct-link-row .btn { margin-left: auto; }
.ct-link-new { align-items: flex-end; }
.ct-link-new .btn { margin-bottom: 1px; }


/* §F15 the two delegation switches on the settings page. Each hint is indented to its own label,
   because a price that looks like it belongs to the switch below it is a price nobody agreed to. */
.set-deleg-body .check { padding: 2px 0; }
.set-deleg-body .hint { margin: 2px 0 12px 22px; }
.set-deleg-body .check input:disabled + span { opacity: .55; }

/* §F15 delegated-call provenance + generated images inside a tool block. Deliberately quiet: this
   is a fact about the answer above it, not a second answer. */
.tool-deleg { font-size: 12px; opacity: .75; margin: 2px 0 6px; }
.tool-deleg b { font-weight: 600; opacity: 1; }
.tool-media { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 8px; }
.tool-img { max-width: 260px; max-height: 260px; border-radius: 8px; display: block; }
/* §F10 a generated picture is a file the person wanted, so the download sits with the picture and
   not in a menu somewhere else. max-width in % so a phone shows the whole image, not a crop. */
.tool-fig { margin: 0; max-width: 100%; }
.tool-fig .tool-img { max-width: min(260px, 100%); }
.tool-fig figcaption { font-size: 11px; opacity: .7; margin-top: 2px; }
.img-choose { display: flex; flex-direction: column; gap: 4px; font-size: 12px; }
.img-choice { line-height: 1.4; }

/* §F10 Bilder-Ansicht. auto-fill + minmax statt fester Spalten: auf dem Handy wird daraus von selbst
   eine Spalte, ohne einen zweiten Satz Regeln in der Media Query. */
.gal-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; margin-top: 12px; }
.gal-item { margin: 0; background: var(--panel, #16161c); border-radius: 10px; overflow: hidden; }
.gal-item img { width: 100%; height: 180px; object-fit: cover; display: block; }
.gal-item figcaption { padding: 8px; font-size: 12px; }
.gal-prompt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gal-meta { font-size: 11px; margin-top: 2px; }
.gal-none { height: 180px; display: grid; place-items: center; font-size: 12px; opacity: .6; }

/* §F3 Handy: Kopfzeile und Eingabeleiste zusammenklappen.
   Gemessen bei 390x844 vor dieser Änderung: Kopfzeile 460 px, Eingabebereich 330 px, für die
   Unterhaltung selbst blieben ~250 px von 844. Deshalb ist hier nicht "kleiner" die Antwort, sondern
   "später": Titel und Eingabefeld bleiben, alles andere kommt auf Tippen.
   Die beiden Knöpfe existieren am Schreibtisch nicht — display:none, kein zweiter Bedienweg. */
.hdr-more, .cmp-more { display: none; }
@media (max-width: 767px) {
  .hdr-more, .cmp-more { display: inline-flex; flex: none; }
  .chat-header { flex-wrap: wrap; row-gap: 6px; align-items: center; }
  /* Ohne min-width:0 beansprucht das Titelfeld seine Wunschbreite, der Knopf rutscht in eine zweite
     Zeile und die eingesparte Höhe ist zur Hälfte wieder weg. */
  .chat-header > .chat-title { flex: 1 1 auto; min-width: 0; }
  .chat-header > *:not(.chat-title):not(.hdr-more) { display: none; }
  .chat-header.open > *:not(.hidden) { display: inline-flex; }
  /* .hidden ist eine Aussage über den Zustand des Chats (kein Container, kein ctx), nicht über die
     Bildschirmbreite — aufgeklappt darf sie nicht plötzlich sichtbar werden. */
  .chat-header.open > .hidden { display: none !important; }
  .chat-header.open > .spacer { display: none; }
  .composer .controls-row, .composer .chips-row { display: none; }
  .composer.open .controls-row { display: flex; flex-wrap: wrap; gap: 8px; }
  .composer.open .chips-row { display: flex; flex-wrap: wrap; gap: 6px; }
  .chat-usage { font-size: 11px; white-space: nowrap; overflow-x: auto; }
  .input-row { align-items: flex-end; gap: 6px; }
  /* Fingergrösse, nicht Mauszeigergrösse. 40 px ist die kleinste Fläche, die auf einem Handy
     verlässlich getroffen wird, und der Sendeknopf ist der, den man am häufigsten trifft. */
  .send-btn, .hdr-more, .cmp-more { min-height: 40px; }
}

/* §F14 Mehr Bewegung im Chat — aber nur dort, wo sie etwas AUSSAGT.
   Der Chat hatte genau eine Regung: .msg mit `rise .2s`. Was hier dazukommt, beantwortet jeweils eine
   Frage, die man sonst durch Hinsehen und Warten beantworten muss: Ist etwas Neues gekommen? Denkt es
   noch? Ist das Bild schon da? Wurde mein Klick angenommen?
   Die prefers-reduced-motion-Regel steht am Ende dieses Blocks und ist NEU: die beiden vorhandenen
   Regeln dieser Art in der Datei betreffen nur ein Summary-Dreieck und ein select, also hätte ein
   Verweis darauf nichts abgeschaltet. Wer Bewegung abbestellt hat, bekommt hier keine. */
.msg-user { animation: msgInRight .26s cubic-bezier(.2, .8, .2, 1); }
.msg-assistant { animation: msgInLeft .26s cubic-bezier(.2, .8, .2, 1); }
@keyframes msgInRight { from { opacity: 0; transform: translate3d(14px, 6px, 0); } }
@keyframes msgInLeft { from { opacity: 0; transform: translate3d(-10px, 6px, 0); } }

/* Der Denkblock pulsiert, solange er läuft — eine Antwort, die noch nicht kommt, sieht damit anders
   aus als eine, die hängt. */
.msg.streaming .msg-think:not(.hidden) { animation: thinkPulse 1.6s ease-in-out infinite; }
@keyframes thinkPulse { 50% { opacity: .55; } }

/* Ein Werkzeugblock, der aufklappt, soll nicht springen. */
.tool-block[open] > *:not(summary) { animation: rise .18s ease; }

/* Erzeugte und präsentierte Bilder blenden auf, statt beim Laden ins Layout zu knallen. */
.tool-img, .gal-item img { animation: imgIn .32s ease; }
@keyframes imgIn { from { opacity: 0; transform: scale(.985); } }

/* Rückmeldung auf den Klick: der Senden-Knopf gibt kurz nach. Auf dem Handy ist das oft die einzige
   Bestätigung, dass der Druck angekommen ist. */
.send-btn:active:not(:disabled) { transform: scale(.96); }
.send-btn { transition: transform .08s ease, background-color .15s ease; }

/* Neue Karten in Galerie und Aufgabenliste kommen gestaffelt, damit man sieht, dass es mehrere sind. */
.gal-item { animation: rise .22s ease both; }
.gal-item:nth-child(2) { animation-delay: .03s; }
.gal-item:nth-child(3) { animation-delay: .06s; }
.gal-item:nth-child(4) { animation-delay: .09s; }
.gal-item:nth-child(n+5) { animation-delay: .12s; }

@media (prefers-reduced-motion: reduce) {
  .msg-user, .msg-assistant, .tool-block[open] > *:not(summary),
  .tool-img, .gal-item, .gal-item img { animation: none; }
  .msg.streaming .msg-think:not(.hidden) { animation: none; opacity: 1; }
  .send-btn { transition: none; }
  .send-btn:active:not(:disabled) { transform: none; }
}

/* §F12 Ports und §F5 Dateien in der Containerzeile */
.ct-ports-box, .ct-files-box { display: flex; flex-direction: column; gap: 6px; padding: 8px 0; font-size: 13px; }
.ct-port-row, .ct-file { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.ct-file span:first-child { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ct-port-new { display: flex; gap: 8px; flex-wrap: wrap; }
.ct-port-new .input { max-width: 220px; }
.ct-files-box { max-height: 320px; overflow-y: auto; }

/* §F5 präsentierte Datei */
.pres-card { border: 1px solid var(--border, #2a2740); border-radius: 10px; padding: 10px; display: flex; flex-direction: column; gap: 6px; max-width: 100%; }
.pres-head { font-size: 13px; }
.pres-note { font-size: 12px; opacity: .85; }
.pres-prev { margin: 0; max-height: 180px; overflow: auto; font-size: 11.5px; background: var(--surface2, #12121a); padding: 8px; border-radius: 6px; }

/* §F9 geplante Aufgaben */
.tk-item { margin-bottom: 10px; }
.tk-task { font-size: 12px; opacity: .8; margin-top: 6px; white-space: pre-wrap; }
.row-between { display: flex; justify-content: space-between; align-items: center; gap: 8px; flex-wrap: wrap; }
.row-gap { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

/* ============================================================================
   VISUAL REFRESH — skin only, never structure.
   Appended last so it wins the cascade without editing fifty rules in place.
   STRICTLY colour, border, radius, shadow, typography and transition: not one
   display / position / flex / grid / width / height / padding / margin here, so
   every measurement the layout work above settled stays exactly where it was.
   ============================================================================ */

/* Ambient depth. A single fixed wash keeps the page from reading as flat black; it sits at the very
   bottom of the stack and is skipped when a §Design background layer is showing. */
body::before {
  content: ''; position: fixed; inset: 0; z-index: -2; pointer-events: none;
  background:
    radial-gradient(1200px 600px at 12% -8%, color-mix(in srgb, var(--accent) 11%, transparent), transparent 60%),
    radial-gradient(900px 500px at 108% 8%, rgba(64, 132, 214, .07), transparent 62%);
}
body.has-bg::before { opacity: 0; }

/* ---------- controls ---------- */
.btn {
  background: linear-gradient(180deg, var(--surface3), var(--surface2));
  border-color: var(--border);
  box-shadow: var(--shadow-1), inset 0 1px 0 var(--border-soft);
  transition: background .16s ease, border-color .16s ease, box-shadow .16s ease,
              color .16s ease, transform .16s ease;
}
.btn:hover { border-color: color-mix(in srgb, var(--accent) 34%, var(--border)); box-shadow: var(--shadow-2), inset 0 1px 0 var(--border-soft); }
.btn:active { transform: translateY(1px); box-shadow: var(--shadow-1); }
.btn:focus-visible { box-shadow: var(--ring), var(--shadow-1); outline: none; }
.btn[disabled], .btn:disabled { box-shadow: none; filter: grayscale(.35); }

/* The one filled action per screen. A gradient plus a lit top edge reads as a raised key, which is
   what separates it from the six neutral buttons that usually sit beside it. */
.btn-primary, .btn.primary {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent) 88%, #fff),
    color-mix(in srgb, var(--accent) 96%, #000));
  border-color: transparent;
  box-shadow: var(--glow), inset 0 1px 0 rgba(255, 255, 255, .22);
}
.btn-primary:hover, .btn.primary:hover {
  filter: brightness(1.06);
  box-shadow: var(--glow), 0 6px 22px -6px color-mix(in srgb, var(--accent) 55%, transparent), inset 0 1px 0 rgba(255, 255, 255, .25);
}
.btn.ghost { background: transparent; box-shadow: none; }
.btn.ghost:hover { background: var(--surface2); box-shadow: var(--shadow-1); }
.btn.danger, .btn-danger {
  color: var(--red);
  border-color: color-mix(in srgb, var(--red) 38%, transparent);
  background: linear-gradient(180deg, color-mix(in srgb, var(--red) 14%, var(--surface2)), color-mix(in srgb, var(--red) 8%, var(--surface2)));
}
.btn.danger:hover, .btn-danger:hover { border-color: color-mix(in srgb, var(--red) 60%, transparent); color: #fff; background: color-mix(in srgb, var(--red) 62%, var(--surface2)); }
.btn.active { box-shadow: var(--ring); background: color-mix(in srgb, var(--accent) 16%, var(--surface2)); }

/* ---------- surfaces ---------- */
.card {
  background: linear-gradient(180deg, color-mix(in srgb, #fff 2.5%, var(--surface)), var(--surface));
  border-color: var(--border);
  box-shadow: var(--shadow-2), inset 0 1px 0 var(--border-soft);
}
.card.stat { background: linear-gradient(165deg, color-mix(in srgb, var(--accent) 7%, var(--surface)), var(--surface) 62%); }
.stat-label { color: var(--muted); }
.stat-value { font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.modal { box-shadow: var(--shadow-3); border-radius: var(--radius); }
.toast { box-shadow: var(--shadow-3); border-radius: var(--radius-sm); }

/* ---------- inputs ---------- */
.input, textarea.input, input.chat-title, select {
  background: color-mix(in srgb, #000 22%, var(--surface2));
  border-color: var(--border);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .35);
  transition: border-color .16s ease, box-shadow .16s ease, background .16s ease;
}
.input:focus, textarea.input:focus, select:focus, input.chat-title:focus {
  border-color: color-mix(in srgb, var(--accent) 62%, transparent);
  box-shadow: var(--ring), inset 0 1px 2px rgba(0, 0, 0, .3);
  outline: none;
}

/* ---------- badges ---------- */
.badge, .badge-free {
  border-radius: 6px;               /* a pill on 10px uppercase text reads as a toy; a chip reads as data */
  letter-spacing: .05em;
  box-shadow: inset 0 1px 0 var(--border-soft);
}

/* ---------- navigation ---------- */
.sidebar { background: linear-gradient(180deg, color-mix(in srgb, #fff 2%, var(--surface)), var(--surface) 45%); }
.side-nav a { border-radius: var(--radius-sm); transition: background .15s ease, color .15s ease, box-shadow .15s ease; }
/* The active row gets a lit rail on its leading edge — position is untouched, the rail is drawn with
   an inset shadow so nothing shifts by a pixel. */
.side-nav a.active {
  box-shadow: inset 2px 0 0 var(--accent), inset 0 1px 0 var(--border-soft);
  background: color-mix(in srgb, var(--accent) 13%, transparent);
}
.chat-item { border-radius: var(--radius-sm); transition: background .15s ease, color .15s ease, box-shadow .15s ease; }
.chat-item.active { box-shadow: inset 2px 0 0 var(--accent); }

/* ---------- chat ---------- */
.msg-user .msg-body {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent) 22%, var(--surface)),
    color-mix(in srgb, var(--accent) 13%, var(--surface)));
  border-color: color-mix(in srgb, var(--accent) 34%, var(--border));
  box-shadow: var(--shadow-1), inset 0 1px 0 rgba(255, 255, 255, .06);
}
.md pre {
  background: color-mix(in srgb, #000 34%, var(--surface));
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: inset 0 1px 0 var(--border-soft);
}
.md code { border-radius: 5px; background: color-mix(in srgb, var(--accent) 12%, var(--surface2)); }
.md pre code { background: transparent; }
.chat-header { background: color-mix(in srgb, #fff 1.5%, var(--surface)); box-shadow: var(--shadow-1); }

/* ---------- tables ---------- */
.table th { color: var(--muted); letter-spacing: .04em; text-transform: uppercase; font-size: 11.5px; }
.table tbody tr { transition: background .14s ease; }
.table tbody tr:hover { background: color-mix(in srgb, #fff 3%, transparent); }

/* ---------- scrollbars ---------- */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb {
  background: var(--surface3); border-radius: 999px; border: 2px solid transparent; background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--accent) 42%, var(--surface3)); background-clip: padding-box; }
* { scrollbar-color: var(--surface3) transparent; scrollbar-width: thin; }

/* Headings sit tighter than body copy at every size — the single change that most makes a dark UI
   look designed rather than defaulted. */
h1, h2, h3, .page-head h1, .card-head h2 { letter-spacing: -.015em; }

/* ============================================================================
   INDUSTRY LAYER — the delivered design's voice, applied over the refresh above.
   Structure is drawn, not filled: hairline frames, square corners, condensed
   headings, monospace for anything measured. Layout is untouched.
   ============================================================================ */

/* Condensed headings are the single loudest signal of this system. Body copy stays Barlow. */
h1, h2, h3, h4, .page-head h1, .card-head h2, .stat-value, .side-brand, .chat-title, .modal h2 {
  font-family: var(--font-heading);
  font-weight: 600;
  letter-spacing: .005em;
}
.page-head h1 { font-size: 27px; }
/* Small labels are set as technical captions — condensed, spaced, upper. */
.stat-label, .table th, .card-kicker, .badge, .badge-free {
  font-family: var(--font-heading); text-transform: uppercase; letter-spacing: .08em;
}

/* Anything MEASURED is monospace: money, tokens, ids, hostnames, paths. A number you are asked to
   trust should look machine-set, and columns of them should align. */
.stat-value, .ct-head .muted, .usage-strip, .msg-meta, .badge, .badge-free {
  font-variant-numeric: tabular-nums;
}

/* Square the system off — the refresh layer above rounded things; this system frames them. */
.card, .btn, .input, textarea.input, select, .modal, .md pre, .badge, .badge-free,
.ct-row, .chat-item, .side-nav a, .toast {
  border-radius: var(--radius);
}
.badge, .badge-free { border-radius: var(--radius-sm); }

/* Cards are line drawings, not filled blocks: hairline border, no surface fill beyond the ground. */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-1);
}
.card.stat { background: linear-gradient(180deg, var(--accent-wash), var(--surface) 70%); }

/* Wells — where machine output lives. Deeper than the ground so the eye reads them as recessed. */
.md pre, .term, .cmd-pane { background: var(--deep); border: 1px solid var(--border); }
.input, textarea.input, select { background: var(--deep); }

/* The primary action is the one solid object on the board. */
.btn-primary, .btn.primary {
  background: var(--accent);
  color: #17131f;                 /* ink on lavender, not white — it is a light accent */
  border-color: transparent;
  font-weight: 600;
  box-shadow: var(--glow);
}
.btn-primary:hover, .btn.primary:hover { background: var(--accent-ink); filter: none; }

/* Focus is a drawn ring in the accent, never the browser default. */
:focus-visible { outline: none; box-shadow: var(--ring); }

/* §Blueprint corner marks — the system's registration crosshairs. Applied to a framed element via
   .blueprint; drawn with gradients on ::before/::after so no extra DOM is required and nothing shifts. */
.blueprint { position: relative; }
.blueprint::before, .blueprint::after {
  content: ''; position: absolute; width: 9px; height: 9px; pointer-events: none;
  background:
    linear-gradient(var(--accent-line), var(--accent-line)) center/100% 1px no-repeat,
    linear-gradient(var(--accent-line), var(--accent-line)) center/1px 100% no-repeat;
}
.blueprint::before { top: -5px; left: -5px; }
.blueprint::after  { bottom: -5px; right: -5px; }

/* Motion: one easing, three durations. Everything that moves uses them. */
.btn, .side-nav a, .chat-item, .input, .card, .badge { transition:
  background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease),
  color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease); }

/* A message entering the stream rises rather than appearing — the design's zl-rise. */
@keyframes zl-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.msg-assistant, .msg-user { animation: zl-rise var(--t-mid) var(--ease) both; }
@media (prefers-reduced-motion: reduce) {
  .msg-assistant, .msg-user { animation: none; }
  .btn, .side-nav a, .chat-item, .input, .card, .badge { transition: none; }
}
