/* Inter Variable — self-hosted, no external requests (2026-07-28 rebrand).
   font-weight: 100 900 covers the whole variable axis, so every existing
   font-weight rule (400/600/700 etc.) already used across the app "just
   works" against this one file — no other CSS needed to change. */
@font-face {
  font-family: 'Inter Variable';
  src: url('/static/fonts/InterVariable.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Inter Variable';
  src: url('/static/fonts/InterVariable-Italic.woff2') format('woff2');
  font-weight: 100 900;
  font-style: italic;
  font-display: swap;
}

/* ==========================================================================
   ZED CRM — design system
   --------------------------------------------------------------------------
   Same family as Zed Payroll / Chat / Leads (warm near-black + ZED orange) so
   all in-house tools read as one product. Two deliberate departures, both
   because this is an operational screen people stare at for a full shift:

   1. The canvas is lifted off pure black (#0f1115) with layered surfaces.
      Pure black + bright text at 8h/day is genuinely fatiguing.
   2. Orange is reserved for BRAND and PRIMARY ACTIONS only — never for status.
      In the old CRM everything was blue-on-navy, so nothing stood out. Here
      status has its own unambiguous palette, because reading status at a
      glance IS the job.
   ========================================================================== */

:root {
  --bg:        #0b0f17;
  --surface:   #111827;
  --surface-2: #172136;
  --border:    #243146;
  --border-lit:#35476b;

  --text:      #e6ebf4;
  --text-dim:  #93a1b8;
  --text-mute: #6b7a94;

  --accent:      #2f7df6;   /* Azure Pulse — brand + primary actions (2026-07-28 rebrand) */
  /* `--brand` is referenced by delivery.ejs:68-74 for the selected bucket's
     outline and was NEVER declared, so selecting a Delivery bucket painted
     nothing at all. It is the same thing as the accent — brand + primary. */
  --brand:       var(--accent);
  --accent-soft: rgba(47,125,246,.14);
  /* Renders native widget internals (select arrow + popup list, date picker)
     dark — CSS can style the control box but not those parts. */
  color-scheme: dark;
  --accent-hover:#4da2ff;

  /* Status palette — each one distinct at a glance, even side by side */
  --st-new:      #8b95a7;   /* SALE_MADE  — unclaimed, neutral */
  --st-progress: #38bdf8;   /* IN_PROCESS — someone is on it (sky, kept distinct from the azure accent) */
  --st-review:   #f5b13d;   /* REVIEW     — needs agent action (the money lever) */
  --st-done:     #35c9a5;   /* PROCESSED  — awaiting supplier verification */
  --st-good:     #3ecf7a;   /* CONFIRMED  — revenue */
  --st-warn:     #ff8f5a;   /* PIN_WRONG  */
  --st-bad:      #f2545b;   /* CANCELED   */
  --st-call:     #a97bff;   /* CALL_BACK  */

  --team-zeds: #4d9fff;
  --team-zedx: #3ecf7a;

  --radius:   10px;
  --radius-lg:14px;
  --shadow:   0 4px 24px rgba(0,0,0,.35);
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  font-family: "Inter Variable", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); }

/* ---------- top bar ---------- */
.topbar {
  display: flex; align-items: center; gap: 18px;
  padding: 0 20px; height: 56px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 40;
}
.brand { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 15px; letter-spacing: .01em; white-space: nowrap; }
/* The ZED mark is a transparent PNG (same asset as Payroll and Chat), so it
   sits on the bar directly — no gradient tile behind it. */
.brand-mark {
  width: 28px; height: 28px; flex: none;
  object-fit: contain; display: block;
}
/* Wordmark sized to sit level with the 28px mark beside it (was 15px, tiny
   next to the logo, from when a subtitle line shared the space — Raymond,
   2026-07-28: drop the subtitle, size the name to the mark). Grey-into-azure
   gradient text, echoing the re-tinted logo and the payslip's net-pay band,
   instead of flat white. */
.brand-name {
  font-size: 19px;
  background: linear-gradient(120deg, var(--text) 25%, var(--accent-hover) 100%);
  -webkit-background-clip: text; background-clip: text; color: var(--text); /* fallback if clip unsupported */
  -webkit-text-fill-color: transparent;
}

.nav { display: flex; gap: 2px; flex: 1; overflow-x: auto; }
.nav a {
  color: var(--text-dim); font-size: 13px; font-weight: 500;
  padding: 7px 13px; border-radius: 8px; white-space: nowrap;
}
.nav a:hover { color: var(--text); background: var(--surface-2); }
.nav a.active { color: var(--text); background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent); }
.nav a .count {
  display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 99px;
  font-size: 11px; font-weight: 700; background: var(--accent-soft); color: var(--accent);
}

/* team badge — instant "whose data am I looking at" */
.team-badge {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12px; font-weight: 600; color: var(--text-dim);
  padding: 5px 11px; border-radius: 99px; border: 1px solid var(--border);
  background: var(--bg); white-space: nowrap;
}
.team-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--team-zeds); }
.team-dot.green { background: var(--team-zedx); }
.team-dot.grey  { background: var(--text-mute); }

.who { display: flex; align-items: center; gap: 10px; font-size: 12px; color: var(--text-dim); white-space: nowrap; }
.who .role { text-transform: uppercase; letter-spacing: .05em; font-size: 10px; color: var(--text-mute); }

/* ---------- layout ---------- */
.wrap { max-width: 1500px; margin: 0 auto; padding: 22px 20px 90px; }
.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 18px; flex-wrap: wrap; }
.page-head h1 { margin: 0; font-size: 20px; font-weight: 650; letter-spacing: -.01em; }
.page-head p { margin: 3px 0 0; color: var(--text-dim); font-size: 13px; }

/* ---------- stat cards ---------- */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 22px; }
.stat {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 15px 16px; position: relative; overflow: hidden;
}
.stat::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--st-new); }
.stat .n { font-size: 26px; font-weight: 700; line-height: 1.1; letter-spacing: -.02em; }
.stat .l { font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: .06em; margin-top: 5px; }
.stat .sub { font-size: 11px; color: var(--text-mute); margin-top: 3px; }
.stat.new::before      { background: var(--st-new); }
.stat.progress::before { background: var(--st-progress); }
.stat.review::before   { background: var(--st-review); }
.stat.done::before     { background: var(--st-done); }
.stat.good::before     { background: var(--st-good); }
.stat.warn::before     { background: var(--st-warn); }
.stat.bad::before      { background: var(--st-bad); }
/* Roll-up totals sit visually apart from the per-status cards — they're the
   yardstick you check the individual statuses against, not another status. */
.stat.rollup { background: var(--surface-2); border-color: var(--border-lit); }
.stat.rollup::before { background: var(--accent); }
.stat.rollup .n { color: var(--text); }
.stat.rollup.done::before { background: var(--st-done); }
.stat.rollup.done .n { color: var(--st-done); }
.stat.good .n { color: var(--st-good); }
.stat.review .n { color: var(--st-review); }
.stat.bad .n  { color: var(--st-bad); }

/* Grey→azure gradient for neutral headings and figures, matching the wordmark
   (Raymond, 2026-07-28: flat bright white read as dull next to the rebrand).
   Deliberately NOT applied where colour already carries meaning — the good /
   review / bad / rollup-done figures keep their status colour, since a
   gradient there would destroy the signal. `-webkit-text-fill-color` beats
   `color`, so those must be excluded rather than overridden. */
.page-head h1,
.panel-head h2,
.stat:not(.good):not(.review):not(.bad):not(.rollup) .n,
.stat.rollup:not(.done) .n {
  background: linear-gradient(120deg, var(--text) 25%, var(--accent-hover) 100%);
  -webkit-background-clip: text; background-clip: text;
  color: var(--text); /* fallback where background-clip:text is unsupported */
  -webkit-text-fill-color: transparent;
  width: fit-content; /* keep the gradient ramp tight to the glyphs */
}

/* ---------- panels & tables ---------- */
.panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; margin-bottom: 18px; }
.panel-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 13px 16px; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.panel-head h2 { margin: 0; font-size: 14px; font-weight: 650; }
.panel-body { padding: 16px; }

.tablewrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; }
/* NOTE: .tablewrap sets overflow-x:auto, which per spec forces overflow-y to
   `auto` as well — making it a scroll container. A sticky `th` therefore
   positions relative to THAT container, not the viewport, so `top: 56px`
   (the topbar height) pushed the header 56px down and it sat on top of the
   first row, hiding it. `top: 0` keeps the header where it belongs. */
th {
  text-align: left; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
  color: var(--text-mute); padding: 10px 14px; border-bottom: 1px solid var(--border);
  background: var(--surface); position: sticky; top: 0; z-index: 5;
}
td { padding: 11px 14px; border-bottom: 1px solid var(--border); vertical-align: middle; }
tbody tr:hover td { background: rgba(255,255,255,.022); }
tbody tr:last-child td { border-bottom: none; }
.t-strong { font-weight: 600; }
.t-dim { color: var(--text-dim); }
.t-mute { color: var(--text-mute); font-size: 12px; }
.t-mono { font-family: "SF Mono", ui-monospace, Menlo, monospace; font-size: 12px; }
.t-right { text-align: right; }
.nowrap { white-space: nowrap; }

/* ---------- status pills ---------- */
.pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: 99px; font-size: 11px; font-weight: 650;
  white-space: nowrap; letter-spacing: .01em;
}
.pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
.pill.new      { color: var(--st-new);      background: rgba(139,149,167,.14); }
.pill.progress { color: var(--st-progress); background: rgba(77,159,255,.14); }
.pill.review   { color: var(--st-review);   background: rgba(245,177,61,.15); }
.pill.done     { color: var(--st-done);     background: rgba(53,201,165,.14); }
.pill.good     { color: var(--st-good);     background: rgba(62,207,122,.15); }
.pill.warn     { color: var(--st-warn);     background: rgba(255,143,90,.15); }
.pill.bad      { color: var(--st-bad);      background: rgba(242,84,91,.15); }
.pill.call     { color: var(--st-call);     background: rgba(169,123,255,.15); }

/* ---------- buttons ---------- */
button, .btn {
  font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
  border-radius: 8px; padding: 8px 15px; border: 1px solid transparent;
  background: var(--accent); color: #14161a; transition: background .12s, border-color .12s;
}
button:hover, .btn:hover { background: var(--accent-hover); color: #14161a; }
button:disabled { opacity: .45; cursor: not-allowed; }
.btn-ghost { background: transparent; color: var(--text); border-color: var(--border-lit); }
.btn-ghost:hover { background: var(--surface-2); color: var(--text); border-color: var(--text-mute); }
.btn-sm { padding: 5px 11px; font-size: 12px; border-radius: 7px; }
.btn-good { background: var(--st-good); color: #08130d; }
.btn-good:hover { background: #5ddb92; color: #08130d; }
.btn-warn { background: var(--st-warn); color: #1a0d06; }
.btn-warn:hover { background: #ffa473; color: #1a0d06; }
.btn-bad  { background: var(--st-bad); color: #fff; }
.btn-bad:hover { background: #ff6b71; color: #fff; }
.btn-row { display: flex; gap: 6px; flex-wrap: wrap; }

/* ---------- forms ---------- */
.field { display: flex; flex-direction: column; gap: 5px; margin-bottom: 13px; }
.field label { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--text-dim); }
input, select, textarea {
  font: inherit; font-size: 14px; width: 100%;
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border-lit); border-radius: 8px; padding: 9px 11px;
  transition: border-color .12s, box-shadow .12s;
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}
input::placeholder, textarea::placeholder { color: var(--text-mute); }
textarea { resize: vertical; min-height: 90px; line-height: 1.55; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0 14px; }
/* Two panels side by side (Processing: New sales | Working on now). Collapses
   earlier than grid-2 because these hold tables, not label/value pairs. */
.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; align-items: start; }
@media (max-width: 1180px) { .pair { grid-template-columns: 1fr; } }
.hint { font-size: 11px; color: var(--text-mute); margin-top: -8px; margin-bottom: 12px; }

.toolbar { display: flex; gap: 9px; align-items: center; flex-wrap: wrap; }
.toolbar input, .toolbar select { width: auto; min-width: 150px; padding: 7px 10px; font-size: 13px; }

/* ---------- empty state ---------- */
.empty { padding: 52px 20px; text-align: center; color: var(--text-mute); }
.empty .icon { font-size: 30px; opacity: .35; margin-bottom: 10px; }
.empty h3 { margin: 0 0 5px; font-size: 14px; color: var(--text-dim); font-weight: 600; }
.empty p { margin: 0; font-size: 13px; }

/* ---------- sticky notes: always one keystroke away ---------- */
.notes-fab {
  position: fixed; right: 22px; bottom: 22px; z-index: 60;
  width: 50px; height: 50px; border-radius: 50%; border: none;
  background: var(--accent); color: #14161a; font-size: 21px;
  box-shadow: var(--shadow); display: grid; place-items: center; padding: 0;
}
.notes-fab:hover { background: var(--accent-hover); transform: translateY(-1px); }
.notes-panel {
  position: fixed; right: 0; top: 0; bottom: 0; width: 400px; max-width: 92vw; z-index: 70;
  background: var(--surface); border-left: 1px solid var(--border);
  box-shadow: -8px 0 40px rgba(0,0,0,.45);
  display: flex; flex-direction: column;
  transform: translateX(100%); transition: transform .18s ease;
}
.notes-panel.open { transform: translateX(0); }
.notes-head { display: flex; align-items: center; justify-content: space-between; padding: 15px 16px; border-bottom: 1px solid var(--border); }
.notes-head h3 { margin: 0; font-size: 14px; font-weight: 650; }
.notes-head .close { background: none; border: none; color: var(--text-mute); font-size: 22px; cursor: pointer; padding: 0 4px; }
.notes-head .close:hover { color: var(--text); background: none; }
.notes-panel textarea {
  flex: 1; border: none; border-radius: 0; background: transparent;
  padding: 16px; font-size: 14px; line-height: 1.65; resize: none;
}
.notes-panel textarea:focus { box-shadow: none; border-color: transparent; }
.notes-foot { padding: 9px 16px; border-top: 1px solid var(--border); font-size: 11px; color: var(--text-mute); display: flex; justify-content: space-between; }
.notes-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.5); z-index: 65; opacity: 0; pointer-events: none; transition: opacity .18s; }
.notes-backdrop.open { opacity: 1; pointer-events: auto; }

/* ---------- toast ---------- */
.toasts { position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%); z-index: 90; display: flex; flex-direction: column; gap: 8px; align-items: center; }
.toast {
  background: var(--surface-2); border: 1px solid var(--border-lit); color: var(--text);
  padding: 10px 17px; border-radius: 99px; font-size: 13px; font-weight: 500;
  box-shadow: var(--shadow); animation: rise .2s ease;
}
.toast.good { border-color: var(--st-good); color: var(--st-good); }
.toast.bad  { border-color: var(--st-bad);  color: var(--st-bad); }
@keyframes rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* ---------- login ---------- */
.login-wrap { min-height: 100vh; display: grid; place-items: center; padding: 20px; }
.login-card { width: 100%; max-width: 370px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 30px; box-shadow: var(--shadow); }
.login-card .brand { justify-content: center; margin-bottom: 6px; font-size: 17px; }
.login-card .tag { text-align: center; color: var(--text-mute); font-size: 12px; margin-bottom: 24px; }
.login-card button { width: 100%; padding: 10px; margin-top: 6px; }
.err { background: rgba(242,84,91,.12); border: 1px solid rgba(242,84,91,.4); color: #ff9096; padding: 9px 12px; border-radius: 8px; font-size: 13px; margin-bottom: 14px; }

/* ---------- misc ---------- */
.sale-detail dt { font-size: 10px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-mute); margin-top: 12px; }
.sale-detail dd { margin: 3px 0 0; font-size: 14px; }
.divider { height: 1px; background: var(--border); margin: 16px 0; }
.claimed-by { font-size: 11px; color: var(--st-progress); }
.age { font-size: 11px; color: var(--text-mute); }
.age.stale { color: var(--st-review); font-weight: 600; }

@media (max-width: 820px) {
  .topbar { height: auto; flex-wrap: wrap; padding: 10px 14px; gap: 10px; }
  th { position: static; }
  .wrap { padding: 16px 12px 90px; }
  .grid-2, .grid-3 { grid-template-columns: 1fr; }
}

/* ---------- follow-ups: rows whose call timer has fired ---------- */
tr.due td { background: rgba(245,177,61,.055); }
tr.due td:first-child { box-shadow: inset 3px 0 0 var(--st-review); }
tr.due .age { color: var(--st-review); font-weight: 600; }

/* ---------- per-user watermark (security) ---------- */
.watermark {
  position: fixed; inset: 0; z-index: 60; pointer-events: none;
  overflow: hidden; display: flex; flex-wrap: wrap; gap: 46px 60px;
  transform: rotate(-24deg) scale(1.5); transform-origin: center;
  opacity: .05; user-select: none;
}
.watermark span { font-size: 12px; font-weight: 600; white-space: nowrap; color: var(--text); letter-spacing: .04em; }
@media print { .watermark { opacity: .16; } }

/* Clickable stat cards (Follow-ups: RTC / Done switch the Completed list).
   The selected one gets the same azure ring the active nav tab uses, so
   "which list am I looking at" reads the same way everywhere. */
a.stat { display: block; color: inherit; cursor: pointer; transition: border-color .15s ease, transform .15s ease; }
a.stat:hover { border-color: var(--border-lit); transform: translateY(-1px); }
a.stat.is-active { box-shadow: inset 0 0 0 1px var(--accent); border-color: var(--accent); }

/* ==========================================================================
   POLISH PASS — 2026-08-06
   One appended, fully-revertible block (delete it to roll back). Three parts:
     A · data-first legibility   B · quiet-craft layer   C · status-at-a-glance
   Sourced from the ui-ux-pro-max, ui-ux-design-pro and better-* skills.
   ========================================================================== */

/* ---- A · DATA-FIRST LEGIBILITY ------------------------------------------
   Tabular figures: numbers, IDs, dates, counts and money line up digit-for-
   digit down every column. Inter's tnum only touches digits, so mixed
   name+number cells are unaffected — safe to apply broadly. */
td, .stat .n, .stat .sub, .nav a .count, .age, .t-mono, .t-num,
.pill, input, .toolbar input, .toolbar select {
  font-variant-numeric: tabular-nums;
}
.t-num { font-variant-numeric: tabular-nums; }
/* Long tracking numbers in the mono column shouldn't crush together. */
.t-mono { letter-spacing: .01em; }

/* ---- B · QUIET-CRAFT LAYER ----------------------------------------------
   Tactile press (better-ui: 0.96–0.97, exact properties, never transition:all). */
button, .btn {
  transition: background .12s ease, border-color .12s ease,
              transform .08s ease, box-shadow .12s ease, color .12s ease;
}
button:active:not(:disabled), .btn:active { transform: scale(.97); }
.notes-fab:active { transform: translateY(-1px) scale(.96); }
/* Keyboard focus was only styled on inputs; every interactive element now gets
   a visible ring for keyboard nav. :focus-visible keeps it off for mouse use. */
a:focus-visible, button:focus-visible, .btn:focus-visible,
.nav a:focus-visible, a.stat:focus-visible, .team-badge:focus-visible,
.notes-fab:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 7px;
}
/* Row hover: a touch more presence, and it settles instead of flipping. */
tbody tr { transition: background .12s ease; }
tbody tr:hover td { background: rgba(255,255,255,.035); }
/* Loading-skeleton utility — for "Scan now", table loads, any pending data. */
.skeleton { position: relative; overflow: hidden; border-radius: 6px;
  background: var(--surface-2); color: transparent !important; }
.skeleton::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.06), transparent);
  animation: skeleton-sweep 1.15s ease-in-out infinite; }
.skel-line { height: 12px; margin: 7px 0; border-radius: 6px; }
@keyframes skeleton-sweep { 100% { transform: translateX(100%); } }
/* Respect reduced-motion everywhere. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important; scroll-behavior: auto !important;
  }
  button:active, .btn:active, .notes-fab:active { transform: none; }
}

/* ---- C · STATUS-AT-A-GLANCE ---------------------------------------------
   OKLCH-normalised: every chromatic status now sits at the same lightness
   (~0.755) so none shouts — REVIEW was 8.6:1 and CANCELED 4.7:1, now a tight
   6.1–7.9 band — while hue keeps each one distinct. `new` stays deliberately
   dim (it should recede). All still pass WCAG AA on the surface. */
:root {
  --st-progress: #39bdf9;   /* IN_PROCESS */
  --st-review:   #e4a126;   /* REVIEW (money lever) — eased down from shouting */
  --st-done:     #37caa6;   /* PROCESSED */
  --st-good:     #3cce79;   /* CONFIRMED */
  --st-warn:     #fd8d58;   /* PIN_WRONG */
  --st-bad:      #ff7377;   /* CANCELED — lifted out of the dim outlier */
  --st-call:     #be91ff;   /* CALL_BACK */
}
/* Pill background now derives from the status colour itself (one source of
   truth) rather than hand-written rgba() that drifts as colours change. */
.pill.new      { color: var(--st-new);      background: color-mix(in oklab, var(--st-new)      15%, transparent); }
.pill.progress { color: var(--st-progress); background: color-mix(in oklab, var(--st-progress) 15%, transparent); }
.pill.review   { color: var(--st-review);   background: color-mix(in oklab, var(--st-review)   16%, transparent); }
.pill.done     { color: var(--st-done);     background: color-mix(in oklab, var(--st-done)     15%, transparent); }
.pill.good     { color: var(--st-good);     background: color-mix(in oklab, var(--st-good)     15%, transparent); }
.pill.warn     { color: var(--st-warn);     background: color-mix(in oklab, var(--st-warn)     15%, transparent); }
.pill.bad      { color: var(--st-bad);      background: color-mix(in oklab, var(--st-bad)      15%, transparent); }
.pill.call     { color: var(--st-call);     background: color-mix(in oklab, var(--st-call)     15%, transparent); }
/* REVIEW is the money lever — any REVIEW-status row gets the same left-edge
   flag the follow-up "due" rows use, so it catches the eye in a long queue.
   (Add class="status-review" on the <tr> where the queue renders REVIEW rows.) */
tr.status-review td { background: color-mix(in oklab, var(--st-review) 6%, transparent); }
tr.status-review td:first-child { box-shadow: inset 3px 0 0 var(--st-review); }
/* Slim REVIEW callout on the single-record page — same gold left-flag language
   as the rows, so a monitor landing on a sale in revision sees the action-item
   state at a glance (agents already get the full "Needs correcting" panel). */
.review-callout {
  display: flex; align-items: center; gap: 9px;
  padding: 10px 14px; margin: -2px 0 16px; border-radius: 9px;
  background: color-mix(in oklab, var(--st-review) 9%, transparent);
  box-shadow: inset 3px 0 0 var(--st-review);
  font-size: 13px; color: var(--text-dim); line-height: 1.45;
}
.review-callout strong { color: var(--st-review); font-weight: 650; }


/* ==========================================================================
   NOCTURNE PASS — 2026-08-25
   ==========================================================================
   Brings the CRM onto the Claude Design "Nocturne" mockups
   (Payroll UI mockups-handoff_24AUG). Analysis: memory/ui-nocturne-refresh.md.

   🔑 THE PALETTE IS ALREADY IDENTICAL — the mockups were built FROM this file
   (mockup --color-bg #0b0f17 == our --bg; --color-accent #2f7df6 == our
   --accent). Nothing here changes a colour token. This is composition,
   typography and density only.

   APPLIES TO EVERY SCREEN. Trialled Dashboard-only first, then unscoped —
   which also let the one markup change be reverted, so this refresh touches
   NO .ejs file at all. 100% CSS, one appended block.
   ⚠️ public/ is shared by BOTH deployments: crm.zedenterprises.org AND
   returns.zedexbpo.com. Whatever lands here lands on both.

   ⚠️ ONE APPENDED BLOCK, LIKE THE 2026-08-06 POLISH PASS. Delete from the
   banner to EOF to revert completely.

   🔴 SIX THINGS THIS BLOCK DELIBERATELY DOES NOT DO, because pure CSS can
   break the privacy layer without ever opening a template:
     1. no `th:empty { display:none }`  — the empty <th> over a read-only
        outcome column is deliberate; hiding it desyncs thead from tbody for
        watcher roles and they read data under the wrong heading.
     2. no `:empty` placeholder on dd/td — that reintroduces the "Delivered —"
        lie sale-detail.ejs was written to prevent.
     3. no `td:nth-child()` sizing — managers get 6 Delivery columns, others 8;
        agents 6 on Sales, others 7. Column indexes are role-dependent here.
     4. no bare `.stat:hover` / `a.stat{cursor:pointer}` — agent tally cards are
        deliberately href-less and must not advertise navigation.
     5. nothing that creates a stacking context on body/.wrap/.topbar and no
        z-index >= 60 — that silently kills the per-user watermark.
     6. `--text-dim` is NOT dimmed — it paints the `______` withheld-phone
        marker, and lowering it turns "withheld" into "no phone on file".
   ========================================================================== */

/* ── The system's SPACING SCALE (theme.json `density: 0.7`). Read live from
      the Nocturne design system 2026-08-25; the earlier pass used ad-hoc px.
      ⚠️ Adopted as a SCALE, not as a density: values snap to the nearest step,
      but the CRM stays looser than `--space-2` where the system would tighten,
      because this is an 8h/shift operational screen, not a deck. ────────── */
:root {
  --space-1: 2.8px;  --space-2: 5.6px;  --space-3: 8.4px;
  --space-4: 11.2px; --space-6: 16.8px; --space-8: 22.4px;
}

/* ── Type: the mockup's base is 15px/1.55, with dense UI stepped DOWN from it
      rather than everything scaled up. ─────────────────────────────────── */
body { font-size: 15px; line-height: 1.55; }

/* Page title sits tight on its own baseline.
   🔑 WEIGHT IS 500, NOT 600 — `--font-heading-weight: 500` in the system's
   theme.json. The 600 here was inferred from the flattened prototype. */
.page-head h1 { font-weight: 500; letter-spacing: -.015em; line-height: 1.12; }
.page-head    { margin-bottom: var(--space-8); }
.page-head p  { margin-top: var(--space-3); font-size: 12px; }

/* Panel titles become micro-caps eyebrow labels, the single most structural
   typographic difference in the mockup.
   ⚠️ `.panel-head h2` is one of the three gradient-text targets (see the
   2026-07-28 rebrand block). Raising specificity here rather than editing that
   selector keeps this block append-only and one-line revertible — but the
   gradient still wins on fill, so the colour is restated explicitly. */
.panel-head h2 {
  font-size: 13px; font-weight: 500; line-height: 1.12;
  text-transform: uppercase; letter-spacing: .08em;
  background: none; -webkit-text-fill-color: var(--text); color: var(--text);
}

/* ── Stat tiles: label ABOVE the number, mono figure, de-carded.
      Achieved with flex `order` so NO .ejs file is touched — the markup order
      (.n then .l) is unchanged, only the paint order. ─────────────────────── */
.stat { display: flex; flex-direction: column; padding: var(--space-4) var(--space-6); }
.stat .l {
  order: 1; margin: 0 0 var(--space-2);
  font-size: 11.5px; text-transform: none; letter-spacing: .01em;
  color: var(--text-dim);
}
.stat .n {
  order: 2;
  font-family: "SF Mono", ui-monospace, Menlo, monospace;
  font-size: 27px; font-weight: 600; letter-spacing: -.01em;
}
.stat .sub { order: 3; margin-top: var(--space-2); }

/* The 3px status bar STAYS. It is the only status signal on four of the seven
   tones — removing it to match the mockup's borderless tiles would delete
   meaning, not decoration. */

/* ── Tables: lighter rules, denser type. `:first-child`/`:last-child` only —
      never nth-child, because column counts differ by role. ──────────────── */
/* 🔑 14px / 11px are the system's STATED values (`.table`, `.table th`); the
   13.5/10.5 here were inferred from the prototype and were both a half-step
   small. Horizontal padding stays 14px — action-button columns are sized
   button-width + 28px and tighten into a second line below that. */
table    { font-size: 14px; }
thead th { font-size: 11px; letter-spacing: .08em; padding: var(--space-3) 14px; }
tbody td { padding: var(--space-4) 14px; }


/* ── ROW RULES — the Nocturne signature, and the one part of the design this
      pass had identified but never built. ───────────────────────────────────
   The system paints the rule on the ROW as a 1px background strip that fades
   to transparent over 48px at each end, so the fade spans the whole row rather
   than restarting at every cell boundary. th/td keep a TRANSPARENT border, so
   the layout box is byte-for-byte what it was.

   🔴 HAZARD 7 — NOT ONE OF THE SIX, AND IT BITES EXACTLY HERE.
   The six hazards above are about privacy; this one is about MEANING. The CRM
   paints row-level meaning on `td` backgrounds:
       :347  tr.due td            — the connector's 10-MINUTE RE-CALL REMINDER
       :449  tr.status-review td  — REVIEW rows
   A `td` background paints OVER the row strip beneath it. Ported naively, the
   fading rule would VANISH on precisely those two rows — the ones the eye is
   hunting for — while looking perfect everywhere else. Same class of silent
   wrongness as the "Delivered —" lie.
   ⇒ The tint MOVES from the cell to the ROW, where it layers UNDER the rule
   instead of over it, and the cells are cleared. The 3px `td:first-child`
   stripes are box-shadows and are deliberately untouched. */
:root {
  --rule-fade: 48px;
  --rule-c:   color-mix(in srgb, var(--text) 8%, transparent);
  --tint-due: rgba(245,177,61,.055);                              /* mirrors :347 */
  --tint-rev: color-mix(in oklab, var(--st-review) 6%, transparent); /* mirrors :449 */
  --tint-hov: rgba(255,255,255,.035);                             /* mirrors :404 */
}

thead th, tbody td { border-bottom-color: transparent; }

thead tr {
  background: linear-gradient(to right, transparent,
    var(--border) var(--rule-fade),
    var(--border) calc(100% - var(--rule-fade)), transparent)
    no-repeat bottom / 100% 1px;
}

tbody tr {
  --row-tint: none;
  --row-rule: linear-gradient(to right, transparent,
    var(--rule-c) var(--rule-fade),
    var(--rule-c) calc(100% - var(--rule-fade)), transparent)
    no-repeat bottom / 100% 1px;
  background: var(--row-tint), var(--row-rule);
}
tbody tr:last-child { --row-rule: none; }

/* Cells cleared so the row strip is never covered. Same specificity as the
   base rules, later in the file — so these win by order, and deleting this
   block restores the originals exactly. */
tr.due td, tr.status-review td, tbody tr:hover td { background: transparent; }

tr.due           { --row-tint: linear-gradient(var(--tint-due), var(--tint-due)) no-repeat 0 0 / 100% 100%; }
tr.status-review { --row-tint: linear-gradient(var(--tint-rev), var(--tint-rev)) no-repeat 0 0 / 100% 100%; }
tbody tr:hover   { --row-tint: linear-gradient(var(--tint-hov), var(--tint-hov)) no-repeat 0 0 / 100% 100%; }

/* Hover on an already-tinted row stacks both, so a due call-back still reads as
   due while the cursor is on it. */
tr.due:hover {
  --row-tint: linear-gradient(var(--tint-hov), var(--tint-hov)) no-repeat 0 0 / 100% 100%,
              linear-gradient(var(--tint-due), var(--tint-due)) no-repeat 0 0 / 100% 100%;
}
tr.status-review:hover {
  --row-tint: linear-gradient(var(--tint-hov), var(--tint-hov)) no-repeat 0 0 / 100% 100%,
              linear-gradient(var(--tint-rev), var(--tint-rev)) no-repeat 0 0 / 100% 100%;
}


/* ==========================================================================
   NOCTURNE — LEFT RAIL (2026-08-25)
   ==========================================================================
   Moves the nav from a 56px top bar to a 216px left rail, matching the
   mockups (`grep -c '<header'` on Zed Payroll Mockups.dc.html returns 0 —
   31 of its 36 cards use a rail; it is the design's identity).

   🔑 ZERO TEMPLATE EDITS. The rail is a re-layout of the EXISTING
   `<header class="topbar">`, whose children are already brand / nav /
   team-badge / who. `src/views/partials/topbar.ejs` is NOT opened — and that
   matters more here than anywhere else in this file, because lines 5-53 of it
   are nothing but role gates (`roles:` on every entry,
   `links.filter(n => n.roles.includes(R))` at :54, the returns-mode swap, the
   `(n.badgeAll && R !== 'super_admin')` badge selector). That filter IS the
   navigation permission model. It stays byte-identical because we never touch it.

   The collapse toggle is injected by public/app.js — also not a template.
   ========================================================================== */

:root { --rail: 216px; --rail-collapsed: 64px; }

/* ⚠️ GUARDED on :has(.topbar). login.ejs has no topbar and error.ejs renders it
   conditionally — without this they would get a 216px empty column. */
body:has(> .topbar) {
  display: grid;
  /* 🔑 THE TRACK IS ALWAYS THE COLLAPSED WIDTH. The rail expands OVER the
     content on hover, never by resizing this column — if the track resized,
     every hover would reflow the whole page and a table someone is reading
     would jump sideways under the cursor. */
  grid-template-columns: var(--rail-collapsed) 1fr;
  align-items: start;
}
/* The watermark, the notes FAB/panel and the toast host are all position:fixed,
   and <script> is display:none, so none of them claim a grid cell. A grid body
   does NOT create a containing block for fixed children, so the watermark still
   covers the viewport — verified, not assumed. */

.topbar {
  flex-direction: column; align-items: stretch; gap: 3px;
  height: 100vh; position: sticky; top: 0;
  padding: 18px 12px 14px;
  border-bottom: 0; border-right: 1px solid var(--border);
  overflow-y: auto; overflow-x: hidden;
}
.brand { padding: 0 8px 14px; }
.nav { flex-direction: column; flex: 0 1 auto; gap: 2px; overflow: visible; }
.nav a {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; font-size: 13.5px; white-space: nowrap;
}
.nav a .count { margin-left: auto; }

/* Route glyphs, keyed off href — no markup involvement. These are what makes a
   collapsed rail legible: the CRM nav is text-only, so without them a 64px rail
   would be blank. */
.nav a::before {
  content: "•"; flex: 0 0 18px; text-align: center;
  font-size: 13px; opacity: .75;
}
.nav a[href="/dashboard"]::before    { content: "▦"; }
.nav a[href="/sales"]::before        { content: "▤"; }
.nav a[href="/processing"]::before   { content: "⚑"; }
.nav a[href="/tracking"]::before     { content: "⌖"; }
.nav a[href="/delivery"]::before     { content: "▸"; }
.nav a[href="/followups"]::before    { content: "☎"; }
.nav a[href="/verification"]::before { content: "✓"; }
.nav a[href="/returns"]::before      { content: "↩"; }
.nav a[href="/trackus"]::before      { content: "⇄"; }
.nav a[href="/admin"]::before        { content: "⚙"; }

/* Identity cluster drops to the bottom of the rail, stacked. */
.team-badge { margin-top: auto; align-self: flex-start; }
.who {
  flex-direction: column; align-items: stretch; gap: 8px;
  padding-top: 12px; margin-top: 10px; border-top: 1px solid var(--border);
  min-width: 0;                       /* let the name wrap instead of overflowing */
}
/* "Chief Operating Officer SUPER ADMIN" is wider than a 216px rail, so the name
   and the role pill stack rather than clip. */
.who > span { display: flex; flex-direction: column; gap: 3px; font-size: 12.5px; line-height: 1.3; min-width: 0; overflow-wrap: anywhere; }
.who .role { font-size: 10px; }
.who .btn, .who .btn-ghost { align-self: stretch; text-align: center; }

/* ⚠️ `margin: 0` is load-bearing. The base rule (:~83) is
   `.wrap { max-width: 1500px; margin: 0 auto }` — and on a GRID ITEM those auto
   margins absorb the free space and centre a shrink-to-fit box instead of
   filling the column. Overriding max-width alone left the content 686px wide in
   a 1484px track. */
.wrap { max-width: none; margin: 0; padding: 26px 30px 60px; }

/* ── AUTO-HIDE ON HOVER — his correction, 2026-08-25 ──────────────────────
   *"THE ONE IN THE MOCKUP IS AN AUTO HIDING SIDE BAR SO WHEN WE HOVER THE MOUSE
   OVER IT IT POPS AND WHEN WE HOVER BACK IT COLLAPSE"* — so the rail RESTS
   collapsed and opens on hover. The click toggle is gone.

   Three things this shape is deliberately buying:
   1. **No reflow.** The grid track stays 64px (above); only the rail's own
      width animates, and it paints over the content at the BASE z-index 40 —
      above the page, and deliberately below the watermark's 60 (hazard 5).
      No new stacking context is created anywhere.
   2. **`:focus-within`, not just `:hover`.** Tabbing into the nav opens the
      rail too. Hover-only would make the whole menu unreachable by keyboard.
   3. **A delay on the way CLOSED, none on the way open.** Brushing the cursor
      past the rail on the way to the table must not make it flap.
   ⚠️ The follow-up count badge stays visible at 64px — it is the connector's
   10-minute re-call reminder and losing it would break a real control. */

.topbar {
  width: var(--rail-collapsed);
  transition: width .16s ease .18s;          /* .18s delay applies to CLOSING */
}
/* The open rail floats OVER the page, so it needs elevation or it reads as if
   the table merged into it. Shadow only — no z-index change (still 40, below
   the watermark's 60) and no new stacking context. */
.topbar:hover,
.topbar:focus-within {
  width: var(--rail); transition-delay: 0s;
  box-shadow: 14px 0 34px -12px rgba(0,0,0,.62);
}

/* Resting (collapsed) presentation. Label text is hidden with font-size:0 on
   the anchor and restored on the glyph and the badge. */
.topbar:not(:hover):not(:focus-within) { padding: 18px 8px 14px; align-items: center; }
.topbar:not(:hover):not(:focus-within) :is(.brand-name, .team-badge, .who span) { display: none; }
.topbar:not(:hover):not(:focus-within) .nav a {
  font-size: 0; justify-content: center; padding: 9px 0; gap: 0; position: relative;
}
.topbar:not(:hover):not(:focus-within) .nav a::before { font-size: 15px; flex-basis: auto; }
.topbar:not(:hover):not(:focus-within) .nav a .count {
  margin: 0; position: absolute; transform: translate(11px, -11px);
  font-size: 10px; padding: 0 4px; min-width: 15px;
}
.topbar:not(:hover):not(:focus-within) .who { border-top: 0; align-items: center; }
/* "Sign out" does not fit 64px — becomes a glyph, with the word as its tooltip. */
.topbar:not(:hover):not(:focus-within) .who .btn { font-size: 0; padding: 7px 0; }
.topbar:not(:hover):not(:focus-within) .who .btn::before { content: "\23FB"; font-size: 14px; }

/* Narrow screens: the rail is already collapsed, so only the content gutter
   needs to tighten. `.pair` collapses at this same breakpoint, so the two never
   disagree about what "narrow" means. */
@media (max-width: 1180px) {
  .wrap { padding: 20px 18px 60px; }
}

/* ── ANIMATED ZED MARK — port of his zed-logo-anim.jsx (2026-08-25) ─────────
   The mark is split down the middle and the two halves glide in from opposite
   sides; a 1.5px seam flashes as they meet. The motion lives in public/app.js
   (Web Animations API, same keyframes as the JSX); this is only the geometry.
   🔑 No template is touched — app.js replaces the existing `<img class=
   "brand-mark">` node with this wrapper at runtime.
   ⚠️ The `transform`s here sit on the SEAM and the wrapper, both deep inside
   `.topbar`. Hazard 5 is about a stacking context on body/.wrap/.topbar
   themselves — the watermark is a fixed-position SIBLING of the rail, so a
   transform on a logo half cannot affect it. */
.brand-anim { position: relative; width: 28px; height: 28px; flex: none; display: block; }
.brand-anim .brand-half { position: absolute; inset: 0; opacity: 0; }
.brand-anim .brand-half-l { clip-path: inset(0 50% 0 0); }
.brand-anim .brand-half-r { clip-path: inset(0 0 0 50%); }
.brand-anim .brand-half img { width: 100%; height: 100%; object-fit: contain; display: block; }
.brand-anim .brand-seam {
  position: absolute; left: 50%; top: -10%; bottom: -10%; width: 1.5px;
  transform: translateX(-50%); opacity: 0; pointer-events: none;
  background: linear-gradient(180deg, transparent, rgba(235,240,248,.95), transparent);
}

/* The mockup runs the mark at 52px on the sign-in card, not the rail's 28px. */
.login-card { position: relative; }
.login-card .brand-anim { width: 52px; height: 52px; }

/* "Signing you in" — his sign-in panel plays a 118px LOOPING mark while the
   credentials are checked. Sits inside the card at z-index 2; login pages carry
   no watermark (there is no signed-in user yet), so nothing to collide with. */
.signing-in {
  position: absolute; inset: 0; z-index: 2; border-radius: inherit;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-6);
  background: color-mix(in srgb, var(--surface) 92%, transparent);
}
.signing-in p { margin: 0; color: var(--text-dim); font-size: 13px; letter-spacing: .02em; }
/* ⚠️ Must out-specify `.login-card .brand-anim` (0,2,0) above, or the big
   mark silently renders at the card's 52px — which is exactly what it did
   on the first run. */
.signing-in .brand-anim-lg { width: 118px; height: 118px; }

/* ==========================================================================
   AGENT DRILL-DOWN + AUDIT TRAIL — Nocturne mockup panels 12b / 9c
   ==========================================================================
   New components only. Nothing here re-styles an existing screen, and none of
   it touches the seven hazards: no `:empty`, no `nth-child` on cells, no
   `.stat:hover`, no z-index at all, and `--text-dim` is untouched (it paints
   the `______` withheld marker on this screen too).
   ========================================================================== */

/* Breadcrumb — the mockup's "Dashboard / Danny Wright". */
.crumb { font-size: 12px; color: var(--text-mute); margin-bottom: var(--space-3); }
.crumb a { color: var(--text-dim); text-decoration: none; }
.crumb a:hover { color: var(--accent-hover); }
.crumb .sep { opacity: .5; margin: 0 var(--space-1); }

/* Identity header: initials tile + name + meta line. */
.who-head { display: flex; align-items: center; gap: var(--space-4); min-width: 0; }
.avatar {
  width: 44px; height: 44px; flex: none; border-radius: 12px;
  display: grid; place-items: center;
  font-size: 15px; font-weight: 600; letter-spacing: .02em;
  color: var(--accent-hover);
  background: var(--accent-soft); border: 1px solid var(--border-lit);
}

/* ── 7-day shape ─────────────────────────────────────────────────────────
   Bars are keyed on the OPERATIONAL day (the route uses opDayExpr), so a shift
   running past midnight stays in one column instead of splitting across two. */
.spark { display: flex; align-items: flex-end; gap: var(--space-2); padding: var(--space-6) var(--space-6) var(--space-4); height: 168px; }
.spark-col { flex: 1 1 0; display: flex; flex-direction: column; align-items: center; gap: var(--space-2); height: 100%; min-width: 0; }
.spark-n { font-family: "SF Mono", ui-monospace, Menlo, monospace; font-size: 12px; color: var(--text-dim); }
.spark-bar { flex: 1; width: 100%; display: flex; align-items: flex-end; }
.spark-bar i {
  display: block; width: 100%; border-radius: 5px 5px 0 0;
  background: linear-gradient(180deg, var(--accent-hover), var(--accent));
  min-height: 0; transition: height .18s ease;
}
.spark-col:hover .spark-bar i { background: linear-gradient(180deg, var(--accent-hover), var(--accent-hover)); }
.spark-d { font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase; color: var(--text-mute); }

/* ── Audit trail ─────────────────────────────────────────────────────────── */
.trail-filters { display: flex; flex-wrap: wrap; gap: var(--space-2); padding: var(--space-3) var(--space-6); border-bottom: 1px solid var(--border); }
.chip {
  padding: 5px 11px; border-radius: 999px; cursor: pointer;
  font: inherit; font-size: 11.5px; line-height: 1.2;
  color: var(--text-dim); background: transparent; border: 1px solid var(--border);
}
.chip:hover { color: var(--text); border-color: var(--border-lit); background: var(--surface-2); }
.chip.on { color: var(--accent-hover); border-color: var(--accent); background: var(--accent-soft); }

.trail-list { list-style: none; margin: 0; padding: var(--space-2) 0; max-height: 420px; overflow-y: auto; }
.trail-row {
  display: grid; grid-template-columns: 140px 78px 1fr; gap: var(--space-4);
  align-items: baseline; padding: var(--space-3) var(--space-6);
  font-size: 13px;
}
/* Row rule as a fading strip, matching the Nocturne table treatment. */
.trail-row + .trail-row {
  background: linear-gradient(to right, transparent,
    var(--rule-c) var(--rule-fade),
    var(--rule-c) calc(100% - var(--rule-fade)), transparent) no-repeat top / 100% 1px;
}
.trail-at { font-size: 11.5px; white-space: nowrap; }
.trail-body { min-width: 0; overflow-wrap: anywhere; }
.trail-body a { color: var(--text); text-decoration: none; font-weight: 600; }
.trail-body a:hover { color: var(--accent-hover); }
.trail-note { font-size: 11.5px; margin-top: 3px; font-style: italic; }
.trail-row.is-filtered { display: none; }

@media (max-width: 1180px) {
  .trail-row { grid-template-columns: 1fr; gap: var(--space-1); }
  .trail-at { white-space: normal; }
}

/* ── Leaderboard "vs top" bar (Nocturne panel 12a) ───────────────────────────
   ⚠️ `.lb-rank` / `.lb-bar` are CLASSES on the cells, not `nth-child` sizing —
   hazard 3. This table has a fixed shape today, but keying widths off column
   POSITION is the habit that breaks the role-dependent tables elsewhere. */
.lb-rank { width: 46px; text-align: right; }
.lb-bar  { width: 180px; }
.bar {
  display: block; height: 8px; border-radius: 4px;
  background: var(--accent-soft); overflow: hidden;
}
.bar i {
  display: block; height: 100%; border-radius: 4px;
  background: linear-gradient(90deg, var(--accent), var(--accent-hover));
  min-width: 2px;
}

/* ── Activity log filters (Nocturne panel 9c) ───────────────────────────────
   The chips are LINKS, not buttons — each is a real filtered URL, so the view
   is shareable and the back button works. */
.filter-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
  padding: var(--space-3) var(--space-6); border-bottom: 1px solid var(--border);
}
.filter-row .lbl {
  font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-mute); margin-right: var(--space-1);
}
a.chip { text-decoration: none; display: inline-block; }

/* ── Admin users: expandable audit trail (Nocturne panel 9a) ────────────────
   The caret is a real <button> so it is keyboard-reachable; the expanded panel
   is a full-width row that spans every column. */
.expander {
  background: transparent; border: 0; cursor: pointer; padding: 2px 6px;
  color: var(--text-dim); font-size: 12px; line-height: 1;
}
.expander:hover { color: var(--accent-hover); }
.trail-cell { padding: 0 !important; background: var(--bg); }
.trail-cell .trail-list { max-height: 320px; }
.trail-cell .trail-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  padding: var(--space-3) var(--space-6); flex-wrap: wrap;
}
.trail-cell .trail-head h3 {
  margin: 0; font-size: 12px; font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase; color: var(--text);
}
.trail-loading { padding: var(--space-6); color: var(--text-mute); font-size: 12.5px; }
