
/* ══ TOKENS ═════════════════════════════════════════════════════════════════
   Design plan: an OPERATIONS CONSOLE. This surface is scanned and operated, not
   read, so the craft goes into information design — summary before detail, state
   encoded in form (a severity stripe, a chip, a meter) as well as in number.

   COLOUR — set by the operator: a ROYAL BLUE ground, a CREAM side panel, and a
   very faded neural-network field behind everything. Content sits on near-white
   "paper" cards so the blue never carries body text. Every pair was measured,
   not eyeballed:
     cream #f7f2e6 text on royal #1b3b9b .......... 8.54:1
     white on royal ............................... 9.79:1
     white card vs royal ground (separation) ...... 9.79:1
     ink #14171d on the cream rail ................ 16.07:1
     rail secondary / muted ink ................... 7.05:1 / 3.37:1
   Dark keeps the identity: a deeper royal ground #0b1637 with lifted royal cards
   #1a2555, on which the series palette re-passes all five checks and every status
   step clears 5.4:1 (good 6.78, warning 7.95, serious 5.53, critical 5.40).
   One accent (blue) carries interactivity; the semantic
   good/warning/serious/critical palette is RESERVED for state and is never the
   accent. Every value is from the validated reference palette and was re-run
   against THESE surfaces (#ffffff light, #12161d dark) when the dark ground
   changed — all five checks pass in both modes; every status step clears 3:1 on
   the dark ground (good 5.40, warning 9.88, serious 6.87, critical 3.77).
   The one WARN is aqua at 2.82:1 on white, only ever used beside a text label.

   TYPE. Two roles, both offline-safe: a system grotesque for interface text, and
   a MONOSPACED instrument face for every datum — ids, counts, timestamps, states,
   keys. In a console, mono-for-data is meaningful rather than decorative. No
   webfont is linked: no font files are available on this host, and a CDN link
   would risk a silent fallback.

   Themes are defined at TOKEN level only — components style through the tokens,
   never inside the media query — so the viewer's toggle overrides the OS
   preference in both directions.
   ═════════════════════════════════════════════════════════════════════════ */
:root {
  color-scheme: light;
  --ground:      #1b3b9b;   /* royal blue page field */
  --on-ground:   #f7f2e6;   /* cream — the only text that sits directly on blue */
  --on-ground-2: #c8d2ee;
  --surface:     #ffffff;   /* paper */
  --surface-2:   #f8f9fc;
  --surface-3:   #eff2f8;
  --rail-ink:    #14171d;
  --line:        #e4e7ee;
  --line-2:      #d0d6e2;
  --ink:         #14171d;
  --ink-2:       #4a5260;
  --ink-3:       #7b8496;
  --accent:      #1b4fa8;
  --accent-ink:  #ffffff;
  --accent-soft: #e9eefb;
  --good:        #0ca30c;  --good-soft:     #e9f6e9;
  --warning:     #fab219;  --warning-soft:  #fdf3e0;
  --serious:     #ec835a;  --serious-soft:  #fdeee7;
  --critical:    #d03b3b;  --critical-soft: #fbe9e9;
  --good-ink: #0a6b0a; --warning-ink: #8a5d00; --serious-ink: #a34a20; --critical-ink: #a82c2c;
  --rail:        #f7f2e6;   /* cream side panel */
  --rail-line:   #e2d9c2;
  /* The rail is cream but very slightly SHEER, so the neural field passes beneath
     it and the texture belongs to the whole window rather than to one pane. 94%
     over royal blue still measures as cream (ink #14171d on it: 15.1:1). */
  --rail-veil:   color-mix(in srgb, #f7f2e6 94%, transparent);
  --elev-1: 0 1px 1px rgba(15,19,25,.04), 0 2px 6px rgba(15,19,25,.04);
  --elev-2: 0 2px 4px rgba(15,19,25,.05), 0 12px 28px rgba(15,19,25,.06);
  --r-lg: 14px; --r-md: 10px; --r-sm: 7px;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, "JetBrains Mono", "Cascadia Mono", Menlo, Consolas, monospace;
  --ui: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;
    --ground:#0b1637; --on-ground:#e8e0cb; --on-ground-2:#9fb0d8;
    --surface:#1a2555; --surface-2:#1f2b60; --surface-3:#26336e;
    --line:#2c3a72; --line-2:#3b4a86;
    --ink:#eef1f6; --ink-2:#b9c3d8; --ink-3:#8b96ad;
    --accent:#7fb0ee; --accent-soft:#22315f;
    --good-soft:#0d2410; --warning-soft:#2a2008; --serious-soft:#2c1a12; --critical-soft:#2c1315;
    --good-ink:#4cc94c; --warning-ink:#fab219; --serious-ink:#ec835a; --critical-ink:#f07a7a;
    --rail:#ded6c0; --rail-line:#c6bda4; --rail-ink:#14171d;
    --rail-veil: color-mix(in srgb, #ded6c0 92%, transparent);
    --elev-1: 0 1px 1px rgba(0,0,0,.4); --elev-2: 0 2px 6px rgba(0,0,0,.45), 0 16px 32px rgba(0,0,0,.35);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --ground:#0b1637; --on-ground:#e8e0cb; --on-ground-2:#9fb0d8;
  --surface:#1a2555; --surface-2:#1f2b60; --surface-3:#26336e;
  --line:#2c3a72; --line-2:#3b4a86;
  --ink:#eef1f6; --ink-2:#b9c3d8; --ink-3:#8b96ad;
  --accent:#7fb0ee; --accent-soft:#22315f;
  --good-soft:#0d2410; --warning-soft:#2a2008; --serious-soft:#2c1a12; --critical-soft:#2c1315;
  --good-ink:#4cc94c; --warning-ink:#fab219; --serious-ink:#ec835a; --critical-ink:#f07a7a;
  --rail:#ded6c0; --rail-line:#c6bda4; --rail-ink:#14171d;
  --rail-veil: color-mix(in srgb, #ded6c0 92%, transparent);
  --elev-1: 0 1px 1px rgba(0,0,0,.4); --elev-2: 0 2px 6px rgba(0,0,0,.45), 0 16px 32px rgba(0,0,0,.35);
}

*,*::before,*::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
/* The neural field: a canvas pinned behind all content at very low alpha, so it
   reads as a texture in the blue rather than a picture competing with the data.
   Canvas rather than hand-authored SVG path data, and pointer-events:none so it
   can never intercept a click. */
#neural {
  position: fixed; inset: 0; width: 100%; height: 100%;
  z-index: 0; pointer-events: none;
}
.app { position: relative; z-index: 1; }

body {
  margin: 0; background: var(--ground); color: var(--ink);
  font-family: var(--ui); font-size: 14px; line-height: 1.5;
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
}
/* Every datum wears the instrument face and lines up in columns. */
code, .mono, td.num, th.num, .tile .v, .metric { font-family: var(--mono); font-variant-numeric: tabular-nums; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 2px; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* One orchestrated entrance, not scattered effects. */
@keyframes rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
main > * { animation: rise .32s cubic-bezier(.2,.7,.3,1) both; }
main > *:nth-child(2) { animation-delay: .04s; }
main > *:nth-child(3) { animation-delay: .08s; }
@media (prefers-reduced-motion: reduce) {
  *,*::before,*::after { animation: none !important; transition: none !important; }
}

/* ══ SHELL: a fixed rail, because consoles have rails ══════════════════════ */
.app { display: grid; grid-template-columns: 216px minmax(0,1fr); min-height: 100vh; }
.rail {
  /* Sheer cream, so the neural field runs under the rail too.
     TWO declarations of the SAME property, in this order, is the fallback idiom —
     the second wins where color-mix is supported and is dropped where it is not,
     leaving opaque cream. The first attempt used background-color for the fallback
     and background-image for the veil, which does not work: they are different
     properties, so the opaque colour was painted UNDERNEATH the sheer layer and
     the result was exactly opaque cream (measured: rgb(247,242,230), no blue at
     all). A fallback has to be overridden, not composited. */
  background: var(--rail);
  background: var(--rail-veil);
  color: var(--rail-ink); border-right: 1px solid var(--rail-line);
  display: flex; flex-direction: column; gap: 1.5rem; padding: 1rem .85rem;
  position: sticky; top: 0; height: 100vh;
}
/* The rail's sibling: the command bar plus the page. min-width:0 so a wide table
   inside it scrolls rather than stretching the grid column. */
.col { min-width: 0; display: flex; flex-direction: column; }
.brand { display: flex; align-items: center; gap: .55rem; padding: .3rem .4rem; }
.brand .mark {
  width: 22px; height: 22px; border-radius: 6px; flex: 0 0 auto;
  background: linear-gradient(140deg, var(--accent), color-mix(in srgb, var(--accent) 55%, #7c3aed));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.35);
}
.brand b { font-size: .95rem; font-weight: 640; letter-spacing: -.015em; }
.rail nav { display: flex; flex-direction: column; gap: 2px; }
.rail nav a {
  display: flex; align-items: center; gap: .55rem;
  padding: .44rem .55rem; border-radius: var(--r-sm);
  color: var(--ink-2); font-weight: 500; font-size: .875rem;
  transition: background .12s ease, color .12s ease;
}
/* Hover/active tints are mixed FROM the cream, so the rail stays one material
   rather than pasting a slate chip onto a cream panel. */
.rail nav a:hover { background: color-mix(in srgb, var(--rail-ink) 7%, var(--rail)); color: var(--rail-ink); text-decoration: none; }
.rail nav a[aria-current="page"] { background: color-mix(in srgb, var(--rail-ink) 12%, var(--rail)); color: var(--rail-ink); font-weight: 640; }
.rail nav a[aria-current="page"] .g { color: #1b4fa8; }
.rail nav a .g { width: 15px; text-align: center; opacity: .8; font-size: .95em; }
.rail nav a .ct {
  margin-left: auto; font-family: var(--mono); font-size: .7rem;
  color: var(--rail-ink); background: color-mix(in srgb, var(--rail-ink) 12%, var(--rail));
  border-radius: 999px; padding: 0 .35rem;
}
.rail nav a[aria-current="page"] .ct { background: #1b4fa8; color: #fff; }
.rail .foot { margin-top: auto; display: flex; flex-direction: column; gap: .5rem; font-size: .72rem; color: color-mix(in srgb, var(--rail-ink) 60%, var(--rail)); }
.rail .foot code { font-size: .68rem; word-break: break-all; color: color-mix(in srgb, var(--rail-ink) 55%, var(--rail)); }
.theme-toggle {
  border: 1px solid var(--rail-line); background: color-mix(in srgb, var(--rail-ink) 5%, var(--rail)); color: var(--rail-ink);
  border-radius: 999px; padding: .22rem .6rem; font-size: .72rem; cursor: pointer; width: fit-content;
}
.theme-toggle:hover { border-color: color-mix(in srgb, var(--rail-ink) 35%, var(--rail)); }
@media (max-width: 860px) {
  .app { grid-template-columns: 1fr; }
  .rail { position: static; height: auto; flex-direction: row; align-items: center; flex-wrap: wrap; gap: .75rem; border-right: 0; border-bottom: 1px solid var(--rail-line); }
  .rail nav { flex-direction: row; flex-wrap: wrap; }
  .rail .foot { margin: 0; flex-direction: row; align-items: center; }
  .topbar { padding: .6rem .9rem .5rem; }
  main { padding: .8rem .9rem 2rem; }
  /* On a phone the viewport is short and the chrome stacks, so an inner scroll
     region fights the page scroll. Let the page own it. */
  .scroll { max-height: none; min-height: 0; }
}

/* ══ COMMAND BAR ═══════════════════════════════════════════════════════════
   ONE row carrying identity + liveness: page title, account state, when the last
   webhook landed, the live indicator. Previously these were three stacked bands
   (health strip, then title block, then filters) — about 190px of chrome above the
   first data row, which is why the page scrolled before any data was visible. A
   console's header belongs in one sticky row; the data gets the rest. */
.topbar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: baseline; gap: .75rem; flex-wrap: wrap;
  padding: .7rem 1.6rem .6rem;
  background: color-mix(in srgb, var(--ground) 82%, transparent);
  backdrop-filter: blur(10px) saturate(1.2);
  border-bottom: 1px solid color-mix(in srgb, var(--on-ground) 16%, transparent);
}
.topbar .spacer { flex: 1 1 auto; }
.topbar h1 { margin: 0; }
.topbar .sub { flex: 0 1 100%; margin: -.1rem 0 0; }
/* The health chips sit on the blue, so they need the cream hairline treatment
   rather than the paper-card one they wear inside a table. */
.topbar .badge { background: color-mix(in srgb, var(--on-ground) 12%, transparent); color: var(--on-ground); border-color: color-mix(in srgb, var(--on-ground) 26%, transparent); }
.topbar .badge.good { color: #baf0ba; border-color: color-mix(in srgb, var(--good) 55%, transparent); background: color-mix(in srgb, var(--good) 20%, transparent); }
.topbar .badge.critical { color: #ffd3d3; border-color: color-mix(in srgb, var(--critical) 60%, transparent); background: color-mix(in srgb, var(--critical) 26%, transparent); }
.topbar .muted, .topbar .pill { color: var(--on-ground-2); border-color: color-mix(in srgb, var(--on-ground) 22%, transparent); }
.topbar .muted strong { color: var(--on-ground); }
.topbar #live-dot.on { color: #baf0ba; }

main { padding: .9rem 1.6rem 2.5rem; max-width: 1500px; }
h1 { font-size: 1.32rem; font-weight: 650; letter-spacing: -.025em; margin: 0 0 .15rem; text-wrap: balance;
     color: var(--on-ground); }
.sub { color: var(--on-ground-2); margin: 0; font-size: .84rem; max-width: 78ch; }
.sub code, .sub a { color: var(--on-ground); }
h2 { font-size: .69rem; font-weight: 650; text-transform: uppercase; letter-spacing: .09em; color: var(--ink-3); margin: 0 0 .7rem; }

/* ══ SURFACES ═════════════════════════════════════════════════════════════ */
.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); padding: 1.05rem 1.1rem; box-shadow: var(--elev-1); }
main > .card, .grid { margin-bottom: 1rem; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%,320px), 1fr)); gap: 1rem; }

/* ══ STAT TILES: summary before detail ════════════════════════════════════ */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%,150px), 1fr)); gap: .7rem; margin-bottom: 1.15rem; }
.tile { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); padding: .8rem .9rem; box-shadow: var(--elev-1); transition: box-shadow .15s ease, transform .15s ease; }
.tile:hover { box-shadow: var(--elev-2); transform: translateY(-1px); }
.tile .k { font-size: .67rem; font-weight: 650; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-3); }
.tile .v { font-size: 1.7rem; font-weight: 600; letter-spacing: -.035em; line-height: 1.1; margin-top: .2rem; }
.tile .n { font-size: .75rem; color: var(--ink-2); margin-top: .15rem; }
.tile.accent { border-color: color-mix(in srgb, var(--accent) 35%, var(--line)); }
.tile.accent .v { color: var(--accent); }

/* ══ TABLES: instrument panels, with a severity stripe ════════════════════ */
.table-wrap { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--elev-1); overflow: hidden; }
/* The LIST scrolls, not the page. thead is already sticky, so the column headers
   stay put while rows move under them, and the toolbar above never leaves the
   screen. min-height so a two-row queue doesn't get a pointless scroll box;
   max-height as a ceiling, so nothing is ever trapped — if a page has more chrome
   than this allows for, it still scrolls normally. */
.scroll { overflow: auto; -webkit-overflow-scrolling: touch; max-height: calc(100vh - 17rem); min-height: 9rem; overscroll-behavior: contain; }
.scroll::-webkit-scrollbar { width: 11px; height: 11px; }
.scroll::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 99px; border: 3px solid var(--surface); }
.scroll::-webkit-scrollbar-thumb:hover { background: var(--ink-3); }
table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px; }
thead th {
  position: sticky; top: 0; z-index: 1; background: var(--surface-2); color: var(--ink-3);
  font-size: .66rem; font-weight: 650; text-transform: uppercase; letter-spacing: .07em;
  text-align: left; white-space: nowrap; padding: .5rem .6rem; border-bottom: 1px solid var(--line);
}
/* The identifying column stays put while the rest scrolls sideways. The campaigns
   table is 15 columns wide, so without this you scroll to "REPLY RATE" and can no
   longer see WHICH campaign the number belongs to — the row loses its subject.
   It doubles as the scroll affordance: content visibly slides under it. */
thead th:first-child { left: 0; z-index: 3; }
tbody td:first-child, thead th:first-child { position: sticky; }
tbody td:first-child {
  left: 0; z-index: 1; background: var(--surface);
  box-shadow: 1px 0 0 var(--line);
}
tbody tr:hover td:first-child { background: var(--surface-2); }
tbody td { padding: .58rem .6rem; border-bottom: 1px solid var(--line); vertical-align: top; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background .1s ease; }
tbody tr:hover td { background: var(--surface-2); }
td.num, th.num { text-align: right; }
/* Prose columns get a floor width. Without one, ten columns competing squeezed the
   draft down to ~10 characters and wrapped it over four lines, which made every row
   tall and the text unreadable — the table can scroll sideways instead. */
td.txt, th.txt { min-width: 15rem; }
.lead-name { font-weight: 560; letter-spacing: -.006em; }
/* The stripe encodes WHY a row wants you — form as well as colour, readable
   before any text is parsed. */
tbody tr.sev > td:first-child { position: relative; padding-left: .95rem; }
tbody tr.sev > td:first-child::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--line-2);
}
tbody tr.sev-needs_review > td:first-child::before { background: var(--serious); }
tbody tr.sev-send_failed  > td:first-child::before { background: var(--critical); }
tbody tr.sev-draft_pending > td:first-child::before { background: var(--accent); }
/* A human owns this lead. A muted stripe on purpose: visible as a distinct group
   when scanning, but never competing with the rows that need action. */
tbody tr.sev-handed_over > td:first-child::before { background: var(--line-2); }
tbody tr.sev-handed_over > td { opacity: .82; }
tbody tr.sev-handed_over:hover > td { opacity: 1; }
/* On the conversations list the stripe means lead state, not queue reason — the
   same visual device carrying the fact that screen is about. */
tbody tr.sev-none > td:first-child::before { background: transparent; }

/* Inline meter: a rate is easier to compare as a length than as digits alone. */
.meter { display: flex; align-items: center; gap: .4rem; justify-content: flex-end; }
.meter .bar { width: 42px; height: 4px; border-radius: 2px; background: var(--surface-3); overflow: hidden; flex: 0 0 auto; }
.meter .bar i { display: block; height: 100%; background: var(--accent); border-radius: 2px; }

/* ══ CHIPS ════════════════════════════════════════════════════════════════ */
.badge {
  display: inline-flex; align-items: center; gap: .3rem; padding: .1rem .48rem;
  border-radius: 999px; font-size: .71rem; font-weight: 600; white-space: nowrap;
  border: 1px solid var(--line); color: var(--ink-2); background: var(--surface-2);
}
.badge.good{background:var(--good-soft);color:var(--good-ink);border-color:transparent}
.badge.warning{background:var(--warning-soft);color:var(--warning-ink);border-color:transparent}
.badge.serious{background:var(--serious-soft);color:var(--serious-ink);border-color:transparent}
.badge.critical{background:var(--critical-soft);color:var(--critical-ink);border-color:transparent}
.badge .ico { width: 5px; height: 5px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }
.pill { display: inline-block; font-size: .68rem; color: var(--ink-3); border: 1px dashed var(--line-2); border-radius: 999px; padding: .02rem .4rem; white-space: nowrap; }
.muted { color: var(--ink-3); } .dim { color: var(--ink-2); }
code { font-size: .85em; color: var(--ink-2); }

/* ══ NOTICES ══════════════════════════════════════════════════════════════ */
.notice { display: flex; gap: .6rem; align-items: flex-start; border-radius: var(--r-md); padding: .68rem .85rem; margin-bottom: 1rem; font-size: .855rem; border: 1px solid transparent; }
.notice .ico { flex: 0 0 auto; }
.notice.warn { background: var(--warning-soft); color: var(--warning-ink); border-color: color-mix(in srgb, var(--warning) 38%, transparent); }
.notice.ok { background: var(--good-soft); color: var(--good-ink); border-color: color-mix(in srgb, var(--good) 32%, transparent); }
.notice.bad { background: var(--critical-soft); color: var(--critical-ink); border-color: color-mix(in srgb, var(--critical) 34%, transparent); }

/* ══ CONTROLS: what is interactive looks interactive ══════════════════════ */
/* The toolbar is a paper strip, not six anonymous selects on the blue: each
   control carries a micro-label, because "all campaigns" and "every state" are
   indistinguishable until you open them. Labels are above, so the row still reads
   left-to-right as one band of controls. */
.filters {
  display: flex; gap: .55rem; flex-wrap: wrap; align-items: flex-end; margin-bottom: .9rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: .6rem .7rem; box-shadow: var(--elev-1);
}
.filters .field { display: flex; flex-direction: column; gap: .18rem; min-width: 0; }
.filters .field > span {
  font-size: .6rem; font-weight: 650; text-transform: uppercase; letter-spacing: .08em;
  color: var(--ink-3); padding-left: .1rem;
}
.filters .field select, .filters .field input { width: 100%; }
.filters .grow { flex: 1 1 13rem; }
.filters .sep { flex: 1 1 auto; }
/* Buttons align with the control boxes, not with their labels. */
.filters .btn, .filters label.check { margin-bottom: 1px; }
@media (max-width: 860px) { .filters .sep { display: none; } }
input[type=search],input[type=text],input[type=password],select,textarea {
  font: inherit; color: var(--ink); background: var(--surface);
  border: 1px solid var(--line-2); border-radius: var(--r-sm); padding: .4rem .55rem;
  transition: border-color .12s ease, box-shadow .12s ease;
}
select { padding-right: 1.6rem; appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-3) 50%), linear-gradient(135deg, var(--ink-3) 50%, transparent 50%);
  background-position: calc(100% - 14px) 52%, calc(100% - 9px) 52%;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; }
input:hover,select:hover,textarea:hover { border-color: var(--ink-3); }
input:focus,select:focus,textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent); }
textarea { width: 100%; min-height: 8rem; line-height: 1.55; resize: vertical; font-family: var(--ui); }
label.check { display: inline-flex; align-items: center; gap: .35rem; color: var(--ink-2); font-size: .83rem; }
.btn { font: inherit; font-weight: 550; cursor: pointer; border: 1px solid var(--line-2); background: var(--surface); color: var(--ink); border-radius: var(--r-sm); padding: .4rem .78rem; transition: background .12s, border-color .12s, transform .06s, box-shadow .12s; }
.btn:hover { border-color: var(--ink-3); background: var(--surface-2); }
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--accent); border-color: transparent; color: var(--accent-ink); box-shadow: 0 1px 2px color-mix(in srgb, var(--accent) 35%, transparent); }
.btn-primary:hover { background: color-mix(in srgb, var(--accent) 88%, #000); border-color: transparent; }
.btn-danger { color: var(--critical-ink); border-color: color-mix(in srgb, var(--critical) 42%, transparent); background: transparent; }
.btn-danger:hover { background: var(--critical-soft); }
.actions { display: flex; gap: .55rem; align-items: center; flex-wrap: wrap; margin-top: .8rem; }

/* ══ CONVERSATION ═════════════════════════════════════════════════════════ */
.thread { display: flex; flex-direction: column; gap: .45rem; }
.msg-row { display: flex; gap: .5rem; align-items: flex-end; max-width: min(80%,760px); }
.msg-row.out { margin-left: auto; flex-direction: row-reverse; }
.avatar { flex: 0 0 auto; width: 25px; height: 25px; border-radius: 50%; display: grid; place-items: center; font-size: .63rem; font-weight: 650; font-family: var(--mono); background: var(--surface-3); color: var(--ink-2); border: 1px solid var(--line); }
.msg-row.out .avatar { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
.bubble { background: var(--surface-2); border: 1px solid var(--line); border-radius: 13px; padding: .48rem .72rem; white-space: pre-wrap; word-break: break-word; }
.msg-row.out .bubble { background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 22%, transparent); }
.msg-row .when { display: block; font-size: .67rem; color: var(--ink-3); margin-bottom: .12rem; font-family: var(--mono); }

dl.facts { display: grid; grid-template-columns: minmax(6rem,auto) 1fr; gap: .42rem .85rem; margin: 0; font-size: 13px; }
dl.facts dt { color: var(--ink-3); font-weight: 500; }
dl.facts dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.draft-text { white-space: pre-wrap; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); padding: .78rem; font-size: 13.5px; line-height: 1.6; }

.empty { text-align: center; padding: 2.4rem 1rem; }
.empty .big { font-size: 1.5rem; margin-bottom: .35rem; color: var(--ink-3); }
.empty p { margin: .3rem auto; max-width: 44ch; color: var(--ink-2); }
/* Paragraphs directly on the blue (row counts, footnotes) read in cream. */
main > p.muted, #live-root > p.muted { color: var(--on-ground-2); }

/* ══ HEALTH: the answer to "is this thing on?" ════════════════════════════
   The chips now ride in the command bar (see .topbar) rather than in a band of
   their own; .healthbar remains the inline group that holds them together. */
.healthbar { display: contents; }
#live-dot { font-family: var(--mono); font-size: .72rem; }
#live-dot.on { animation: pulse 2.4s ease-in-out infinite; }

.spark { vertical-align: middle; color: var(--accent); }
