/* Echova — Agency Control System
   ECOVA visual identity, 2026-08 rebrand: near-black editorial luxury.
   A quiet, ink-dark ground; warm paper-white type; a serif display face for
   every heading and figure; and ONE chroma — champagne brass — spent only on
   the marks that must be found (active nav, live rules, key accents).
   The previous indigo→wine cosmic theme is gone: no gradient text, no glowing
   buttons, no drifting orbs. Restraint is the brand now. */

/* ─── Display face — self-hosted, works offline ─────────────────────
   DM Serif Display (OFL) ships in Echova/assets/fonts. It carries 333
   glyphs and NO Arabic, so `unicode-range` pins it to Latin and common
   punctuation: every Arabic codepoint skips this face entirely and binds
   to the Arabic family next in --font-display. Declaring a range wider
   than the font's real coverage is safe — the browser still falls through
   per character for anything the face lacks. */
@font-face {
  font-family: "DM Serif Display";
  src: url("assets/fonts/DMSerifDisplay-Regular.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-024F, U+2000-206F, U+2070-209F, U+20A0-20BF,
                 U+2190-21FF, U+2200-22FF, U+FB00-FB4F;
}
@font-face {
  font-family: "DM Serif Display";
  src: url("assets/fonts/DMSerifDisplay-Italic.ttf") format("truetype");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-024F, U+2000-206F, U+2070-209F, U+20A0-20BF,
                 U+2190-21FF, U+2200-22FF, U+FB00-FB4F;
}

:root {
  /* Surfaces — near-black, warm-neutral. The ground carries no hue at all;
     what warmth the UI has comes from the type and the single accent. */
  --bg-0: #0a0a0a;         /* base canvas */
  --bg-1: #121212;         /* sidebar, cards — the lifted plane */
  --bg-2: #1b1b1b;         /* inputs, hover */
  --bg-3: #242424;         /* active states */
  --bg-4: #303030;         /* pressed */
  --bg-hover: #202020;

  /* Borders — plain white at low alpha. Hairlines, never colour. */
  --border-1: rgba(255, 255, 255, 0.07);
  --border-2: rgba(255, 255, 255, 0.12);
  --border-3: rgba(255, 255, 255, 0.22);

  /* Surface aliases — used by page-strategy.jsx (kept for forward-compat) */
  --surface-1: var(--bg-1);
  --surface-2: var(--bg-2);
  --border:    var(--border-2);

  /* Short aliases. Six JSX call sites read var(--acc, …) and var(--line, …),
     and NEITHER was ever defined — so every one of them had been rendering
     its hardcoded fallback instead of the theme: a #adadad blue for --acc
     (api.jsx's confirm modal, brand-cards.jsx, page-strategy.jsx) and, in
     page-stubs.jsx, a #eee rule — a near-WHITE hairline drawn across a
     near-black page. Defining them here fixes all six at the root and keeps
     them following the theme from now on. */
  --acc:  var(--accent);
  --line: var(--border-2);

  /* Text — warm off-white hierarchy (paper, not screen-white).
     --text-3 measures 5.06:1 on --bg-0, so muted labels clear WCAG AA;
     the old purple theme's --text-3 sat at 4.52:1, barely over the line. */
  --text-0: #f3f3f3;
  --text-1: #d7d7d7;
  --text-2: #a1a1a1;
  --text-3: #808080;
  --text-4: #464646;

  /* Primary accent — champagne brass, lifted from the ECOVA logotype.
     This is the only chroma in the chrome; spend it sparingly. */
  --accent: #a3a3a3;
  --accent-2: #d1d1d1;
  --accent-soft: rgba(163, 163, 163, 0.12);
  --accent-line: rgba(163, 163, 163, 0.34);
  --accent-glow: 0 0 0 1px rgba(163, 163, 163, 0.34);

  /* Kept because rules and JSX still reference it — but it is now a quiet
     brass ramp, not the old cyan→magenta sweep. */
  --accent-gradient: linear-gradient(135deg,
                       #e4e4e4 0%,
                       #a3a3a3 55%,
                       #818181 100%);

  /* Inverted surface — the light pill the screenshot uses for the one
     primary action on a page ("Onboard client"). Light on dark, dark text. */
  --invert-bg: #eeeeee;
  --invert-bg-hover: #ffffff;
  --invert-fg: #131313;

  /* AI activity / live — muted slate. Reads as "machine", stays out of the
     champagne's way so the two never compete for the eye. */
  --ai: #b3b3b3;
  --ai-2: #989898;
  --ai-soft: rgba(179, 179, 179, 0.13);

  /* Status — desaturated into the editorial register but still plainly
     distinguishable. These are semantic; muting them to invisibility would
     be a usability regression, not restraint. */
  --ok: #9c9c9c;
  --ok-soft: rgba(156, 156, 156, 0.13);
  --warn: #8d8d8d;
  --warn-soft: rgba(141, 141, 141, 0.13);
  --err: #d9705f;
  --err-soft: rgba(217, 112, 95, 0.14);
  /* A deeper oxblood for SOLID destructive fills. --err is tuned to be read
     as text on a dark ground; white text on it measures only 3.2:1, so a
     filled button needs its own darker value (5.4:1 against white). */
  --err-strong: #b8452f;
  --info: #ababab;
  --info-soft: rgba(171, 171, 171, 0.13);

  /* Wordmark — the champagne tone from the ECOVA logotype */
  --wordmark: #dbdbdb;

  /* Type
     --font-display: DM Serif Display covers Latin only (verified: 333
     glyphs, zero in U+0600-06FF). Arabic headings therefore fall to Dubai —
     the Emirate's official typeface, installed system-wide on this machine
     and so available OFFLINE — then Sakkal Majalla, then the app's existing
     IBM Plex Sans Arabic. Latin serif + Arabic humanist sans is a deliberate
     pairing: no serif Arabic on the box was worth gambling the Arabic UI on.
     --font-sans keeps IBM Plex Sans Arabic first (the app's established,
     tested body face) with Dubai behind it as the offline fallback. */
  --font-sans: "Inter", "IBM Plex Sans Arabic", "Dubai", "Segoe UI",
               "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-display: "DM Serif Display", "Dubai", "Sakkal Majalla",
                  "IBM Plex Sans Arabic", Georgia, "Times New Roman", serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", "Cascadia Mono", Menlo, monospace;

  /* Layout
     --sidebar-w went 232→248: the screenshot's own sidebar wrapped
     "Approval queue" onto two lines. Truncation (below) is the guarantee;
     the extra 16px is what stops it having to fire on ordinary labels. */
  --sidebar-w: 248px;
  /* Stays 52px. The rebrand wanted a slightly airier bar, but the topbar
     height is load-bearing OUTSIDE this file: page-chat.jsx sizes itself
     with a hardcoded calc(100vh - 52px), and several other pages carry
     compound offsets (70/140/200/220/280px) that fold the same 52 in.
     Four cosmetic pixels are not worth silently desynchronising them.
     --sidebar-w has no such dependants -- nothing hardcodes 232 -- so the
     width that fixes the wrapping labels is safe to take. */
  --topbar-h: 52px;
  --radius-1: 6px;
  --radius-2: 10px;
  --radius-3: 14px;
  --radius-4: 18px;

  /* Motion */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-snap: cubic-bezier(0.3, 0.7, 0.4, 1);
  --t-fast: 0.14s;
  --t-base: 0.22s;
  --t-slow: 0.38s;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  /* Flat ink. The cosmic radial glows are gone — the rebrand's ground is
     one uninterrupted near-black so the type and the hairlines carry the
     whole composition. */
  background: var(--bg-0);
  color: var(--text-1);
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  font-feature-settings: "kern", "liga";
  overflow: hidden;
}

/* The drifting constellation and the floating orbs (body::before /
   body::after) are retired with the purple theme. The selectors are kept,
   inert, because `constellationDrift` and `orbsDrift` are still referenced
   further down and a dangling animation name costs nothing but confusion. */
body::before { content: none; }
@keyframes constellationDrift {
  0%   { transform: translate(0, 0); }
  100% { transform: translate(-12px, 18px); }
}
.app { position: relative; z-index: 1; }

button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; padding: 0; }
input, textarea, select { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }

/* App layout */
.app {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  grid-template-rows: var(--topbar-h) 1fr;
  grid-template-areas: "sidebar topbar" "sidebar main";
  height: 100vh;
  width: 100vw;
  transition: grid-template-columns 0.18s ease;
}
.app.sidebar-collapsed { --sidebar-w: 60px; }
/* Mercury's route hides the app topbar; the grid must give that row's height
   back to the conversation instead of leaving a 52px hole. */
.app.chat-focus { --topbar-h: 0px; }
.app.chat-focus .topbar { display: none; }

/* Team-chat: the same-author timestamp gutter. page-chat.jsx renders it with
   a 0.12s opacity transition inside .chat-msg-row, plainly built for a hover
   reveal — and neither class had a single rule anywhere, so the element
   occupied its 40px column permanently invisible. The row's own JS handlers
   only ever touched `background`. */
.chat-msg-row .msg-ts-gutter { opacity: 0; }
.chat-msg-row:hover .msg-ts-gutter { opacity: 1; }

/* Sidebar */
.sidebar {
  grid-area: sidebar;
  background: var(--bg-1);
  border-right: 1px solid var(--border-1);
  display: flex;
  flex-direction: column;
  padding: 14px 10px 10px;
  gap: 4px;
  overflow-y: auto;
  overflow-x: hidden;
  transition: padding 0.18s ease;
}
.sidebar.collapsed { padding: 14px 6px 10px; }
.sidebar::-webkit-scrollbar { width: 6px; }
.sidebar::-webkit-scrollbar-thumb { background: var(--bg-3); border-radius: 3px; }

.brand {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 8px 18px;
  position: relative;
}
.sidebar.collapsed .brand {
  padding: 6px 4px 14px;
  flex-direction: column;
  gap: 6px;
}
.brand-text { flex: 1; min-width: 0; }

/* Sidebar collapse/expand toggle */
.sidebar-toggle {
  width: 24px;
  height: 24px;
  border-radius: 6px;
  background: transparent;
  border: 1px solid var(--border-1);
  color: var(--text-3);
  display: grid;
  place-items: center;
  cursor: pointer;
  margin-left: auto;
  flex-shrink: 0;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
  padding: 0;
}
.sidebar-toggle:hover {
  background: var(--accent-soft);
  color: var(--ai);
  border-color: var(--accent-line);
}
.sidebar.collapsed .sidebar-toggle {
  margin: 4px auto 0;
}
/* Brand mark — the ECOVA mandala (the logotype the rebrand screenshot puts
   in the sidebar, wordmark and all), set in a dark rounded tile behind a
   hairline ring. Partially desaturated so it sits inside the monochrome
   sidebar instead of shouting across it; no glow, no pulse. */
.brand-mark {
  width: 34px; height: 34px;
  border-radius: 9px;
  background-image: url("logo-mandala.png?v=3");
  background-size: cover;
  background-position: center;
  background-color: #0f0f0f;
  box-shadow: inset 0 0 0 1px var(--border-2);
  position: relative;
  flex-shrink: 0;
  filter: saturate(0.55) contrast(1.05);
  transition: filter var(--t-base) var(--ease-out),
              transform var(--t-base) var(--ease-out);
}
.brand-mark:hover {
  filter: saturate(0.9) contrast(1.05);
  transform: scale(1.03);
}
/* Serif wordmark, flat warm white. The gradient text-clip is gone: the
   rebrand's brand name is a typeface, not a colour effect. */
.brand-name {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 19px;
  line-height: 1.15;
  color: var(--text-0);
  letter-spacing: 0;
  -webkit-text-fill-color: currentColor;
}
.brand-sub {
  font-size: 9.5px;
  color: var(--text-3);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  margin-top: 3px;
}

.nav-section { margin-top: 16px; }
.nav-label {
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  /* --text-3, not --text-4. These are the group headings the owner reads to
     find a page; --text-4 is the decorative tier (breadcrumb separators) and
     measures 2.11:1 on the new ground, which is not a label. */
  color: var(--text-3);
  padding: 6px 10px;
}
.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;
  border-radius: var(--radius-1);
  color: var(--text-2);
  font-size: 12.5px;
  cursor: pointer;
  position: relative;
  min-width: 0;                 /* lets the label shrink instead of wrapping */
  transition: background 0.12s, color 0.12s;
}
/* ── The wrapping-label defect ─────────────────────────────────────────
   The rebrand screenshot shows "Approval queue", "Mercury holds", "Talk to
   Mercury" and "Marketplace listings" each broken across two lines, which
   ragged the whole sidebar. A nav label is a single line by definition:
   it may shrink and it may truncate, but it may not wrap. shell.jsx now
   also carries title={it.label} unconditionally, so a truncated label
   still says its full name on hover. */
.nav-item > span:not(.badge):not(.nav-dot):not(.pulse) {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.nav-item:hover { background: var(--bg-2); color: var(--text-1); }
.nav-item.active {
  background: var(--bg-3);
  color: var(--text-0);
}
.nav-item.active::before {
  content: "";
  position: absolute;
  left: -10px; top: 6px; bottom: 6px;
  width: 2px;
  background: var(--accent);
  border-radius: 2px;
}
.nav-item svg { width: 15px; height: 15px; flex-shrink: 0; }
.nav-item .badge {
  margin-left: auto;
  flex-shrink: 0;
  font-size: 10px;
  padding: 1px 6px;
  border-radius: 8px;
  background: var(--bg-4);
  color: var(--text-2);
}
.nav-item .badge.ai { background: var(--ai-soft); color: var(--ai); }

/* Marked nav item (.glow) — the AI Studio / generative entries.
   It still has to read as "different from the rest of the list", but the
   pulsing magenta neon it used to be is the loudest thing the old theme
   owned and cannot survive into a near-black editorial skin. The mark is
   now the brass accent on the ICON plus a warmer label — quiet, steady,
   and legible. Nothing animates: a sidebar that throbs on seven rows at
   once is noise, not emphasis. */
.nav-item.glow {
  background: transparent;
  border: none;
  box-shadow: none;
  color: var(--text-1);
  text-shadow: none;
  animation: none;
}
.nav-item.glow svg {
  color: var(--accent);
  filter: none;
  animation: none;
}
.nav-item.glow:hover {
  background: var(--bg-2);
  color: var(--text-0);
  text-shadow: none;
}
.nav-item.glow.active {
  background: var(--bg-3);
  color: var(--text-0);
  text-shadow: none;
}
.nav-item.glow.active svg { color: var(--accent-2); }

/* Collapsed sidebar — icons only */
.sidebar.collapsed .nav-item {
  justify-content: center;
  padding: 9px 0;
  position: relative;
}
.sidebar.collapsed .nav-item span:not(.nav-dot):not(.pulse) { display: none; }
.sidebar.collapsed .nav-section { margin-top: 8px; }
.sidebar.collapsed .nav-label { display: none; }
.sidebar.collapsed .nav-item.active::before {
  left: -6px;
  top: 6px;
  bottom: 6px;
}
/* small dot indicator in collapsed mode */
.nav-dot {
  position: absolute;
  top: 7px; right: 9px;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 2px var(--bg-1);
}
.nav-dot.ai { background: var(--ai); box-shadow: 0 0 0 2px var(--bg-1); }

/* Tooltip on hover in collapsed mode */
.sidebar.collapsed .nav-item[data-label]:hover::after {
  content: attr(data-label);
  position: absolute;
  left: calc(100% + 12px);
  top: 50%;
  transform: translateY(-50%);
  background: var(--bg-1);
  border: 1px solid var(--border-1);
  padding: 5px 9px;
  border-radius: 6px;
  font-size: 11.5px;
  color: var(--text-0);
  white-space: nowrap;
  z-index: 50;
  box-shadow: 0 6px 18px rgba(0,0,0,0.4);
  pointer-events: none;
}

.sidebar.collapsed .workspace-switch { justify-content: center; padding: 8px 4px; }

.workspace-switch {
  margin-top: auto;
  padding: 8px;
  border-top: 1px solid var(--border-1);
  display: flex;
  align-items: center;
  gap: 9px;
  cursor: pointer;
}
.workspace-switch:hover { background: var(--bg-2); border-radius: var(--radius-2); }
/* Neutral tile — the old #a6a6a6→pink gradient was the brightest object in
   the sidebar and the screenshot's is plain graphite. */
.ws-avatar {
  width: 28px; height: 28px; border-radius: 7px;
  background: var(--bg-3);
  border: 1px solid var(--border-2);
  display: grid; place-items: center;
  font-weight: 600; font-size: 10.5px; color: var(--text-1);
  letter-spacing: 0.04em;
}
.ws-name { font-size: 12px; color: var(--text-0); font-weight: 500; }
.ws-role { font-size: 10.5px; color: var(--text-3); }

/* Topbar */
.topbar {
  grid-area: topbar;
  background: var(--bg-1);
  border-bottom: 1px solid var(--border-1);
  display: flex;
  align-items: center;
  padding: 0 18px;
  gap: 16px;
}
.crumbs { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--text-2); }
.crumbs .sep { color: var(--text-4); }
.crumbs .current { color: var(--text-0); font-weight: 500; }
.search {
  margin-left: 20px;
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--bg-2);
  border: 1px solid var(--border-1);
  border-radius: 999px;
  padding: 6px 14px;
  width: 320px;
  font-size: 12px;
  color: var(--text-3);
}
.search:focus-within { border-color: var(--border-3); }
.search input { background: none; border: none; outline: none; flex: 1; color: var(--text-0); font-size: 12px; }
.search kbd {
  font-family: var(--font-mono); font-size: 10px;
  background: var(--bg-3); border: 1px solid var(--border-2);
  padding: 1px 5px; border-radius: 4px; color: var(--text-3);
}
.topbar-actions { margin-left: auto; display: flex; align-items: center; gap: 4px; }
.icon-btn {
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border-radius: var(--radius-1);
  color: var(--text-2);
  position: relative;
}
.icon-btn:hover { background: var(--bg-3); color: var(--text-0); }
.icon-btn .dot {
  position: absolute; top: 7px; right: 7px;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--err);
  box-shadow: 0 0 6px var(--err);
}
.user-chip {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 10px 4px 4px;
  border-radius: var(--radius-2);
  background: var(--bg-2);
  border: 1px solid var(--border-1);
  margin-left: 6px;
}
.user-chip:hover { background: var(--bg-3); }
/* Graphite disc, not a cyan→violet gradient. The initials carry it. */
.user-avatar {
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--bg-4);
  border: 1px solid var(--border-2);
  display: grid; place-items: center;
  font-size: 10px; font-weight: 600; color: var(--text-1);
  letter-spacing: 0.03em;
}
.user-chip-name { font-size: 12px; color: var(--text-0); }

/* Main */
.main {
  grid-area: main;
  background: var(--bg-0);
  overflow-y: auto;
  position: relative;
}
.main::-webkit-scrollbar { width: 8px; }
.main::-webkit-scrollbar-thumb { background: var(--bg-3); border-radius: 4px; }

.page { padding: 26px 32px 48px; max-width: 1480px; margin: 0 auto; }
.page-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  margin-bottom: 24px;
  gap: 16px;
}
/* Editorial display heading — the serif, at size, in warm white. */
.page-title {
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 400;
  color: var(--text-0);
  letter-spacing: -0.005em;
  line-height: 1.18;
}
.page-sub { font-size: 12.5px; color: var(--text-2); margin-top: 6px; }
.page-actions { display: flex; gap: 8px; align-items: center; }

/* .page-head — the header wrapper page-adjudication.jsx and page-studio.jsx
   render. It had NO rule anywhere: both pages shipped an unstyled <h1> and a
   flush paragraph. Given a definition here so nothing the live app uses is
   undefined. */
.page-head { margin-bottom: 22px; }
.page-head h1 {
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 400;
  color: var(--text-0);
  letter-spacing: -0.005em;
  line-height: 1.18;
  margin: 0 0 6px;
}
.page-head p { margin: 0; font-size: 12.5px; max-width: 78ch; }

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: var(--radius-1);
  font-size: 12px;
  font-weight: 500;
  border: 1px solid transparent;
  transition: all 0.12s;
  white-space: nowrap;
}
.btn svg { width: 14px; height: 14px; }
/* Primary = the light pill. The screenshot's one prominent action per page
   ("Onboard client") is near-white with dark text — inverted against the ink
   ground. That inversion is the rebrand's loudest permitted gesture, and it
   replaces the old glowing magenta gradient outright. */
.btn.primary {
  background: var(--invert-bg);
  color: var(--invert-fg);
  border-color: transparent;
  font-weight: 600;
}
.btn.primary:hover { background: var(--invert-bg-hover); color: var(--invert-fg); }
.btn.ghost {
  background: var(--bg-2);
  border-color: var(--border-2);
  color: var(--text-1);
}
.btn.ghost:hover { background: var(--bg-3); border-color: var(--border-3); }
.btn.subtle { color: var(--text-2); padding: 6px 8px; }
.btn.subtle:hover { background: var(--bg-2); color: var(--text-0); }

/* .btn.small — used in 8 files and defined in none of them. Small buttons
   have been rendering at full size across the app. */
.btn.small {
  padding: 3px 9px;
  font-size: 11px;
  gap: 4px;
  border-radius: 5px;
}
.btn.small svg { width: 12px; height: 12px; }

/* .btn-warn — page-stubs.jsx toggles loop kill-switches with
   className={on ? 'btn btn-warn' : 'btn'}. The variant was never defined, so
   the ON state looked identical to OFF: a safety control whose state you
   could not read. It is now unmistakably #b0b0b0. */
.btn.btn-warn,
.btn-warn {
  background: var(--warn-soft);
  border-color: var(--accent-line);
  color: var(--warn);
  font-weight: 600;
}
.btn.btn-warn:hover,
.btn-warn:hover { background: rgba(141, 141, 141, 0.2); color: #a6a6a6; }

/* Cards */
.card {
  background: var(--bg-1);
  border: 1px solid var(--border-1);
  border-radius: var(--radius-3);
  overflow: hidden;
}
.card-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border-1);
}
.card-title {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-0);
  display: flex; align-items: center; gap: 8px;
}
.card-body { padding: 14px 16px; }

/* Stat cards — the lifted plane the screenshot floats on the ink ground:
   a touch lighter than the page, a hairline ring, a muted label and a big
   serif figure. */
.stat-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; margin-bottom: 22px; }
.stat {
  background: var(--bg-1);
  border: 1px solid var(--border-2);
  border-radius: var(--radius-3);
  padding: 16px 18px 18px;
  position: relative;
  overflow: hidden;
}
.stat-label {
  font-size: 11px;
  color: var(--text-2);
  display: flex;
  align-items: center;
  gap: 6px;
  line-height: 1.35;
  min-height: 2.7em;          /* keeps 1- and 2-line labels on one baseline */
}
.stat-value {
  font-family: var(--font-display);
  font-size: 32px;
  font-weight: 400;
  color: var(--text-0);
  margin-top: 8px;
  letter-spacing: -0.01em;
  line-height: 1.05;
}
.stat-trend { display: inline-flex; align-items: center; gap: 3px; font-size: 11px; margin-top: 4px; padding: 2px 6px; border-radius: 4px; }
.stat-trend.up { color: var(--ok); background: var(--ok-soft); }
.stat-trend.down { color: var(--err); background: var(--err-soft); }
.stat-spark { position: absolute; right: 12px; bottom: 12px; opacity: 0.7; }

/* Pulse */
.pulse {
  display: inline-block;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--ok);
  position: relative;
}
.pulse::after {
  content: "";
  position: absolute; inset: -3px;
  border-radius: 50%;
  border: 1px solid var(--ok);
  animation: pulseRing 1.6s ease-out infinite;
}
.pulse.ai { background: var(--ai); }
.pulse.ai::after { border-color: var(--ai); }
.pulse.warn { background: var(--warn); }
.pulse.warn::after { border-color: var(--warn); }
@keyframes pulseRing {
  0% { transform: scale(0.7); opacity: 1; }
  100% { transform: scale(2); opacity: 0; }
}

/* Pills */
.pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 500;
  background: var(--bg-3);
  color: var(--text-2);
  border: 1px solid var(--border-2);
  white-space: nowrap;
}
/* Status pills. The four border colours here were hardcoded leftovers from a
   theme two rebrands ago (a #727272 green, a #a3a3a3 #b0b0b0…) that no longer
   matched their own fill. Each now derives from its status token. */
.pill.ai { background: var(--ai-soft); color: var(--ai); border-color: rgba(179, 179, 179, 0.30); }
.pill.ok { background: var(--ok-soft); color: var(--ok); border-color: rgba(156, 156, 156, 0.30); }
.pill.warn { background: var(--warn-soft); color: var(--warn); border-color: rgba(141, 141, 141, 0.30); }
.pill.err { background: var(--err-soft); color: var(--err); border-color: rgba(217, 112, 95, 0.30); }
.pill.info { background: var(--info-soft); color: var(--info); border-color: rgba(171, 171, 171, 0.30); }

/* Chips — interactive pills used in filter rows, KPI tags, mention tokens */
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: 999px;
  font-size: 10.5px; font-weight: 500;
  background: var(--bg-3); color: var(--text-1);
  border: 1px solid var(--border-2);
  white-space: nowrap;
  transition: background var(--t-fast, 150ms) ease,
              border-color var(--t-fast, 150ms) ease,
              color var(--t-fast, 150ms) ease;
}
.chip.clickable { cursor: pointer; }
.chip:hover { background: var(--bg-4); border-color: var(--border-3); color: var(--text-0); }
.chip.active {
  background: var(--accent-soft);
  border-color: var(--accent-line);
  color: var(--text-0);
}
.chip.muted { background: transparent; border-color: var(--border-1); color: var(--text-3); }

/* Avatars */
.avatar {
  width: 24px; height: 24px;
  border-radius: 50%;
  display: grid; place-items: center;
  font-size: 10px; font-weight: 600; color: white;
  flex-shrink: 0;
}
.avatar.sm { width: 20px; height: 20px; font-size: 9px; }
.avatar.lg { width: 36px; height: 36px; font-size: 12.5px; }
/* Agent avatars read as graphite discs in the rebrand, not violet gradients —
   the screenshot's activity feed is a column of neutral marks. */
.avatar.bot {
  background: var(--bg-3);
  color: var(--text-1);
  position: relative;
  box-shadow: inset 0 0 0 1px var(--border-2);
}
.avatar-stack { display: flex; }
.avatar-stack > * { margin-left: -6px; box-shadow: 0 0 0 2px var(--bg-1); border-radius: 50%; }
.avatar-stack > *:first-child { margin-left: 0; }

/* Tables */
.table { width: 100%; border-collapse: collapse; font-size: 12px; }
.table th {
  text-align: left;
  font-weight: 500;
  color: var(--text-3);
  font-size: 10.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 9px 14px;
  border-bottom: 1px solid var(--border-1);
  background: var(--bg-1);
  position: sticky; top: 0;
}
.table td {
  padding: 11px 14px;
  border-bottom: 1px solid var(--border-1);
  color: var(--text-1);
  vertical-align: middle;
}
.table tr:hover td { background: var(--bg-2); }
.table tr:last-child td { border-bottom: none; }

/* Tabs */
.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border-1); margin-bottom: 16px; }
.tab {
  padding: 9px 14px;
  font-size: 12.5px;
  color: var(--text-3);
  border-bottom: 2px solid transparent;
  cursor: pointer;
  margin-bottom: -1px;
  transition: color 0.12s;
}
.tab:hover { color: var(--text-1); }
.tab.active { color: var(--text-0); border-bottom-color: var(--accent); }

/* Segmented */
.segmented {
  display: inline-flex;
  background: var(--bg-2);
  border: 1px solid var(--border-1);
  border-radius: var(--radius-1);
  padding: 2px;
  gap: 1px;
}
.segmented button {
  padding: 4px 10px;
  font-size: 11.5px;
  color: var(--text-3);
  border-radius: 4px;
}
.segmented button:hover { color: var(--text-1); }
.segmented button.active { background: var(--bg-4); color: var(--text-0); }

/* Glow effect for AI elements */
.ai-glow {
  position: relative;
}
.ai-glow::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  background: linear-gradient(135deg, transparent, var(--accent-line), transparent);
  z-index: -1;
}

/* Drawer */
.drawer-backdrop {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.45);
  backdrop-filter: blur(2px);
  z-index: 50;
}
.drawer {
  position: fixed;
  top: 0; right: 0;
  height: 100vh;
  width: 520px;
  background: var(--bg-1);
  border-left: 1px solid var(--border-1);
  z-index: 51;
  overflow-y: auto;
  animation: slideIn 0.22s ease-out;
}
@keyframes slideIn { from { transform: translateX(100%); } to { transform: translateX(0); } }

/* Scrollbar */
.main *::-webkit-scrollbar { width: 8px; height: 8px; }
.main *::-webkit-scrollbar-thumb { background: var(--bg-3); border-radius: 4px; }
.main *::-webkit-scrollbar-thumb:hover { background: var(--bg-4); }

/* Utility */
.row { display: flex; align-items: center; gap: 8px; }
.col { display: flex; flex-direction: column; gap: 8px; }
.muted { color: var(--text-3); }
.mono { font-family: var(--font-mono); }
.divider { height: 1px; background: var(--border-1); margin: 12px 0; }

/* Kanban */
.kanban {
  display: grid;
  grid-template-columns: repeat(6, minmax(230px, 1fr));
  gap: 10px;
  height: calc(100vh - 200px);
  overflow-x: auto;
  padding-bottom: 4px;
  scroll-padding: 8px;
}
.kanban-col {
  background: var(--bg-1);
  border: 1px solid var(--border-1);
  border-radius: var(--radius-2);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-width: 0;
}
.kanban-col-head {
  padding: 9px 11px;
  display: flex; align-items: center; justify-content: space-between;
  border-bottom: 1px solid var(--border-1);
  background: var(--bg-2);
  position: sticky; top: 0; z-index: 1;
}
.kanban-col-title {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-0);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.kanban-col-count { font-size: 10.5px; color: var(--text-3); background: var(--bg-3); padding: 1px 6px; border-radius: 8px; flex-shrink: 0; }
.kanban-list {
  flex: 1;
  overflow-y: auto;
  padding: 7px;
  display: flex; flex-direction: column; gap: 7px;
}
.kanban-card {
  background: var(--bg-2);
  border: 1px solid var(--border-2);
  border-radius: var(--radius-2);
  padding: 9px 10px;
  cursor: grab;
  transition: background 0.15s, border-color 0.15s, transform 0.15s;
  min-width: 0;
}
.kanban-card:hover { background: var(--bg-3); border-color: var(--border-3); transform: translateY(-1px); }
.kanban-card.ai { border-left: 2px solid var(--accent); padding-left: 9px; }
.kanban-card.dragging { opacity: 0.4; }
.kanban-card .row { min-width: 0; }
/* When sidebar is collapsed there's more room — give columns a bit more breathing */
.app.sidebar-collapsed .kanban { grid-template-columns: repeat(6, minmax(260px, 1fr)); gap: 12px; }

/* Glow border for live AI */
.live-border {
  position: relative;
  border: 1px solid var(--border-2);
}
.live-border::after {
  content: "";
  position: absolute; inset: -1px;
  border-radius: inherit;
  border: 1px solid var(--accent);
  opacity: 0;
  animation: liveBlink 2.4s ease-in-out infinite;
  pointer-events: none;
}
@keyframes liveBlink {
  0%, 100% { opacity: 0; }
  50% { opacity: 0.6; box-shadow: 0 0 12px -2px var(--accent); }
}

/* Misc */
.sr { position: absolute; left: -9999px; }


/* ════════════════════════════════════════════════════════════════════
   ECOVA REBRAND — editorial polish layer (2026-08)
   Loaded AFTER all the legacy rules so it cleanly overrides where it
   needs to. This layer used to be where the theme spent its chroma:
   gradient buttons, gradient page titles, a magenta glow on the active
   nav rule, a 3px animated rainbow strip across the top of every page,
   and two full-viewport drifting orb fields. The rebrand keeps the
   MOTION (page enter, hover lift, focus rings) and retires the colour.
   Every selector the old layer defined still exists here, so nothing
   the live app references has become undefined.
   ═══════════════════════════════════════════════════════════════════ */

/* ─── Page enter animation ───────────────────────────────────────── */
.main {
  animation: pageEnter var(--t-slow) var(--ease-out);
}
@keyframes pageEnter {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ─── Active nav item — one thin brass rule, no glow ─────────────── */
.nav-item.active::before {
  background: var(--accent);
  box-shadow: none;
  width: 2px;
  border-radius: 2px;
}
.nav-item.active {
  background: var(--bg-3);
  color: var(--text-0);
}
.nav-item { transition: background var(--t-fast) var(--ease-out),
                       color var(--t-fast) var(--ease-out),
                       transform var(--t-fast) var(--ease-out); }
.nav-item:not(.active):hover { transform: translateX(2px); }

/* ─── Buttons ────────────────────────────────────────────────────── */
.btn {
  transition: transform var(--t-fast) var(--ease-out),
              box-shadow var(--t-fast) var(--ease-out),
              border-color var(--t-fast) var(--ease-out),
              background var(--t-fast) var(--ease-out);
}
/* The light inverted pill, restated here so it wins over where the old
   gradient rule sat in the cascade. */
.btn.primary {
  background: var(--invert-bg);
  color: var(--invert-fg);
  border: 1px solid transparent;
  box-shadow: none;
}
.btn.primary:hover {
  background: var(--invert-bg-hover);
  color: var(--invert-fg);
  transform: translateY(-1px);
  box-shadow: 0 6px 18px -8px rgba(0, 0, 0, 0.8);
}
.btn.primary:active {
  transform: translateY(0);
  box-shadow: none;
}
/* Secondary hover: lift the surface, brighten the border. It used to wash
   the whole button in accent-soft, which on a monochrome ground read as a
   second primary button competing with the real one. */
.btn:not(.primary):hover {
  border-color: var(--border-3);
  background: var(--bg-3);
  color: var(--text-0);
}

/* ─── Cards — subtle lift on hover, neutral shadow ───────────────── */
.card {
  transition: transform var(--t-base) var(--ease-out),
              border-color var(--t-base) var(--ease-out),
              box-shadow var(--t-base) var(--ease-out);
}
.card:hover:not(.dragging) {
  transform: translateY(-2px);
  border-color: var(--border-3);
  box-shadow: 0 16px 40px -14px rgba(0, 0, 0, 0.85);
}

/* ─── Inputs — focused get a brass ring ──────────────────────────── */
input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--accent) !important;
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* .input — page-adjudication.jsx and page-clients.jsx render
   className="input" on text fields. There was no such rule anywhere in the
   app, so those fields fell back to the browser default: a white box with
   black text, sitting on a near-black page. */
.input {
  background: var(--bg-2);
  border: 1px solid var(--border-2);
  border-radius: var(--radius-1);
  color: var(--text-0);
  padding: 6px 10px;
  font-size: 12px;
  font-family: inherit;
  outline: none;
  transition: border-color var(--t-fast) var(--ease-out),
              box-shadow var(--t-fast) var(--ease-out);
}
.input::placeholder { color: var(--text-3); }
.input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }

/* T2.7 — global keyboard focus indicator. Covers buttons, role="button"
   divs, nav items, drawer triggers — every interactive element gets a
   visible 2px outline on tab focus. :focus-visible avoids the ring on
   mouse clicks so the look stays clean. */
button:focus-visible,
[role="button"]:focus-visible,
[tabindex]:focus-visible,
a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

/* T2.7 — hide outline for non-tab triggers; keep for keyboard users. */
button:focus:not(:focus-visible),
[role="button"]:focus:not(:focus-visible),
[tabindex]:focus:not(:focus-visible),
a:focus:not(:focus-visible) {
  outline: none;
}

/* ─── Pulse for AI / live states ─────────────────────────────────── */
.pulse {
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--accent);
  position: relative;
}
.pulse.ai { background: var(--ai); }
.pulse.ok { background: var(--ok); }
.pulse::after {
  content: "";
  position: absolute; inset: -3px;
  border-radius: 50%;
  background: inherit;
  opacity: 0.5;
  animation: pulseRing 1.6s ease-out infinite;
}
@keyframes pulseRing {
  0%   { transform: scale(0.8); opacity: 0.55; }
  100% { transform: scale(2.2); opacity: 0;    }
}

/* ─── Live-AI border — a brass hairline that breathes ─────────────
   The old rule painted the rainbow gradient into a masked ring and
   hue-rotated it. Same job, one colour. */
.live-border::after {
  background: none;
  border: 1px solid var(--accent-line);
  -webkit-mask: none;
  mask-composite: add;
  padding: 0;
  opacity: 0;
  animation: liveGlowSweep 3.6s ease-in-out infinite;
}
@keyframes liveGlowSweep {
  0%   { opacity: 0; }
  50%  { opacity: 0.85; }
  100% { opacity: 0; }
}

/* ─── Scrollbars — match the new theme ───────────────────────────── */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--bg-3);
  border-radius: 4px;
  transition: background var(--t-fast);
}
::-webkit-scrollbar-thumb:hover { background: var(--bg-4); }

/* ─── Topbar ─────────────────────────────────────────────────────── */
.topbar {
  backdrop-filter: blur(10px);
  background: rgba(10, 10, 10, 0.86);
  /* WHY position+z-index live here, beside the blur that made them necessary:
     `backdrop-filter` on anything other than `none` makes this element a NEW
     STACKING CONTEXT. Every z-index inside the topbar is therefore ranked only
     against its siblings INSIDE the topbar — and the topbar itself was
     z-index:auto, so it painted in DOM order among its own siblings and lost to
     the main content area, which comes later.

     Measured in Chromium on 2026-07-30: the identity menu rendered correctly
     (z-index 51, visible, 240x125 at the right coordinates, with the owner's
     name, email, admin badge and Sign out in it) and
     document.elementFromPoint at the Sign-out button's centre returned
     `div.card` — a dashboard card, painting over it. The owner clicked their
     name, the menu appeared underneath the page, and nothing happened.

     20 is above the whole main area and below every dialog in the app (those
     start at 89 and go to 9999), so modals still cover the topbar. */
  position: relative;
  z-index: 20;
  border-bottom: 1px solid var(--border-1);
  box-shadow: none;
}

/* ─── Headings — the serif display face ──────────────────────────── */
h1, h2, .page-title {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: -0.005em;
}

/* The full-viewport drifting orb field is retired with the purple theme.
   The keyframes stay defined because `orbsDrift` is still a live name. */
body::after { content: none; }
@keyframes orbsDrift {
  0%   { transform: translate(0, 0) scale(1); }
  50%  { transform: translate(14px, -10px) scale(1.04); }
  100% { transform: translate(-12px, 16px) scale(0.97); }
}

/* ─── Glass cards — hairline stroke instead of a rainbow one ───────
   Kept as a primitive; the gradient border is now a warm hairline that
   fades out, so a "glass" card still reads as special without colour. */
.glass-border, .card.glow {
  position: relative;
  background: var(--bg-1);
  border: 1px solid var(--border-2);
}
.glass-border::before, .card.glow::before {
  content: "";
  position: absolute;
  inset: 0;
  padding: 1px;
  border-radius: inherit;
  background: linear-gradient(135deg,
                var(--accent-line),
                transparent 55%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
  opacity: 0.7;
}

/* The 3px animated rainbow strip that ran across the top of every page is
   gone — the rebrand screenshot has no such bar, and it was the single most
   theme-defining object on screen. */
.main::before { content: none; }
@keyframes heroSweep {
  0%   { background-position: 0% 0; }
  100% { background-position: 200% 0; }
}

/* ─── Sidebar brand — larger mark, no halo, no pulse ─────────────── */
.sidebar .brand-mark {
  width: 38px; height: 38px;
  border-radius: 10px;
  animation: none;
}
.sidebar.collapsed .brand-mark {
  width: 36px; height: 36px;
}
@keyframes brandPulse {
  0%   { filter: saturate(0.55) contrast(1.05); }
  100% { filter: saturate(0.75) contrast(1.05); }
}

/* ─── Brand name — serif, set at sidebar scale ───────────────────── */
.brand-name {
  font-size: 20px;
  font-weight: 400;
}

/* ─── Nav-item hover — a lifted surface, not a coloured wash ─────── */
.nav-item:hover {
  background: var(--bg-2);
  color: var(--text-0);
}

/* ─── Badges — the AI badge reads slate, not neon ────────────────── */
.nav-item .badge.ai {
  background: var(--ai-soft);
  color: var(--ai);
  font-weight: 600;
  border: 1px solid rgba(179, 179, 179, 0.28);
}

/* ─── Sidebar workspace switch — a rule, not a tinted panel ──────── */
.workspace-switch {
  background: transparent;
  border: none;
  border-top: 1px solid var(--border-1);
}

/* ─── Page-title — flat warm white. The gradient text-clip is gone:
       clipped gradient text is the one effect that cannot be made quiet,
       and the screenshot's heading is simply the serif in white. ───── */
.page-title, h1.page-title {
  background: none;
  -webkit-background-clip: border-box;
  -webkit-text-fill-color: currentColor;
  background-clip: border-box;
  color: var(--text-0);
}

/* ─── Stat / KPI cards — brass hairline on the leading edge ──────── */
.stat-card {
  border-left: 2px solid var(--accent-line);
  background-image: none;
  background-color: var(--bg-1);
  position: relative;
}

/* ─── Avatar fallback styles (mostly used for agents) ────────────── */
.avatar {
  box-shadow: 0 0 0 1px rgba(255,255,255,0.05),
              inset 0 0 0 1px rgba(255,255,255,0.04);
}

/* ─── Mandala-splash helper (used by login page) ─────────────────── */
.brand-splash {
  width: 220px; height: 220px;
  background-image: url("logo-mandala.png?v=3");
  background-size: cover;
  background-position: center;
  filter: saturate(0.7) contrast(1.05) drop-shadow(0 18px 48px rgba(0, 0, 0, 0.9));
  animation: splashFloat 6s ease-in-out infinite alternate;
}
@keyframes splashFloat {
  0%   { transform: translateY(0) rotate(0deg); }
  100% { transform: translateY(-6px) rotate(2deg); }
}

/* ─── Reduce-motion fallback — respect OS settings ───────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}


/* ════════════════════════════════════════════════════════════════════
   BIDI / Arabic text support
   The chat + memory + learning panel render free-form text that may
   start in Arabic, English, or mix the two. We rely on Unicode Bidi
   Algorithm (UBA) via `dir="auto"` on the JSX side, but the rules
   below tune typography so Arabic doesn't look cramped beside Latin.
   ═══════════════════════════════════════════════════════════════════ */
.bidi-msg {
  /* Slightly looser tracking helps Arabic glyph spacing without
     ruining Latin readability — IBM Plex Sans Arabic handles both. */
  font-feature-settings: "kern", "liga", "ss01";
  text-align: start;   /* respects the inferred direction */
}
.bidi-msg :where(p, li, h1, h2, h3, h4) {
  /* Each paragraph picks its own direction from its first strong
     char — keeps Arabic right-aligned and English left-aligned
     within the same bubble. */
  unicode-bidi: plaintext;
}
/* Markdown-style inline markers (** ` etc.) shouldn't break bidi flow */
.bidi-msg code, .bidi-msg strong, .bidi-msg em {
  unicode-bidi: isolate;
}
/* Numbers and Latin currency in Arabic context — keep them LTR */
.bidi-msg [lang="en"], .bidi-msg .ltr {
  direction: ltr;
  unicode-bidi: isolate;
}
/* When a textarea is asked to auto-detect, give the placeholder a
   neutral align so it doesn't look broken before any input. */
textarea[dir="auto"]:placeholder-shown { text-align: start; }

/* ─── T3.6 Mobile responsive — tablets + phones ─────────────────────
   The desktop-first layout assumes ≥1024px viewports. Below that the
   sidebar wastes space, drawers overflow, and grids squish unreadably.
   Three breakpoints: tablet (≤900px), phone (≤600px), narrow phone (≤400px). */
@media (max-width: 900px) {
  .page { padding: 16px 16px 32px; }
  /* Authority dashboard 5-card strip + 2-col body collapse on tablet. */
  .page .page-actions { gap: 6px; }
}
@media (max-width: 768px) {
  /* Sidebar still visible but more compact. */
  .app .sidebar { min-width: 200px; }
}
@media (max-width: 600px) {
  /* Phones: sidebar becomes a collapsed strip with icon-only navigation. */
  .app .sidebar { min-width: 56px; max-width: 56px; }
  .app .sidebar .nav-text,
  .app .sidebar .brand-text,
  .app .sidebar .nav-group-title { display: none; }
  /* Drawer should not overflow viewport. */
  .drawer { width: 92vw !important; min-width: 0 !important; }
}
@media (max-width: 400px) {
  .page { padding: 12px 8px 24px; }
  .app .sidebar { display: none; }
}

/* Shimmer — used by the Shorts Pipeline drawer's StageStrip and
   SceneStatusRow to indicate a stage/scene that is actively in progress.
   The pseudo-overlay slides its highlight band across the pill so the user
   sees movement instead of a static colored chip. */
@keyframes shimmer {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}
