/* =============================================================================
   The operator console — design system.

   Written for one person who reads this screen forty times a day and already
   knows the domain. That fact decides most of what follows: one accent colour
   reserved for focus and selection, and colour otherwise spent ONLY on
   severity. If an element is red here, something is wrong; nothing is red
   because it looked nice.

   It used to say "density over decoration, hairlines over shadows" and take
   that to mean every surface sat flat on one background. Watching the operator
   use it settled that: "there is just a lot happening where I don't know
   what's going on... it's hard for me to digest." Hairline-only separation is
   not density, it is a wall — twenty-five objects with nothing telling you
   where one ends and the next begins. Rows stay dense. CONTAINERS now float:
   a recessed page, white cards, real padding, one shadow step. Density is
   about how much fits in a row; legibility is about how the page is grouped,
   and those are different arguments.

   Every value is chosen, not defaulted. Radii stop at 10px, spacing uses 6 and
   10 as well as 8 and 12 because dense rows need them, and the type scale is
   in half-pixels where the half-pixel was what read correctly at 13px.
   ============================================================================= */

/* --------------------------------------------------------------- tokens ---- */

:root {
  color-scheme: light;

  /* Type ---------------------------------------------------------------
     A system stack, deliberately. This console has to open when a network
     is down, a font CDN is blocked, or the operator is on a plane — and on
     the machine it runs on, the system stack IS SF Pro. A webfont would buy
     nothing here and cost a render-blocking request on the one screen that
     must never fail to paint. */
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  --t-micro: 10.5px;   /* column headers, eyebrows — always uppercase + tracked */
  --t-meta: 11.5px;    /* timestamps, counts, secondary meta */
  --t-sm: 12.5px;      /* secondary text inside a row */
  --t-base: 13px;      /* table body, controls, the console's true default */
  --t-body: 14px;      /* prose */
  --t-read: 14.5px;    /* message bodies — the only text read in long form */
  --t-lg: 15px;        /* panel titles */
  --t-xl: 18px;        /* page title */
  --t-fig: 27px;       /* the one figure a page is actually about */

  --track-caps: 0.065em;

  /* Space — 2px grid, with 6 and 10 kept because 32px rows need them -------- */
  --s1: 2px;  --s2: 4px;  --s3: 6px;  --s4: 8px;  --s5: 10px;
  --s6: 12px; --s7: 16px; --s8: 20px; --s9: 24px; --s10: 32px; --s11: 44px;

  /* Radii — four values, none above 8px. Large radii are the single loudest
     tell of a generated UI and they make a dense table look like a toy. Table
     rows and the table itself are square; only floating surfaces round. */
  --r-chip: 3px;
  --r: 4px;
  --r-panel: 6px;
  --r-modal: 8px;
  --r-pill: 999px;

  --row: 32px;         /* dense table row */
  --row-lg: 44px;      /* primary list row (two lines of content) */
  --topbar: 46px;

  /* Surfaces — warm neutrals. Cool #f8fafc greys are the Tailwind default and
     read as untouched; a hair of warmth reads as chosen. */
  /* The page is RECESSED, the cards are white. The gap between these two
     values is what makes a card read as a card; at #fbfbfa vs #ffffff it was
     2% and the whole screen read as one continuous sheet. */
  --bg: #f2f2ef;
  --surface: #ffffff;
  --surface-2: #f7f7f5;
  --surface-3: #efeeeb;
  --line: #e4e3df;
  --line-soft: #eeedea;
  --line-strong: #d3d2cd;

  --ink: #17181a;
  --ink-2: #5c5f66;
  /* 4.66:1 on the palest surface this is used on, measured — the previous
     #8b8f97 was 3.13:1, and it carries column headers, timestamps, skipped-send
     reasons and the label above a typed retire confirmation. Not decoration. */
  --ink-3: #6b6f77;
  --ink-inv: #ffffff;

  /* Exactly one accent, and it means "focused / selected / actionable". */
  --accent: #2c5ff6;
  --accent-ink: #1e47c4;
  --accent-soft: #eef2fe;
  --accent-line: #c8d6fb;

  /* Severity triples. Nothing else in this file is allowed these hues. */
  --crit-bg: #fef1f1; --crit-line: #f1c6c3; --crit-ink: #b4231c;
  --warn-bg: #fff8eb; --warn-line: #efd7a3; --warn-ink: #92620a;
  --ok-bg:   #eef8f0; --ok-line:   #bee2c7; --ok-ink:   #197539;
  --info-bg: #eff3fe; --info-line: #c7d6f8; --info-ink: #2551c4;
  --neut-bg: #f4f4f2; --neut-line: #e1e0dc; --neut-ink: #5c5f66;

  /* Three elevations and no others.
     FLAT   — tables, rows, panels: separated by a hairline, never a shadow.
     DOCKED — the top bar and sticky headers: a single hairline, no blur.
     OVERLAY— things genuinely above the page. Two stops, a tight contact
              shadow plus a wide ambient one, tinted toward the ink rather
              than pure black, because a pure-black shadow on a warm surface
              reads grey and dirty. */
  /* CARD — the new one. A tight contact shadow plus a short ambient one, both
     tinted toward the ink. Enough to lift a panel off the recessed page and
     no more: a card that announces itself is as tiring as no card at all. */
  --shadow-card: 0 1px 1px rgba(20, 22, 26, 0.04), 0 2px 6px -2px rgba(20, 22, 26, 0.08);
  --shadow-docked: 0 1px 0 var(--line);
  --shadow-pop: 0 1px 2px rgba(20, 22, 26, 0.06), 0 16px 40px -18px rgba(20, 22, 26, 0.30);
  --shadow-modal: 0 1px 2px rgba(20, 22, 26, 0.08), 0 28px 60px -18px rgba(20, 22, 26, 0.32);

  --focus: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --bg: #0a0b0d;
  --surface: #16181b;
  --surface-2: #1b1e21;
  --surface-3: #22262a;
  --line: #282c31;
  --line-soft: #202428;
  --line-strong: #363b42;

  --ink: #e8e9ea;
  --ink-2: #9ba1a9;
  /* 5.97:1 on --surface-2, measured. */
  --ink-3: #949ba4;
  --ink-inv: #0e0f11;

  --accent: #5a87ff;
  --accent-ink: #9db6ff;
  --accent-soft: #151c2e;
  --accent-line: #2c3f6d;

  /* Dark severity is NOT the light palette inverted. Saturated reds on near
     black vibrate; these are desaturated backgrounds with a bright,
     legible foreground, tuned for 13px text rather than for a swatch. */
  --crit-bg: #271618; --crit-line: #52292a; --crit-ink: #ff8d85;
  --warn-bg: #241d11; --warn-line: #4e3d1d; --warn-ink: #eab458;
  --ok-bg:   #12211a; --ok-line:   #234430; --ok-ink:   #5fd08a;
  --info-bg: #151c2e; --info-line: #2a3a62; --info-ink: #86a9ff;
  --neut-bg: #1b1e21; --neut-line: #2b2f35; --neut-ink: #9ba1a9;

  /* On a near-black page a 4%-black shadow is invisible. Dark elevation comes
     mostly from the surface being LIGHTER than the page, with a harder shadow
     doing the rest. */
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.36), 0 2px 8px -2px rgba(0, 0, 0, 0.44);
  --shadow-docked: 0 1px 0 var(--line);
  --shadow-pop: 0 1px 2px rgba(0, 0, 0, 0.5), 0 18px 44px -18px rgba(0, 0, 0, 0.75);
  --shadow-modal: 0 1px 2px rgba(0, 0, 0, 0.55), 0 30px 70px -18px rgba(0, 0, 0, 0.8);
}

/* ---------------------------------------------------------------- base ---- */

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

html, body { height: 100%; }

/* A focused element must not land under the sticky header. Measured: six of
   twenty-six Shift+Tab stops were fully hidden behind the topbar and the
   dry-run banner. */
html { scroll-padding-top: 56px; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--t-base);
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Faux bold on a system stack looks smeared at 13px. */
  font-synthesis: none;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, p, figure, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }

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

button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
button:disabled, [aria-disabled="true"] { cursor: not-allowed; }

::selection { background: var(--accent-soft); color: var(--accent-ink); }

/* One focus treatment everywhere. It uses outline rather than a border swap so
   focusing an element can never reflow the row it sits in. */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-radius: var(--r-chip);
}

/* Every number in this application is tabular. Digits that change width make a
   column of figures jitter on every poll, and jitter reads as instability in
   the system being measured. */
.num, td.num, th.num, .figure, time, .mono {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}
.mono { font-family: var(--font-mono); font-size: 12px; letter-spacing: -0.01em; }

::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border: 3px solid var(--bg);
  border-radius: var(--r-pill);
}
::-webkit-scrollbar-thumb:hover { background: var(--ink-3); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---------------------------------------------------------------- shell --- */

/* A SIDEBAR, not a top bar.
   Seven destinations across the top competed with the page title for the same
   horizontal line, and left nowhere to put a workspace switcher — which a
   multi-tenant system needs and this one had nowhere to show. Vertical nav
   also stops the list being a width budget: adding an eighth destination is
   now free. */
#root { display: flex; min-height: 100%; align-items: stretch; }

.sidebar {
  width: 228px; flex: none;
  position: sticky; top: 0; align-self: flex-start;
  height: 100vh; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: var(--s5);
  padding: var(--s6) var(--s5);
  background: var(--surface);
  border-right: 1px solid var(--line);
  z-index: 40;
}
.main-col { flex: 1; min-width: 0; display: flex; flex-direction: column; }



.side-foot { margin-top: auto; display: flex; flex-direction: column; gap: var(--s2); padding-top: var(--s5); }
.side-foot .btn { justify-content: flex-start; width: 100%; }

/* The workspace switcher. Rendered as static text when there is only one —
   a select with a single option is a control that lies about having a choice. */
.side-ws {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s4) var(--s5);
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface-2);
  font-size: var(--t-sm); color: var(--ink-2);
  min-width: 0;
}
.side-ws select {
  appearance: none; border: 0; background: none; padding: 0; margin: 0;
  font: inherit; color: var(--ink); font-weight: 600;
  width: 100%; min-width: 0; cursor: pointer;
}
.side-ws select:focus-visible { outline: none; box-shadow: var(--focus); border-radius: var(--r-chip); }
.side-ws-static { font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side-ws::before {
  content: "WS"; flex: none;
  font-size: var(--t-micro); font-weight: 700; letter-spacing: var(--track-caps);
  color: var(--ink-3);
}
.brand-name { letter-spacing: -0.01em; }

.brand { display: flex; align-items: center; gap: var(--s4); min-width: 0; padding: var(--s2) var(--s4); }
.brand-name {
  font-size: var(--t-base); font-weight: 600; letter-spacing: -0.008em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* The health pip. Three states and no more, because a pip that can be five
   things is read as decoration. */
.pip {
  width: 7px; height: 7px; border-radius: var(--r-pill);
  flex: none; background: var(--ink-3);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ink-3) 18%, transparent);
}
.pip[data-state="ok"] { background: var(--ok-ink); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok-ink) 16%, transparent); }
.pip[data-state="warn"] { background: var(--warn-ink); box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn-ink) 18%, transparent); }
.pip[data-state="crit"] {
  background: var(--crit-ink);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--crit-ink) 20%, transparent);
  animation: pulse 2.4s ease-in-out infinite;
}
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }

.nav { display: flex; flex-direction: column; align-items: stretch; gap: 1px; }
.nav a {
  position: relative;
  display: flex; align-items: center; gap: var(--s3);
  height: 32px; padding: 0 var(--s5);
  border-radius: var(--r);
  color: var(--ink-2); font-size: var(--t-base); font-weight: 500;
  white-space: nowrap;
  transition: background-color 0.12s ease, color 0.12s ease;
}
.nav a:hover { background: var(--surface-2); color: var(--ink); text-decoration: none; }
.nav a[aria-current="page"] { background: var(--surface-3); color: var(--ink); font-weight: 600; }
.nav .count {
  margin-left: auto;
  font-size: var(--t-meta); font-weight: 600; font-variant-numeric: tabular-nums;
  padding: 0 5px; min-width: 16px; text-align: center;
  border-radius: var(--r-chip);
  background: var(--neut-bg); color: var(--ink-2); border: 1px solid var(--neut-line);
}
.nav a[aria-current="page"] .count { background: var(--surface); }
.nav .count[data-tone="crit"] { background: var(--crit-bg); color: var(--crit-ink); border-color: var(--crit-line); }
.nav .count[data-tone="warn"] { background: var(--warn-bg); color: var(--warn-ink); border-color: var(--warn-line); }

.topbar-right { margin-left: auto; display: flex; align-items: center; gap: var(--s5); }

/* Freshness, stated rather than implied. A console that silently shows data
   from nine minutes ago is lying by omission; this is how it stops. */
.freshness {
  display: inline-flex; align-items: center; gap: var(--s3);
  font-size: var(--t-meta); color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.freshness[data-stale="true"] { color: var(--warn-ink); }
.freshness[data-error="true"] { color: var(--crit-ink); }

kbd {
  font-family: var(--font); font-size: 10.5px; font-weight: 600;
  line-height: 16px; min-width: 17px; height: 17px; padding: 0 4px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); border-bottom-width: 2px;
  border-radius: var(--r-chip);
  background: var(--surface-2); color: var(--ink-2);
}

.dryrun {
  display: flex; align-items: center; gap: var(--s4);
  padding: var(--s3) var(--s7);
  background: var(--warn-bg);
  border-bottom: 1px solid var(--warn-line);
  color: var(--warn-ink);
  font-size: var(--t-sm); font-weight: 600;
  /* The sidebar is its own scroll context now, so this pins to the top of the
     content column rather than under a bar that no longer exists. */
  position: sticky; top: 0; z-index: 39;
}
.dryrun span { font-weight: 400; }

main { flex: 1; min-height: 0; }

.page { padding: var(--s9) var(--s8) var(--s11); max-width: 1560px; margin: 0 auto; }
.page-wide { max-width: none; }

.page-head {
  display: flex; align-items: baseline; gap: var(--s6);
  margin-bottom: var(--s7); flex-wrap: wrap;
}
.page-title { font-size: var(--t-xl); font-weight: 600; letter-spacing: -0.014em; }
.page-sub { font-size: var(--t-sm); color: var(--ink-3); }
.page-actions { margin-left: auto; display: flex; align-items: center; gap: var(--s4); }

/* --------------------------------------------------------------- panels --- */

.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-panel);
  /* The one change that does the most work. A panel is now a CARD lifted off a
     recessed page, not a hairline box drawn on the same sheet as everything
     else. Grouping stops being something the reader has to infer. */
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.panel-head {
  display: flex; align-items: center; gap: var(--s5);
  /* Was 10/12. A card's head needs to breathe or the shadow just wraps the
     same cramped box more expensively. */
  padding: var(--s6) var(--s7);
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface);
}
.panel-title {
  font-size: var(--t-base); font-weight: 600; letter-spacing: -0.006em;
  /* It is an <h2>: a console with no heading structure is unnavigable with a
     screen reader, and a <span> that looks like a heading helps nobody. */
  margin: 0;
}
.panel-head .spacer { margin-left: auto; }
.panel-body { padding: var(--s7); }
.panel-foot {
  padding: var(--s5) var(--s7);
  border-top: 1px solid var(--line-soft);
  background: var(--surface-2);
  font-size: var(--t-meta); color: var(--ink-3);
}

.eyebrow {
  font-size: var(--t-micro); font-weight: 600; text-transform: uppercase;
  letter-spacing: var(--track-caps); color: var(--ink-3);
}

.grid { display: grid; gap: var(--s7); }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* Column groups need more air than elements inside one. At a 12px gap a
   right-aligned figure sat one space from the next column's label and the two
   read as a single phrase. */
.panel-body.grid-3, .panel-body.grid-2 { gap: var(--s10); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid-main { grid-template-columns: minmax(0, 1fr) 372px; align-items: start; }

/* ------------------------------------------------------------- figures ---- */

/* A figure block, used sparingly. Cards are correct for exactly one thing:
   a small fixed set of headline quantities that are read together and never
   sorted or compared across rows. Everything else is a table. */
/* Each figure is its OWN card.
   They were one card subdivided by hairlines, which reads as a table — four
   cells of a single object rather than four independent measurements. Giving
   each its own surface is what lets the eye take one and stop, instead of
   scanning the row as a unit and absorbing none of it. */
.figures {
  display: grid; gap: var(--s6);
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.figures > * {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-panel);
  box-shadow: var(--shadow-card);
  padding: var(--s7);
  min-width: 0;
}

.figure-label {
  font-size: var(--t-micro); font-weight: 600; text-transform: uppercase;
  letter-spacing: var(--track-caps); color: var(--ink-3);
  display: flex; align-items: center; gap: var(--s3);
}
.figure {
  margin-top: var(--s3);
  font-size: var(--t-fig); font-weight: 600; letter-spacing: -0.02em; line-height: 1.05;
}
.figure small { font-size: 15px; font-weight: 500; color: var(--ink-3); letter-spacing: -0.01em; }
.figure-note { margin-top: var(--s2); font-size: var(--t-meta); color: var(--ink-3); }
.figure[data-tone="crit"] { color: var(--crit-ink); }
.figure[data-tone="warn"] { color: var(--warn-ink); }
.figure[data-tone="ok"] { color: var(--ok-ink); }
.figure[data-tone="muted"] { color: var(--ink-3); }

/* Capacity: the only bar in the application, because "412 of 640" is a ratio
   whose headroom is the actual question. */
.bar { height: 5px; border-radius: var(--r-pill); background: var(--surface-3); overflow: hidden; margin-top: var(--s5); }
/* Neutral until it matters. The accent belongs to focus and selection, and
   every other colour belongs to severity; a meter at 30% of capacity is
   neither, so it is ink. */
.bar > i { display: block; height: 100%; border-radius: var(--r-pill); background: var(--ink-3); transition: width 0.3s ease; }
.bar > i[data-tone="warn"] { background: var(--warn-ink); }
.bar > i[data-tone="crit"] { background: var(--crit-ink); }

/* ---------------------------------------------------------------- alerts -- */

/* Two severities. A third would be ignored, and an alarm that is ignored is
   worse than no alarm: it teaches the operator that red does not mean act. */
.alert {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  gap: var(--s5); align-items: start;
  padding: var(--s5) var(--s6);
  border-bottom: 1px solid var(--line-soft);
}
.alert:last-child { border-bottom: 0; }
.alert[data-sev="critical"] { background: var(--crit-bg); }
.alert[data-sev="warning"] { background: var(--warn-bg); }

.alert-tag {
  font-size: var(--t-micro); font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--track-caps);
  padding: 3px var(--s4); border-radius: var(--r-chip); white-space: nowrap;
  margin-top: 1px;
}
.alert[data-sev="critical"] .alert-tag { background: var(--crit-ink); color: #fff; }
.alert[data-sev="warning"] .alert-tag { background: var(--warn-ink); color: #fff; }

.alert-kind { font-weight: 600; font-size: var(--t-base); }
.alert[data-sev="critical"] .alert-kind { color: var(--crit-ink); }
.alert[data-sev="warning"] .alert-kind { color: var(--warn-ink); }
.alert-detail { font-size: var(--t-sm); color: var(--ink-2); margin-top: 3px; max-width: 86ch; }

/* --------------------------------------------------------------- tables --- */

.table-wrap { overflow: auto; }
/* Rows and cells are square. Rounding a table row is the clearest signal that
   whoever built it was thinking about cards, not about columns of numbers. */
table, tbody tr, tbody td, thead th { border-radius: 0; }

table { width: 100%; border-collapse: separate; border-spacing: 0; }

thead th {
  position: sticky; top: 0; z-index: 2;
  height: 28px; padding: 0 var(--s5);
  text-align: left; white-space: nowrap;
  font-size: var(--t-micro); font-weight: 600; text-transform: uppercase;
  letter-spacing: var(--track-caps); color: var(--ink-3);
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
}
/* Every cell is tabular, not only the ones tagged .num.
   A table where one column's digits align and the next column's do not is
   worse than one where none do: the eye reads the misalignment as a data
   problem. Text columns are unaffected — tabular figures only change digits. */
thead th, tbody td { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }
thead th.num, td.num { text-align: right; }
thead th.sortable { cursor: pointer; user-select: none; }
thead th.sortable:hover { color: var(--ink); }
thead th[aria-sort] { color: var(--ink); }
thead th[aria-sort]::after { content: "↑"; margin-left: 4px; font-size: 9px; }
thead th[aria-sort="descending"]::after { content: "↓"; }

tbody td {
  height: var(--row); padding: 0 var(--s5);
  border-bottom: 1px solid var(--line-soft);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  /* Forces ellipsis to respect the column width rather than widening the
     table. It has to be undone for any cell holding a control, because the
     clamp squeezes a checkbox or a button down to a sliver. */
  max-width: 1px;
}
tbody td.shrink, tbody td.control {
  max-width: none; width: 1%; overflow: visible; text-overflow: clip;
}
/* The first and last cells align to the panel's own gutter, so a table's
   content sits on the same vertical line as every heading above it. */
tbody td:first-child, thead th:first-child { padding-left: var(--s6); }
tbody td:last-child, thead th:last-child { padding-right: var(--s6); }

/* Chips must never be clipped into an ellipsis: a truncated status reads as a
   different status. The cell scrolls its own overflow instead. */
tbody td .chip { flex: none; }
tbody td.chips { overflow: visible; text-overflow: clip; }
/* A strip of chips never wraps. A DNS cell that breaks onto a second line
   doubles the row height and pushes the row actions off the right edge, which
   is how the Pause and Retire buttons disappeared. */
tbody td.chips > span { display: inline-flex; align-items: center; gap: 3px; flex-wrap: nowrap; }
tbody td.control > div { flex-wrap: nowrap; }

/* Actions stay reachable when a wide table scrolls horizontally. Without this
   the Pause and Retire buttons on the fleet table sat past the right edge —
   present in the DOM, invisible on screen, on the one table whose whole
   purpose is pulling a mailbox out of rotation. */
tbody td.sticky-end, thead th.sticky-end {
  position: sticky; right: 0; z-index: 1;
  background: var(--surface);
  box-shadow: -1px 0 0 var(--line-soft);
}
thead th.sticky-end { background: var(--surface-2); z-index: 3; }
tbody tr:hover td.sticky-end { background: var(--surface-2); }
tbody tr:last-child td { border-bottom: 0; }
tbody tr[data-href] { cursor: pointer; }
tbody tr:hover td { background: var(--surface-2); }
tbody tr[aria-selected="true"] td { background: var(--accent-soft); }
tbody tr.focused td { box-shadow: inset 0 0 0 1px var(--accent-line); }
tbody td.wrap { white-space: normal; }
tbody td.shrink { width: 1%; }

td .sub { color: var(--ink-3); font-size: var(--t-meta); }
td .primary { font-weight: 500; }

/* ---------------------------------------------------------------- chips --- */

.chip {
  display: inline-flex; align-items: center; gap: var(--s2);
  height: 18px; padding: 0 var(--s4);
  border-radius: var(--r-chip);
  font-size: var(--t-meta); font-weight: 600; white-space: nowrap;
  background: var(--neut-bg); color: var(--neut-ink); border: 1px solid var(--neut-line);
}
.chip[data-tone="crit"] { background: var(--crit-bg); color: var(--crit-ink); border-color: var(--crit-line); }
.chip[data-tone="warn"] { background: var(--warn-bg); color: var(--warn-ink); border-color: var(--warn-line); }
.chip[data-tone="ok"] { background: var(--ok-bg); color: var(--ok-ink); border-color: var(--ok-line); }
.chip[data-tone="info"] { background: var(--info-bg); color: var(--info-ink); border-color: var(--info-line); }
.chip[data-tone="accent"] { background: var(--accent-soft); color: var(--accent-ink); border-color: var(--accent-line); }
.chip.mono { font-size: 10.5px; letter-spacing: 0; }

/* Tier is inventory, not decoration: A is the scarce, expensive, one-to-one
   list and the console should say so at a glance. It is rendered with WEIGHT
   rather than hue, because the accent belongs to focus and selection and every
   other colour belongs to severity — and a Tier A contact is not an alarm. */
.tier { width: 16px; justify-content: center; padding: 0; text-transform: uppercase; }
.tier[data-tier="a"] { background: var(--ink); color: var(--ink-inv); border-color: var(--ink); }

/* -------------------------------------------------------------- controls -- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s3);
  height: 28px; padding: 0 var(--s5);
  border: 1px solid var(--line-strong); border-radius: var(--r);
  background: var(--surface); color: var(--ink);
  font-size: var(--t-base); font-weight: 500; white-space: nowrap;
  transition: background-color 0.12s ease, border-color 0.12s ease, opacity 0.12s ease;
}
.btn:hover:not(:disabled) { background: var(--surface-2); border-color: var(--ink-3); }
.btn:active:not(:disabled) { background: var(--surface-3); }
.btn:disabled { opacity: 0.45; }

.btn-primary {
  background: var(--ink); color: var(--ink-inv); border-color: var(--ink);
  font-weight: 600;
}
.btn-primary:hover:not(:disabled) { background: #000; border-color: #000; }
:root[data-theme="dark"] .btn-primary:hover:not(:disabled) { background: #fff; border-color: #fff; }

.btn-danger { color: var(--crit-ink); border-color: var(--crit-line); background: var(--crit-bg); }
.btn-danger:hover:not(:disabled) { background: var(--crit-ink); color: #fff; border-color: var(--crit-ink); }

.btn-quiet { border-color: transparent; background: transparent; color: var(--ink-2); }
.btn-quiet:hover:not(:disabled) { background: var(--surface-2); color: var(--ink); border-color: transparent; }

.btn-sm { height: 24px; padding: 0 var(--s4); font-size: var(--t-sm); }

.btn.busy { position: relative; color: transparent !important; pointer-events: none; }
.btn.busy::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  width: 12px; height: 12px; border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent;
  color: var(--ink-2);
  animation: spin 0.6s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.input, select.input, textarea.input {
  height: 28px; padding: 0 var(--s5);
  border: 1px solid var(--line-strong); border-radius: var(--r);
  background: var(--surface); color: var(--ink);
  font-size: var(--t-base); width: 100%;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
textarea.input { height: auto; padding: var(--s4) var(--s5); line-height: 1.5; resize: vertical; }
.input::placeholder { color: var(--ink-3); }
.input:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.input:disabled { background: var(--surface-2); color: var(--ink-3); }
select.input { appearance: none; padding-right: var(--s8); background-image: none; }

.field { display: flex; flex-direction: column; gap: var(--s3); }
.field > label {
  font-size: var(--t-micro); font-weight: 600; text-transform: uppercase;
  letter-spacing: var(--track-caps); color: var(--ink-3);
}
.field .hint { font-size: var(--t-meta); color: var(--ink-3); }

.toolbar {
  display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap;
  padding: var(--s5) var(--s6);
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface);
}
.toolbar .search { flex: 1 1 220px; max-width: 340px; }

/* Segmented filter. A row of these replaces a dropdown when the options are
   few and the operator switches between them constantly. */
.segmented { display: inline-flex; border: 1px solid var(--line-strong); border-radius: var(--r); overflow: hidden; }
.segmented button {
  height: 26px; padding: 0 var(--s5);
  border: 0; border-right: 1px solid var(--line-soft);
  background: var(--surface); color: var(--ink-2);
  font-size: var(--t-sm); font-weight: 500; white-space: nowrap;
}
.segmented button:last-child { border-right: 0; }
.segmented button:hover { background: var(--surface-2); color: var(--ink); }
.segmented button[aria-pressed="true"] { background: var(--surface-3); color: var(--ink); font-weight: 600; }
.segmented .n { margin-left: 5px; font-variant-numeric: tabular-nums; color: var(--ink-3); font-weight: 500; }
.segmented button[aria-pressed="true"] .n { color: var(--ink-2); }

/* --------------------------------------------------------------- states --- */

/* First load only. A refresh never shows skeletons — replacing live figures
   with grey bars every ten seconds is how a working system is made to look
   broken. */
.skeleton { background: var(--surface-2); border-radius: var(--r-chip); animation: shimmer 1.4s ease-in-out infinite; }
@keyframes shimmer { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
.skeleton-row { height: var(--row); border-bottom: 1px solid var(--line-soft); display: flex; align-items: center; padding: 0 var(--s5); gap: var(--s6); }
.skeleton-row > i { display: block; height: 9px; border-radius: var(--r-pill); background: var(--surface-3); }

.empty { padding: var(--s11) var(--s7); text-align: center; }
.empty h3 { font-size: var(--t-lg); font-weight: 600; margin-bottom: var(--s3); }
.empty p { font-size: var(--t-sm); color: var(--ink-3); max-width: 46ch; margin: 0 auto var(--s6); }

.errbox {
  padding: var(--s6);
  background: var(--crit-bg); border: 1px solid var(--crit-line); border-radius: var(--r-panel);
  color: var(--crit-ink);
}
.errbox h4 { font-size: var(--t-base); font-weight: 700; margin-bottom: var(--s2); }
.errbox p { font-size: var(--t-sm); color: var(--ink-2); }
.errbox .code { font-family: var(--font-mono); font-size: 11.5px; }

/* --------------------------------------------------------------- toasts --- */

.toasts {
  position: fixed; bottom: var(--s7); left: 50%; transform: translateX(-50%);
  z-index: 90; display: flex; flex-direction: column-reverse; gap: var(--s4);
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  display: flex; align-items: center; gap: var(--s5);
  min-width: 280px; max-width: 520px;
  padding: var(--s5) var(--s6);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-panel);
  box-shadow: var(--shadow-pop);
  font-size: var(--t-base);
  animation: rise 0.18s cubic-bezier(0.2, 0.8, 0.3, 1);
}
@keyframes rise { from { opacity: 0; transform: translateY(6px); } }
.toast[data-tone="crit"] { border-color: var(--crit-line); background: var(--crit-bg); color: var(--crit-ink); }
.toast[data-tone="ok"] { border-color: var(--ok-line); background: var(--ok-bg); color: var(--ok-ink); }
.toast .msg { flex: 1; min-width: 0; }

/* --------------------------------------------------------------- dialog --- */

dialog {
  padding: 0; border: 1px solid var(--line); border-radius: var(--r-modal);
  background: var(--surface); color: var(--ink);
  box-shadow: var(--shadow-modal);
  width: min(520px, calc(100vw - 32px));
}
dialog::backdrop { background: rgba(16, 18, 21, 0.38); backdrop-filter: blur(2px); }
dialog .dlg-head { padding: var(--s7) var(--s7) var(--s5); }
dialog .dlg-head h2 { font-size: var(--t-lg); font-weight: 600; letter-spacing: -0.008em; }
dialog .dlg-head p { margin-top: var(--s3); font-size: var(--t-sm); color: var(--ink-2); }
dialog .dlg-body { padding: 0 var(--s7) var(--s6); display: flex; flex-direction: column; gap: var(--s6); }
dialog .dlg-foot {
  display: flex; justify-content: flex-end; gap: var(--s4);
  padding: var(--s5) var(--s7);
  border-top: 1px solid var(--line-soft); background: var(--surface-2);
}

/* ------------------------------------------------------------- palette ---- */

#palette { width: min(600px, calc(100vw - 32px)); }
#palette .dlg-body { padding: 0; }
.pal-input {
  width: 100%; height: 46px; padding: 0 var(--s7);
  border: 0; border-bottom: 1px solid var(--line);
  background: transparent; font-size: var(--t-body);
}
.pal-input:focus-visible { outline: none; }
.pal-list { max-height: 340px; overflow: auto; padding: var(--s3); }
.pal-group { padding: var(--s5) var(--s5) var(--s2); }
.pal-item {
  display: flex; align-items: center; gap: var(--s5);
  height: 32px; padding: 0 var(--s5); border-radius: var(--r);
  font-size: var(--t-base); color: var(--ink-2);
}
.pal-item[aria-selected="true"] { background: var(--accent-soft); color: var(--accent-ink); }
.pal-item .where { margin-left: auto; font-size: var(--t-meta); color: var(--ink-3); }
.pal-item[data-danger="true"] { color: var(--crit-ink); }

/* ------------------------------------------------------------ split view -- */

/* Replies and Prospects are list-plus-detail. The list keeps its own scroll so
   reading a long conversation never loses the operator's place in the queue. */
.split {
  display: grid; grid-template-columns: 368px minmax(0, 1fr);
  /* --offset is set by the shell when the dry-run banner is showing, so the
     two panes still end exactly at the viewport instead of pushing a second
     scrollbar onto the page. */
  height: calc(100vh - var(--topbar) - var(--offset, 0px));
  border-top: 1px solid var(--line);
}
.split > .list { border-right: 1px solid var(--line); overflow: auto; background: var(--surface); }
.split > .detail { overflow: auto; background: var(--bg); }

.row-lg {
  display: block; width: 100%; text-align: left;
  padding: var(--s5) var(--s6);
  border: 0; border-bottom: 1px solid var(--line-soft); border-left: 2px solid transparent;
  background: transparent;
}
.row-lg:hover { background: var(--surface-2); }
.row-lg[aria-selected="true"] { background: var(--accent-soft); border-left-color: var(--accent); }
.row-lg .l1 { display: flex; align-items: center; gap: var(--s4); }
.row-lg .who { font-weight: 600; font-size: var(--t-base); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row-lg .when { font-size: var(--t-meta); color: var(--ink-3); font-variant-numeric: tabular-nums; white-space: nowrap; }
.row-lg .l2 { margin-top: 3px; font-size: var(--t-sm); color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row-lg .l3 { margin-top: var(--s3); display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }

/* ---------------------------------------------------------- conversation -- */

.msg { padding: var(--s6) 0; border-bottom: 1px solid var(--line-soft); }
.msg-head { display: flex; align-items: baseline; gap: var(--s4); margin-bottom: var(--s4); flex-wrap: wrap; }
.msg-from { font-weight: 600; font-size: var(--t-base); }
.msg-dir {
  font-size: var(--t-micro); font-weight: 600; text-transform: uppercase; letter-spacing: var(--track-caps);
  color: var(--ink-3);
}
.msg-body {
  font-size: var(--t-read); line-height: 1.6; color: var(--ink);
  white-space: pre-wrap; word-break: break-word;
  max-width: 78ch;
}
.msg[data-dir="outbound"] .msg-body { color: var(--ink-2); }
.msg-quote { color: var(--ink-3); border-left: 2px solid var(--line); padding-left: var(--s5); margin-top: var(--s5); }

.kv { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--s3) var(--s6); font-size: var(--t-sm); }
.kv dt { color: var(--ink-3); white-space: nowrap; }
.kv dd { margin: 0; overflow: hidden; text-overflow: ellipsis; }

.stack { display: flex; flex-direction: column; gap: var(--s7); }

/* A whole row that is one link.
   An <a> rather than a div with onclick, so middle-click, cmd-click, Tab and
   a screen reader's link list all work — a "clickable row" that only responds
   to a left click is a row a keyboard user cannot reach. */
.row-link {
  display: block; color: inherit; text-decoration: none;
}
.row-link:hover { background: var(--surface-2); }
.row-link:focus-visible {
  outline: none; box-shadow: var(--focus); border-radius: var(--r);
  position: relative; z-index: 1;
}
.row-link:last-child { border-bottom: none !important; }
.rowx { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap; }
.muted { color: var(--ink-3); }

/* Present for a screen reader, absent for everyone else. Used for the page
   headings on split-view screens, where the visible title belongs to the
   selected record rather than to the page. */
.visually-hidden,
.visually-hidden-focusable:not(:focus) {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* The skip link: invisible until it is the thing you are on. */
.visually-hidden-focusable:focus {
  position: fixed; top: var(--s3); left: var(--s3); z-index: 100;
  width: auto; height: auto; margin: 0; padding: var(--s3) var(--s6);
  clip-path: none; background: var(--surface); border: 1px solid var(--accent);
  box-shadow: var(--shadow-pop);
}
.strong { font-weight: 600; }
.right { margin-left: auto; }
.trunc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --------------------------------------------------------------- spark ---- */

/* A sparkline earns its place only where the direction of travel is the
   question and a single number cannot express it: 14 days of send volume,
   replies and bounces. There are no other charts in this application. */
.spark { display: block; width: 100%; height: 38px; overflow: visible; }
.spark-empty {
  height: 38px; display: flex; align-items: center;
  font-size: var(--t-meta); color: var(--ink-3);
  border-bottom: 1px dashed var(--line);
}
.spark .axis { stroke: var(--line); stroke-width: 1; }
.spark .fill { fill: var(--accent-soft); }
.spark .line { fill: none; stroke: var(--accent); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }
.spark .line[data-tone="crit"] { stroke: var(--crit-ink); }
.spark .line[data-tone="ok"] { stroke: var(--ok-ink); }
.spark .dot { fill: var(--accent); }

/* --------------------------------------------------------------- login ---- */

/* Flex, not `grid; place-items: center`. Grid centres the item inside its
   track, and a single auto track is only stretched to full width by some
   engines when the item carries a percentage width — so the card centred
   vertically but sat at the left edge in Firefox. Flex has no track to
   size, so both axes behave the same everywhere. */
.login-wrap { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: var(--s7); }
.login {
  width: min(380px, 100%);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-panel);
  box-shadow: var(--shadow-pop);
  padding: var(--s9);
}
.login h1 { font-size: var(--t-lg); font-weight: 600; letter-spacing: -0.008em; }
.login p { margin-top: var(--s3); font-size: var(--t-sm); color: var(--ink-3); }
.login form { margin-top: var(--s8); display: flex; flex-direction: column; gap: var(--s5); }

/* ------------------------------------------------------------ responsive -- */

/* Real breakpoints for the two ways this is used: a laptop at 1280–1680, and
   a phone checked between meetings. There is no tablet layout because there is
   no tablet in this workflow. */
@media (max-width: 1180px) {
  .grid-main { grid-template-columns: minmax(0, 1fr); }
  .split { grid-template-columns: 300px minmax(0, 1fr); }
}

@media (max-width: 860px) {
  .topbar { height: auto; flex-wrap: wrap; padding: var(--s4) var(--s6); gap: var(--s4); row-gap: var(--s3); }
  .nav { order: 3; width: 100%; overflow-x: auto; scrollbar-width: none; margin-left: 0; padding-bottom: 2px; }
  .nav::-webkit-scrollbar { display: none; }
  .topbar-right { gap: var(--s4); }
  .freshness, .kbd-hint { display: none; }
  .page { padding: var(--s6) var(--s5) var(--s10); }
  .grid-2, .grid-3, .grid-4 { grid-template-columns: minmax(0, 1fr); }
  .figures > * { flex-basis: 50%; border-bottom: 1px solid var(--line-soft); }
  /* The list becomes the page; opening a conversation replaces it, which is
     the only honest way to show two panes on a 390px screen. */
  .split { grid-template-columns: minmax(0, 1fr); height: auto; }
  .split > .list { border-right: 0; }
  .split[data-open="true"] > .list { display: none; }
  .split:not([data-open="true"]) > .detail { display: none; }
}

/* ----------------------------------------------------------- narrow ---- */

@media (max-width: 880px) {
  #root { flex-direction: column; }
  .sidebar {
    width: auto; height: auto; position: static;
    flex-direction: row; align-items: center; gap: var(--s5);
    border-right: 0; border-bottom: 1px solid var(--line);
    /* nowrap + scroll, NOT wrap. Wrapping put the theme toggle and the user
       chip on a line ABOVE the navigation, which is both ugly and wrong: the
       nav is what this bar is for. */
    flex-wrap: nowrap; overflow-x: auto; padding: var(--s4) var(--s5);
  }
  .sidebar .nav { flex-direction: row; gap: var(--s1); flex: none; }
  .sidebar .nav a { height: 28px; }
  .side-foot {
    margin-top: 0; margin-left: auto; flex-direction: row; flex-wrap: nowrap;
    align-items: center; padding-top: 0; flex: none;
  }
  .side-foot .btn { width: auto; }
  /* The brand truncated to "Acq…" and told nobody anything; the workspace
     chip beside it already carries the identity. */
  .sidebar .brand-name { display: none; }
  .side-ws { max-width: 160px; flex: none; }
  /* ⌘K still opens the palette; the button is the first thing to go. */
  .side-foot .kbd-hint, .side-foot .freshness { display: none; }
}
