/* Hans Hyundai Calling Portal — sidebar layout.
   One light theme, deliberately (no dark mode). The ink block that used to
   be a horizontal header bar is now the left rail, which is what lets 11
   nav items + 6 SMR sub-reports fit without the horizontal overflow the
   old pill bar had at every viewport width.

   Colour discipline: blue is the single accent (active nav, links, focus,
   the Calls series). Green / amber / red mean performance ONLY, nowhere
   decorative. The four stat tiles used to be four saturated gradients that
   encoded nothing and competed with the chart series for the same hues. */

:root {
  color-scheme: light;

  --ink:            #0e1016;
  --ink-2:          #171a22;
  --ink-3:          #232732;

  --page:           #f6f7f9;
  --surface:        #ffffff;
  --surface-2:      #f4f5f7;
  --surface-3:      #eceef2;
  --zebra:          #fbfbfc;  /* table even-row tint, distinct from surface-2/3 */

  --text:           #12141a;
  --text-2:         #565b68;  /* 6.4:1 on white */
  /* Was #8b909d, which measured 3.20:1 on white — below the 4.5:1 AA floor
     for normal text, and this token carries nearly every small label in the
     app (stat labels, chart axis ticks, "Last updated", the "–" placeholder,
     bucket counts), all of it at 10-12px where it hurts most. 4.8:1. */
  --text-3:         #6b7280;

  --border:         #e4e6eb;
  --border-strong:  #d3d7de;

  --accent:         #2454ff;
  --accent-hover:   #1b45e0;
  --accent-soft:    #eef2ff;

  /* Performance colours, measured against their own soft badge background
     (NOT against white — the badge is what they actually sit on, and that
     lighter ground is what was quietly eating the contrast):
       old #0f9d63 on #e6f6ef = 3.13:1  ->  now 4.8:1
       old #b45309 on #fdf3e3 = 4.46:1  ->  now 4.6:1
       old #d92d20 on #fdeceb = 4.20:1  ->  now 5.3:1
     All three also clear 4.5:1 as plain text on white, which is how the
     Booking% ".rate" variant renders them. Green mattered most: it's both
     the most common state and was the worst of the three. */
  --good:           #0a7a4d;
  --good-soft:      #e6f6ef;
  --warn:           #a85a06;
  --warn-soft:      #fdf3e3;
  --crit:           #c0201a;
  --crit-soft:      #fdeceb;

  --series-1: #2454ff;
  --series-2: #ff6a45;
  --series-3: #0f9d63;

  --status-good:     var(--good);
  --status-warning:  var(--warn);
  --status-critical: var(--crit);

  --shadow-sm: 0 1px 2px rgba(14,16,22,0.05);
  --shadow:    0 1px 2px rgba(14,16,22,0.04), 0 10px 28px -8px rgba(14,16,22,0.14);
  --shadow-lg: 0 8px 40px rgba(14,16,22,0.18);

  --sidebar-w: 236px;
  --sidebar-w-collapsed: 60px;
  --r-sm: 7px;
  --r:    10px;
  --r-lg: 14px;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--page);
  color: var(--text);
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 14px;
  -webkit-font-smoothing: antialiased;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--accent);
  color: #fff;
  padding: 10px 16px;
  border-radius: 0 0 var(--r-sm) 0;
  font-weight: 600;
}
.skip-link:focus { left: 0; }

/* ---------- Shell ---------- */

.app {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  min-height: 100vh;
  transition: grid-template-columns 0.16s ease;
}
/* Collapsed rail — icon-only width. Toggled by app.js via a body class,
   same pattern as the row-density toggle (.rows-compact). */
body.sidebar-collapsed .app {
  grid-template-columns: var(--sidebar-w-collapsed) minmax(0, 1fr);
}
@media (prefers-reduced-motion: reduce) {
  .app { transition: none; }
}

/* ---------- Sidebar ---------- */

.sidebar {
  /* Subtle vertical depth instead of a flat fill — barely perceptible as a
     gradient, reads as "a surface with light falling on it" rather than a
     single flat ink block. Kept inside the --ink/--ink-3 range already
     defined, not a new color. */
  background: linear-gradient(180deg, var(--ink-3) 0%, var(--ink) 55%, #0a0b10 100%);
  color: #fff;
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.brand {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 18px 18px 16px;
}
.brand-mark {
  /* Real Hyundai logo (oval mark + wordmark, ~1.78:1), swapped in for the
     old "HH" monogram on request 2026-10-13 — sized as a small lockup
     rather than forced into the old 34x34 square, which squished it. A
     white card behind it keeps the logo's own white background readable
     against the dark sidebar instead of showing as a stray white box. */
  width: 56px;
  height: 32px;
  flex: none;
  border-radius: 7px;
  background: #fff;
  box-shadow: 0 2px 8px rgba(0,0,0,0.25);
  padding: 3px 5px;
  display: grid;
  place-items: center;
}
.brand-mark img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.brand-name {
  font-family: "Unbounded", "Inter", sans-serif;
  font-weight: 700;
  font-size: 13.5px;
  line-height: 1.25;
  letter-spacing: -0.01em;
}
.brand-sub {
  font-size: 11.5px;
  color: rgba(255,255,255,0.45);
  line-height: 1.3;
}

.sidebar-nav {
  flex: 1;
  padding: 4px 10px 16px;
}
.nav-group + .nav-group { margin-top: 16px; }
.nav-group-label {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  /* Was 0.32 alpha — measured ~2.9:1 against the sidebar background, well
     under the 4.5:1 AA floor this file's own --text-3 comment holds every
     other label to. These three section headers (LIVE TABLES/SMR/TOOLS)
     are the most persistent chrome in the app, not a one-off label.
     ~0.58 clears AA at this size. Confirmed live in a UI audit, 2026-10-10. */
  color: rgba(255,255,255,0.58);
  padding: 0 8px 6px;
}

/* Nav items. Inter, not Unbounded — the display face at 12.5px was a real
   legibility cost and made every label wider than it needed to be, which
   was part of why 11 tabs never fit horizontally. */
.tab {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  text-align: left;
  color: rgba(255,255,255,0.68);
  background: transparent;
  border: none;
  border-radius: var(--r-sm);
  padding: 8px 10px;
  cursor: pointer;
  transition: background-color 0.12s ease, color 0.12s ease;
}
.tab + .tab { margin-top: 1px; }
.tab-icon {
  width: 17px;
  height: 17px;
  flex: none;
  display: inline-flex;
  opacity: 0.62;
}
.tab-icon svg {
  width: 100%;
  height: 100%;
  stroke: currentColor;
}
.tab-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tab:hover {
  background: rgba(255,255,255,0.07);
  color: #fff;
}
.tab.active {
  background: linear-gradient(155deg, var(--accent) 0%, var(--accent-hover) 100%);
  color: #fff;
  font-weight: 600;
  box-shadow: 0 2px 10px rgba(36,84,255,0.32);
}
.tab.active .tab-icon { opacity: 1; }
.tab:focus-visible {
  outline: 2px solid #fff;
  outline-offset: -2px;
}

/* SMR sub-reports, nested under their parent nav item. Reuses the
   .crequeue-bucket class app.js's renderSmrSubtabs() emits, restyled for
   this context rather than changing the generator. */
.smr-subtabs {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin: 2px 0 2px 18px;
  padding-left: 10px;
  border-left: 1px solid rgba(255,255,255,0.12);
}
.smr-subtabs .crequeue-bucket {
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  text-align: left;
  color: rgba(255,255,255,0.55);
  background: transparent;
  border: none;
  border-radius: var(--r-sm);
  padding: 6px 9px;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.smr-subtabs .crequeue-bucket:hover {
  background: rgba(255,255,255,0.07);
  color: #fff;
}
.smr-subtabs .crequeue-bucket.active {
  color: #fff;
  background: rgba(255,255,255,0.12);
  font-weight: 600;
}
.smr-subtabs .crequeue-bucket:focus-visible {
  outline: 2px solid #fff;
  outline-offset: -2px;
}

.sidebar-foot {
  padding: 12px 16px 16px;
  border-top: 1px solid rgba(255,255,255,0.08);
  display: flex;
  flex-direction: column;
  gap: 7px;
}
.status-pill {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  font-weight: 600;
  color: rgba(255,255,255,0.85);
}
.status-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: rgba(255,255,255,0.35);
  flex: none;
}
.status-dot.ok {
  background: #2ee6a0;
  box-shadow: 0 0 0 3px rgba(46,230,160,0.18);
  animation: live-pulse 2.4s ease-in-out infinite;
}
.status-dot.error { background: #ff6b6b; box-shadow: 0 0 0 3px rgba(255,107,107,0.18); }
@keyframes live-pulse {
  0%, 100% { box-shadow: 0 0 0 3px rgba(46,230,160,0.18); }
  50%      { box-shadow: 0 0 0 6px rgba(46,230,160,0.08); }
}
@media (prefers-reduced-motion: reduce) {
  .status-dot.ok { animation: none; }
}
.source-badge {
  font-size: 11px;
  /* Was 0.38 alpha — measured ~3.6:1, under the 4.5:1 AA floor. This is the
     text naming which system the live data is coming from, exactly what a
     viewer checks to confirm the numbers on screen are real. */
  color: rgba(255,255,255,0.58);
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sidebar-toggle {
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  color: rgba(255,255,255,0.6);
  background: transparent;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: var(--r-sm);
  padding: 6px 9px;
  cursor: pointer;
}
.sidebar-toggle:hover { background: rgba(255,255,255,0.07); color: #fff; }
.sidebar-toggle:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }
.toggle-icon { font-size: 13px; width: 12px; text-align: center; flex: none; }

/* Collapsed rail: hide every label, centre every icon, shrink paddings that
   assumed a 236px-wide column. The SMR sub-report list is hidden outright
   rather than squeezed — there's no legible way to show six report names
   in 60px, so reaching them means expanding first.
   Guarded to min-width:1081px — collapse is a wide-viewport affordance
   only (the toggle itself is hidden below that, see the 1080px query
   further down), so these rules simply don't exist at narrow widths
   instead of needing to be un-done there one at a time. */
@media (min-width: 1081px) {
  body.sidebar-collapsed .brand-text,
  body.sidebar-collapsed .nav-group-label,
  body.sidebar-collapsed .tab-text,
  body.sidebar-collapsed .smr-subtabs,
  body.sidebar-collapsed .status-pill span:not(.status-dot),
  body.sidebar-collapsed .source-badge,
  body.sidebar-collapsed .toggle-text {
    display: none;
  }
  body.sidebar-collapsed .brand { justify-content: center; padding: 18px 0 16px; }
  body.sidebar-collapsed .sidebar-nav { padding: 4px 8px 16px; }
  body.sidebar-collapsed .tab { justify-content: center; padding: 9px; }
  body.sidebar-collapsed .tab-icon { width: 18px; height: 18px; opacity: 0.72; }
  body.sidebar-collapsed .sidebar-toggle { justify-content: center; }
  body.sidebar-collapsed .toggle-icon { transform: rotate(180deg); }
  body.sidebar-collapsed .status-pill { justify-content: center; }
}

/* ---------- Main column ---------- */

.main {
  min-width: 0;
  padding: 16px 22px 28px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Section title, date range and freshness on ONE row. Previously a topbar
   and a panel-header stacked, ~90px of chrome before any data. */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.topbar-left {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.topbar-left h2 {
  font-family: "Unbounded", "Inter", sans-serif;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.015em;
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.topbar-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.date-context {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
  border-radius: 999px;
  padding: 2px 9px;
  white-space: nowrap;
}

/* Quick ranges. The native date inputs below can't be locale-forced away
   from mm/dd/yyyy, so these exist to make the common windows one click
   instead of two fiddly date pickers. */
.date-presets {
  display: inline-flex;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 2px;
  gap: 2px;
}
.preset {
  font: inherit;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text-2);
  background: transparent;
  border: none;
  border-radius: 5px;
  padding: 5px 10px;
  cursor: pointer;
  white-space: nowrap;
}
.preset:hover { color: var(--text); background: var(--surface); }
.preset.active { background: var(--surface); color: var(--accent); box-shadow: var(--shadow-sm); }
.preset:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.date-range {
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 3px 6px;
}
.date-field {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
/* Both date inputs — the old rule targeted a #dateFilter id that no longer
   existed, so these rendered completely unstyled. */
#dateFromFilter,
#dateToFilter {
  font: inherit;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text);
  background: transparent;
  border: none;
  border-radius: 5px;
  padding: 3px 4px;
  font-variant-numeric: tabular-nums;
}
#dateFromFilter:focus-visible,
#dateToFilter:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.date-sep { color: var(--text-3); font-size: 11px; }
.date-range-clear {
  background: var(--surface-3);
  color: var(--text-2);
  border: none;
  border-radius: 5px;
  width: 22px;
  height: 22px;
  cursor: pointer;
  font-size: 11px;
  line-height: 1;
  flex: none;
}
.date-range-clear:hover { background: var(--border-strong); color: var(--text); }

.last-updated {
  font-size: 11.5px;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.loading-indicator {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: 999px;
  padding: 3px 10px;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* Shared "quiet, bordered, secondary action" language for Refresh, the
   ghost/pressed filter toggles, and pagination Prev/Next — three separate
   copies of essentially the same button had quietly drifted apart
   (different disabled opacity, hover only touching border on one and only
   text color on the others), confirmed live in a UI audit as reading like
   three inconsistent button styles rather than one. Padding/font-size stay
   per-class below since those are real size variants — a toolbar action vs.
   a compact page-nav button — not accidental drift. */
.btn-refresh,
.btn-ghost,
.btn-page {
  font: inherit;
  font-weight: 600;
  color: var(--text-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.btn-refresh:hover:not(:disabled),
.btn-ghost:hover:not(:disabled),
.btn-page:hover:not(:disabled) { background: var(--surface-2); border-color: var(--border-strong); color: var(--text); }
.btn-refresh:disabled,
.btn-ghost:disabled,
.btn-page:disabled { opacity: 0.5; cursor: default; }
.btn-refresh:focus-visible,
.btn-ghost:focus-visible,
.btn-page:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn-refresh { font-size: 12.5px; padding: 7px 13px; }

/* ---------- Day summary strip ---------- */
/* One card with four cells, replacing four full-bleed gradient blocks. The
   heading is load-bearing: these are always Call ScoreCard totals for every
   CRE, shown on every tab, so without it they read as the open tab's own
   numbers on CRE Queue / Lookup / Forecast. */

.stat-row {
  display: flex;
  align-items: stretch;
  gap: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.stat-row-label {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  padding: 12px 16px;
  background: var(--surface-2);
  border-right: 1px solid var(--border);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-2);
  white-space: nowrap;
  flex: none;
}
.stat-row-scope {
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--text-3);
}
.stat-cells {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  flex: 1;
  min-width: 0;
}
.stat-cell {
  padding: 11px 16px;
  min-width: 0;
  border-left: 1px solid var(--border);
  position: relative;
}
.stat-cell:first-child { border-left: none; }
/* A 2px top rule instead of a full saturated fill — enough to tell the
   four apart at a glance without four colour-blocks shouting. */
.stat-cell::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
}
.stat-cell--calls::before    { background: var(--series-1); }
.stat-cell--contacts::before { background: var(--series-2); }
/* Unlike the other three tiles (fixed identity colours matching their
   chart series), this one tracks an actual rate — its bar follows
   Contact%'s real good/warn/critical value (set via JS's rate-* classes,
   see setStatContactPctRate) instead of always reading "good". Neutral
   fallback is for the moment before the first tab load sets a value, and
   for tabs (CRE Queue) that repurpose this cell for a non-rate count. */
.stat-cell--rate::before        { background: var(--border-strong); }
.stat-cell--rate.rate-good::before     { background: var(--good); }
.stat-cell--rate.rate-warning::before  { background: var(--warn); }
.stat-cell--rate.rate-critical::before { background: var(--crit); }
.stat-cell--bookings::before { background: #8b3dff; }

.stat-label {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.stat-value {
  font-family: "Unbounded", "Inter", sans-serif;
  font-size: 23px;
  font-weight: 600;
  letter-spacing: -0.025em;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
  /* The delta used to sit inline and broke out of the tile below ~1100px,
     with "yst." landing outside the coloured block entirely. */
  display: flex;
  align-items: baseline;
  gap: 7px;
  flex-wrap: wrap;
}
/* Free/Paid breakdown under the Fresh Bookings tile's main number — quieter
   than .stat-value on purpose (a footnote on the headline total, not a
   second headline), same spirit as .profile-headline-hint elsewhere. */
.stat-split {
  font-size: 11px;
  font-weight: 500;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.stat-delta {
  font-family: "Inter", sans-serif;
  font-size: 11px;
  font-weight: 600;
  border-radius: 999px;
  padding: 2px 7px;
  white-space: nowrap;
  letter-spacing: 0;
}
.stat-delta.up   { color: var(--good); background: var(--good-soft); }
.stat-delta.down { color: var(--crit); background: var(--crit-soft); }
.stat-delta.flat { color: var(--text-2); background: var(--surface-3); }

/* ---------- Panel ---------- */

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-sm);
  padding: 16px 18px 18px;
  min-width: 0;
}

/* Plain-language note. Replaces the old .crequeue-note, which carried
   engineering changelog text ("Filtered by pipeline below before anything
   else, so Paid's much higher raw Bitrix activity never crowds out Free")
   straight into a CRE's face. */
.panel-note {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--text-2);
  background: var(--surface-2);
  border-left: 2px solid var(--border-strong);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  padding: 9px 13px;
  margin: 0 0 14px;
}
.panel-note strong { color: var(--text); font-weight: 600; }
.panel-note--tight { margin-bottom: 10px; }
/* For a note with several distinct caveats (SMR Reports') — a lead sentence
   plus a short bullet list scans in a glance; the same four sentences run
   together as one paragraph read as the densest, hardest-to-parse note in
   the app (confirmed live in a UI audit, 2026-10-10). */
.panel-note--list p { margin: 0 0 6px; }
.panel-note--list ul { margin: 0; padding-left: 18px; }
.panel-note--list li + li { margin-top: 3px; }

.pending-notice {
  font-size: 12.5px;
  font-weight: 500;
  color: var(--warn);
  background: var(--warn-soft);
  border: 1px solid color-mix(in srgb, var(--warn) 22%, transparent);
  border-radius: var(--r-sm);
  padding: 9px 13px;
  margin-bottom: 12px;
}

.section-title {
  font-family: "Unbounded", "Inter", sans-serif;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 20px 0 9px;
}
.section-title:first-child { margin-top: 0; }
.section-sub {
  font-family: "Inter", sans-serif;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--text-3);
  letter-spacing: 0;
}

.panel-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 11px;
  flex-wrap: wrap;
}
.toolbar-actions { display: flex; gap: 7px; }
.search-hidden-col-hint {
  font-size: 12px;
  color: var(--text-2);
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-radius: var(--r-sm);
  padding: 7px 11px;
  margin: -3px 0 11px;
}

.table-search {
  font: inherit;
  font-size: 12.5px;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 7px 11px;
  min-width: 240px;
  flex: 1;
  max-width: 380px;
}
.table-search::placeholder { color: var(--text-3); }
.table-search:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }

.btn-ghost { font-size: 12px; padding: 7px 12px; }
.btn-ghost[aria-pressed="true"] {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 35%, transparent);
  color: var(--accent);
}

/* Compact rows. A call-centre screen is read far more often than it's
   scrolled, so trading padding for ~5 extra visible rows is usually the
   right call — but it's a preference, not a default, so it's a toggle. */
body.rows-compact tbody td { padding: 4px 12px; }
body.rows-compact thead th { padding: 6px 12px; }
body.rows-compact table { font-size: 12px; }
body.rows-compact .cre-mini-table td { padding: 3px 10px; }

.btn-primary {
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: #fff;
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--r-sm);
  padding: 7px 16px;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.12s ease, border-color 0.12s ease, transform 0.1s ease, box-shadow 0.12s ease;
}
.btn-primary:hover:not(:disabled) {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  box-shadow: 0 2px 10px rgba(36,84,255,0.28);
}
/* Physical-push feedback on click, matching .crequeue-bucket's own hover
   lift — a plain color-only state change reads as "did my click register?"
   on a trackpad; a tiny press-down is a cheap, immediate answer. */
.btn-primary:active:not(:disabled) { transform: translateY(1px); box-shadow: none; }
.btn-primary:disabled { opacity: 0.55; cursor: default; }
.btn-primary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- Tables ---------- */

.table-scroll,
.crequeue-table-scroll {
  overflow: auto;
  max-height: calc(100vh - 250px);
  min-height: 140px;
  position: relative;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.crequeue-table-scroll { max-height: calc(100vh - 380px); }
#forecastBookingsScroll,
#forecastCallVolumeScroll,
#forecastDueServicesScroll { max-height: 300px; min-height: 0; }

/* Visible scrollbars on the table containers. CRE Queue is 15 fixed columns
   with no density toggle, so sideways scrolling is a normal part of using
   it — with the OS's default overlay scrollbars that's completely
   invisible until you happen to try. The sticky first column above is the
   other half of making that scroll usable. */
.table-scroll::-webkit-scrollbar,
.crequeue-table-scroll::-webkit-scrollbar,
.cre-mini-table-scroll::-webkit-scrollbar { width: 10px; height: 10px; }
.table-scroll::-webkit-scrollbar-track,
.crequeue-table-scroll::-webkit-scrollbar-track,
.cre-mini-table-scroll::-webkit-scrollbar-track { background: var(--surface-2); }
.table-scroll::-webkit-scrollbar-thumb,
.crequeue-table-scroll::-webkit-scrollbar-thumb,
.cre-mini-table-scroll::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: 999px;
  border: 2px solid var(--surface-2);
}
.table-scroll::-webkit-scrollbar-thumb:hover,
.crequeue-table-scroll::-webkit-scrollbar-thumb:hover,
.cre-mini-table-scroll::-webkit-scrollbar-thumb:hover { background: var(--text-3); }
.table-scroll, .crequeue-table-scroll, .cre-mini-table-scroll {
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) var(--surface-2);
}

/* Same "more content this way" fade as the main table — previously only
   wired up for #tableScroll, even though CRE Queue, Customer Lookup, and
   every profile-modal mini-table scroll sideways too (confirmed live in a
   UI audit: those three had the identical clipped-last-column problem with
   no visual cue a horizontal scroll existed at all). */
.table-scroll.has-overflow-right::after,
.crequeue-table-scroll.has-overflow-right::after,
.cre-mini-table-scroll.has-overflow-right::after {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 18px;
  pointer-events: none;
  /* A pure white-fade gradient here previously — over a table body that's
     already near-white (the zebra-stripe rows, the plain white default) —
     was functionally invisible: confirmed live in a UI audit by
     pixel-sampling a rendered screenshot, the "fade" shifted by ~4/255 RGB
     units before hitting pure white. A shadow darkens the edge regardless
     of what color sits behind it, so it stays visible over white, striped,
     or hovered rows alike. */
  box-shadow: inset -10px 0 8px -8px rgba(20, 22, 28, 0.22);
}

table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 12.5px;
}
thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  text-align: left;
  font-weight: 600;
  font-size: 11.5px;
  color: var(--text-2);
  background: var(--surface-2);
  border-bottom: 1px solid var(--border-strong);
  padding: 9px 12px;
  white-space: nowrap;
}
tbody td {
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
tbody tr:last-child td { border-bottom: none; }
tbody tr:nth-child(even) td { background: var(--zebra); }
tbody tr:hover td { background: var(--accent-soft); }

/* The name column stays put while wide tables scroll sideways — CRE Queue
   is 15 columns and ScoreCard 14, so without this you lose track of whose
   row you're reading the moment you scroll right. */
thead th:first-child,
tbody td:first-child {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--surface);
  border-right: 1px solid var(--border);
}
thead th:first-child { z-index: 3; background: var(--surface-2); }
tbody tr:nth-child(even) td:first-child { background: var(--zebra); }
tbody tr:hover td:first-child { background: var(--accent-soft); }

thead th.sortable { cursor: pointer; user-select: none; }
thead th.sortable:hover { color: var(--text); background: var(--surface-3); }
thead th.sortable:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.sort-arrow { margin-left: 5px; font-size: 9px; color: var(--accent); }
/* Faintly visible at rest, not fully hidden until hover. Sorting is one of
   the main things people do with these tables, and hiding every affordance
   behind a hover state means a first-time user has no way to discover it —
   and a keyboard-only user never triggers hover at all. */
.sort-arrow-idle { color: var(--text-3); opacity: 0.4; transition: opacity 0.12s ease; }
thead th.sortable:hover .sort-arrow-idle { opacity: 1; }

/* Contact% keeps the pill (a true 0-100 rate). Booking% is deliberately
   NOT a pill any more: the two sit side by side but run on completely
   different scales (good is 80%+ for contact, 10%+ for booking), so an
   identical-looking green "15%" next to an amber "51%" read as a mistake. */
.badge {
  display: inline-block;
  min-width: 42px;
  text-align: center;
  border-radius: 999px;
  padding: 2px 8px;
  font-size: 11.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.badge-good     { color: var(--good); background: var(--good-soft); }
.badge-warning  { color: var(--warn); background: var(--warn-soft); }
.badge-critical { color: var(--crit); background: var(--crit-soft); }
.badge-neutral  { color: var(--text-2); background: var(--surface-3); }

.rate {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.rate-good     { color: var(--good); }
.rate-warning  { color: var(--warn); }
.rate-critical { color: var(--crit); }

.cell-na { color: var(--text-3); }

.table-empty {
  padding: 28px 12px;
  text-align: center;
  color: var(--text-3);
  font-size: 13px;
}

.skeleton-bar {
  display: block;
  height: 9px;
  border-radius: 4px;
  background: linear-gradient(90deg, var(--surface-3) 25%, var(--surface-2) 37%, var(--surface-3) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.3s ease-in-out infinite;
}
.skeleton-row td:nth-child(3n+1) .skeleton-bar { width: 72%; }
.skeleton-row td:nth-child(3n+2) .skeleton-bar { width: 44%; }
.skeleton-row td:nth-child(3n)   .skeleton-bar { width: 58%; }
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }
@media (prefers-reduced-motion: reduce) {
  .skeleton-bar { animation: none; }
}

/* ---------- Overview ---------- */

.overview-content { display: flex; flex-direction: column; gap: 12px; }
/* Side by side instead of stacked — the two charts used to be full-width
   and ~380px tall each, which pushed the second one below the fold on any
   normal laptop. */
.overview-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}
@media (max-width: 1180px) {
  .overview-grid { grid-template-columns: minmax(0, 1fr); }
}
.overview-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 14px 16px;
  min-width: 0;
}
.overview-card-title {
  font-family: "Unbounded", "Inter", sans-serif;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: 10px;
}
.overview-card-sub {
  font-family: "Inter", sans-serif;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--text-3);
  letter-spacing: 0;
}

.chart-legend { display: flex; gap: 14px; margin-bottom: 6px; }
.chart-legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  color: var(--text-2);
  font-weight: 500;
}
.chart-legend-swatch { width: 9px; height: 9px; border-radius: 2px; flex: none; }
.chart-container { width: 100%; overflow: hidden; }
.chart-container svg { display: block; width: 100%; height: auto; }
.chart-bar { transition: opacity 0.1s ease; cursor: pointer; }
.chart-bar:hover { opacity: 0.75; }
.chart-bar:focus-visible { opacity: 0.75; outline: 2px solid var(--accent); outline-offset: 1px; }
.chart-axis-label { font-size: 10px; fill: var(--text-3); font-family: "Inter", sans-serif; }
.chart-gridline { stroke: var(--border); stroke-width: 1; }
/* Hours still to come today — tells "nothing has happened yet" apart from
   "this hour had zero calls", which look identical as bare empty slots. */
/* Drawn on top of the bars now (see app.js's renderBarChart) — opacity
   lets the muted bars still show through instead of fully hiding them,
   pointer-events:none keeps bar hover/tooltips working underneath it. */
.chart-future { fill: var(--surface); opacity: 0.72; pointer-events: none; }
.chart-future-label { fill: var(--text-2); font-weight: 600; }
.chart-tooltip {
  position: fixed;
  z-index: 60;
  background: var(--ink);
  color: #fff;
  font-size: 11.5px;
  line-height: 1.5;
  border-radius: var(--r-sm);
  padding: 7px 10px;
  pointer-events: none;
  transform: translate(12px, -50%);
  box-shadow: var(--shadow-lg);
  font-variant-numeric: tabular-nums;
}

.leaderboard { display: flex; flex-direction: column; gap: 7px; }
.leaderboard-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 100px 42px;
  align-items: center;
  gap: 10px;
}
.leaderboard-name {
  font-size: 12.5px;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.leaderboard-track {
  height: 6px;
  background: var(--surface-3);
  border-radius: 999px;
  overflow: hidden;
}
.leaderboard-fill { display: block; height: 100%; border-radius: 999px; }
.leaderboard-value {
  font-size: 12px;
  font-weight: 700;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.leaderboard-empty { font-size: 12.5px; color: var(--text-3); padding: 10px 0; }

/* ---------- CRE Queue ---------- */

/* One grouped panel for pipeline + bucket + search/dropdown filters, instead
   of three rows of controls floating loose on the page background — a QA
   pass flagged that the filter controls read as an afterthought rather than
   "this is the one control surface for this table". Subtle enough not to
   compete with the table's own surface right below it. */
.crequeue-toolbar {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 14px 14px 12px;
  margin-bottom: 14px;
}

.filter-bar { margin-bottom: 10px; }

/* Pipeline picker is a segmented control, visually distinct from the bucket
   pills below it — the two used to be identical pill rows stacked on top of
   each other, with nothing showing they were different kinds of choice. */
.segmented {
  display: inline-flex;
  flex-wrap: wrap;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 2px;
  gap: 2px;
  box-shadow: var(--shadow-sm);
}
.seg {
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  background: transparent;
  border: none;
  border-radius: 5px;
  padding: 6px 14px;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.12s ease, color 0.12s ease;
}
.seg:hover { color: var(--text); }
.seg.active { background: var(--accent); color: #fff; box-shadow: var(--shadow-sm); }
.seg:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.crequeue-buckets {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 12px;
}
.crequeue-buckets .crequeue-bucket {
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  color: var(--text-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 6px 12px;
  cursor: pointer;
  white-space: nowrap;
  transition: border-color 0.12s ease, color 0.12s ease, box-shadow 0.12s ease, transform 0.12s ease;
}
.crequeue-buckets .crequeue-bucket:hover {
  border-color: var(--accent);
  color: var(--text);
  box-shadow: var(--shadow-sm);
  transform: translateY(-1px);
}
/* Accent blue, not the near-black --ink this used before — every OTHER
   "currently selected" control in the app (sidebar nav, the pipeline
   segmented control above) already uses --accent for that state; the old
   black pill was a one-off that didn't match, confirmed live in a design
   pass alongside the toolbar grouping above. */
.crequeue-buckets .crequeue-bucket.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  font-weight: 600;
  box-shadow: 0 2px 8px rgba(36,84,255,0.28);
}
.crequeue-buckets .crequeue-bucket:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.crequeue-buckets .count {
  display: inline-block;
  margin-left: 5px;
  font-size: 11px;
  font-weight: 700;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
}
.crequeue-buckets .crequeue-bucket.active .count { color: rgba(255,255,255,0.75); }

.crequeue-filters-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
}

/* Icon-embedded search field — scoped to this wrapper only (the plain
   .table-search class is reused elsewhere without an icon, e.g. Customer
   Lookup, so the icon padding can't live on that shared class itself). */
.search-field {
  position: relative;
  display: flex;
  flex: 1;
  min-width: 260px;
  max-width: 380px;
}
.search-field-icon {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  width: 15px;
  height: 15px;
  color: var(--text-3);
  pointer-events: none;
}
.search-field .table-search {
  width: 100%;
  min-width: 0;
  max-width: none;
  padding-left: 32px;
  padding-right: 28px;
}
.search-field .table-search:focus-visible ~ .search-field-icon,
.search-field:focus-within .search-field-icon { color: var(--accent); }
/* No left icon on this variant (#tableSearch) — restore the plain
   left padding .table-search already has everywhere else, instead of the
   icon's reserved 32px. */
.search-field--no-icon .table-search { padding-left: 11px; }

/* The "/" focus-this-search shortcut (see app.js's keydown handler) used to
   be appended straight into the placeholder string ("Search…   /") — three
   literal spaces and a bare character read as a stray typo to anyone who
   hadn't already learned the shortcut, not as an intentional hint.
   A proper <kbd> badge is a standard, recognizable "this is a shortcut"
   affordance instead. */
.search-kbd {
  position: absolute;
  right: 9px;
  top: 50%;
  transform: translateY(-50%);
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: 11px;
  line-height: 1;
  color: var(--text-3);
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  padding: 2px 6px;
  pointer-events: none;
}

#creQueueSearch { min-width: 260px; }
.crequeue-dropdown-filters {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  align-items: center;
}
.crequeue-dropdown-filters select {
  font: inherit;
  font-size: 12px;
  color: var(--text-2);
  background-color: var(--surface);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='M5.5 8l4.5 4.5L14.5 8' stroke='%23565b68' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
  background-size: 15px;
  appearance: none;
  -webkit-appearance: none;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 6px 26px 6px 10px;
  cursor: pointer;
  max-width: 170px;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.crequeue-dropdown-filters select:hover { border-color: var(--border-strong); }
.crequeue-dropdown-filters select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.crequeue-dropdown-filters select.filter-active {
  border-color: var(--accent);
  color: var(--accent);
  background-color: var(--accent-soft);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='M5.5 8l4.5 4.5L14.5 8' stroke='%232454ff' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  font-weight: 600;
}
.btn-clear-filters {
  color: var(--crit);
  border-color: color-mix(in srgb, var(--crit) 30%, transparent);
}
.btn-clear-filters:hover:not(:disabled) {
  background: var(--crit-soft);
  border-color: var(--crit);
}

.crequeue-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-top: 11px;
}
.crequeue-pagination-status {
  font-size: 12px;
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
}
.btn-page { font-size: 12px; padding: 6px 14px; }

.page-jump {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--text-2);
}
.page-jump input {
  font: inherit;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  width: 58px;
  padding: 5px 7px;
  text-align: center;
}
.page-jump input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Chrome/Safari spinner arrows add width this field doesn't have room for
   and aren't needed — typing or Prev/Next already covers the up/down case. */
.page-jump input::-webkit-outer-spin-button,
.page-jump input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.page-jump input { -moz-appearance: textfield; }

/* ---------- Customer Lookup ---------- */

.lookup-form { display: flex; gap: 8px; margin-bottom: 12px; }
.lookup-form .table-search { min-width: 280px; max-width: 440px; }

/* ---------- Entity links + profile modals ---------- */

/* Not accent-coloured by default: in a table where every row has one, a
   column of blue links is visual noise. The underline on hover is the
   affordance. */
.cre-link {
  font: inherit;
  font-size: inherit;
  color: inherit;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  text-align: left;
  border-bottom: 1px dotted var(--border-strong);
}
.cre-link:hover,
.cre-link:focus-visible { color: var(--accent); border-bottom-color: var(--accent); }
.cre-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(14,16,22,0.5);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 40px 20px;
  z-index: 50;
  overflow-y: auto;
}
.modal-panel {
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  width: min(1060px, 100%);
  padding: 20px 22px 24px;
}
.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}
.modal-heading { min-width: 0; }
.modal-header h2 {
  font-family: "Unbounded", "Inter", sans-serif;
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.015em;
  margin: 0;
}
.modal-subheading {
  display: block;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text-2);
  margin-top: 1px;
}
.modal-close {
  font: inherit;
  font-size: 14px;
  color: var(--text-2);
  background: var(--surface-2);
  border: none;
  border-radius: var(--r-sm);
  width: 30px;
  height: 30px;
  cursor: pointer;
  flex: none;
}
.modal-close:hover { background: var(--surface-3); color: var(--text); }
.modal-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.modal-body { display: flex; flex-direction: column; gap: 18px; }

/* Profile modal (CRE/Workshop/Campaign 360) — redesigned 2026-09-19. Used
   to be one flat scroll of 6-8 stacked sections with no sense of what
   mattered most; a headline strip now gives the tl;dr before anything
   else, and everything past it lives behind tabs instead of one long
   scroll (Overview groups the smaller stat sections together; Recent
   Calls / Queue Items — the two actually-tall, detailed tables — each get
   their own tab instead of forcing a scroll through both every time). */
.profile-headline {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 14px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--border);
}
.profile-headline-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-3);
  margin-bottom: 3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Distinguishes a date-scoped headline number ("Bookings": from that date's
   calls) from a current-snapshot one ("Pending Follow-ups": right now,
   regardless of date) — sitting in the same row with no visual difference
   read as the two disagreeing, even though each was already correct for
   its own definition (confirmed live 2026-10-10). Not uppercase/letter-
   spaced like the label above it — deliberately quieter, a footnote rather
   than a second heading. */
.profile-headline-hint {
  font-size: 10px;
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-3);
  opacity: 0.85;
}
.profile-headline-value {
  font-family: "Unbounded", "Inter", sans-serif;
  font-size: 21px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Contact% is the one number here with an app-wide established good/warn/
   critical meaning (see formatCell's own comment on why every OTHER rate —
   Booking% included — deliberately stays uncoloured) — reusing the exact
   same .rate-good/warning/critical text-colour classes keeps that meaning
   consistent instead of inventing a second colour language for the modal. */
.profile-headline-value.rate-good     { color: var(--good); }
.profile-headline-value.rate-warning  { color: var(--warn); }
.profile-headline-value.rate-critical { color: var(--crit); }

/* Reuses .segmented/.seg (the CRE Queue pipeline picker) rather than a
   second tab style — same control, same meaning ("pick one of a few
   views"), just relabelled. */
.profile-tabs { margin-top: 14px; }
.profile-tab-content { display: flex; flex-direction: column; gap: 18px; margin-top: 16px; }

/* The smaller CRE-only stat sections (Data Availability, SMR Nth/N+1 Day,
   Over Due Bookings, FollowUp Calls) used to stack full-width, one under
   the other — five section titles and, worse, on a page that just loaded,
   up to five identical full-width "Still loading" placeholders in a row,
   reading like five broken widgets rather than one thing not done yet
   loading. Two columns roughly halves that vertical run either way. */
.profile-grid-2col {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
}
/* One column of a .profile-grid-2col that needs to hold several stacked
   sections instead of just one — e.g. Data Availability's list sits beside
   FOUR smaller sections (SMR Nth/N+1 Day, Over Due Bookings, FollowUp
   Calls) stacked under each other, not a further nested 2-col grid (that
   would leave each of THEM only a quarter of the row's width, too narrow
   for Over Due Bookings' own 4 cards). Same gap as .profile-tab-content's
   own section spacing, so the rhythm matches whichever column a section
   happens to render in. */
.profile-stack { display: flex; flex-direction: column; gap: 18px; }
@media (max-width: 640px) {
  .profile-grid-2col { grid-template-columns: minmax(0, 1fr); }
}

.cre-section-title {
  font-family: "Unbounded", "Inter", sans-serif;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: 9px;
}
.cre-stat-grid {
  display: grid;
  /* 128px -> 145px: a time value like "9:51:41 AM" was wrapping onto a
     second line at 128px (confirmed live 2026-09-19) — Unbounded is a wide
     display face, and 10 characters at 17px bold genuinely didn't fit that
     card's ~104px of usable width after padding. That one tall card then
     broke the row's rhythm, since every other card in the same row was a
     single short number. 145px gives that ~14-15 characters of room,
     comfortably one line for every value this screen actually shows.
     auto-fit, not auto-fill: auto-fill reserves a track for every column
     that COULD fit at the minimum width even when there's no card left to
     put in it, so a row with fewer cards than tracks left a visible dead
     strip on the right (confirmed live — a 6-card ScoreCard row and a
     2-card FollowUp Calls row both trailed off with empty space instead of
     using it). auto-fit collapses those empty tracks and lets the real
     cards share the reclaimed width instead, so every row — however many
     cards it has — fills the container. */
  grid-template-columns: repeat(auto-fit, minmax(145px, 1fr));
  gap: 7px;
}
/* loadForecast writes a .cre-stat-grid INTO #mtdStatGrid, which is itself
   one — collapse the inner wrapper so it doesn't nest two grids. */
.cre-stat-grid .cre-stat-grid { display: contents; }

/* Forces a fixed column count, but ONLY once the modal has genuinely
   narrowed (below where .modal-panel's own min(1060px,100%) cap stops
   being the binding constraint — roughly true viewport widths ≲1000px).
   Confirmed live in a UI audit: Call ScoreCard's 6-card row, left to plain
   auto-fit, wrapped unevenly (5+1, then 4+2 as it narrowed further) once
   it stopped fitting one row — the exact "stranded card, empty space
   beside it" pattern already fixed once for Over Due Bookings, just
   width-triggered instead of always-on. Double-class selector so this
   wins over the base rule regardless of the two rules' order in the file. */
@media (max-width: 1050px) {
  .cre-stat-grid.cre-stat-grid--narrow-fixed-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Compact label/value list — used for CRE 360's queue breakdown instead of
   a card grid. 11 near-identical small boxes (2026-09-19 v1 of this screen)
   turned out to still read as a wall of noise even once each got a
   category-colour left border: the border was too subtle to actually help
   scanning, and a GRID of boxes makes you re-orient (find the label, drop
   down to the number, move to the next box) eleven separate times. A list
   lets the eye run straight down one column of labels and one column of
   right-aligned numbers — the same reason a spreadsheet column is easier to
   scan than the equivalent numbers scattered across separate boxes — and
   grouping by "Still Open" vs "Closed" (the app's own existing active/
   terminal split — see cloudConnectProvider.js's TERMINAL_BUCKETS) states
   the distinction as an actual heading instead of a border colour a viewer
   has to notice and remember the meaning of. */
.cre-stat-list-groups {
  display: flex;
  flex-direction: column;
  gap: 16px;
  /* Left unconstrained, the modal's full ~1400px width stretched every row
     into a bare word on the left and a number way off on the right with a
     long empty gap between them — technically readable, but nothing about
     it reads as "one line of information" at a glance. Capped to a width a
     label+number pair actually needs. */
  max-width: 420px;
}
.cre-stat-list-group-title {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-3);
  margin-bottom: 4px;
}
.cre-stat-list-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 2px;
  border-bottom: 1px solid var(--border);
}
.cre-stat-list-row:last-child { border-bottom: none; }
.cre-stat-list-label {
  font-size: 13px;
  color: var(--text-2);
}
.cre-stat-list-value {
  font-family: "Unbounded", "Inter", sans-serif;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* "Closed" rows are settled, lower-priority to look at than the still-open
   ones above them — de-emphasised the same way the rest of the app treats
   a resolved/inactive state (muted text), not hidden, just quieter. */
.cre-stat-list-group--closed .cre-stat-list-label,
.cre-stat-list-group--closed .cre-stat-list-value { color: var(--text-3); }
.cre-stat {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 10px 12px;
}
.cre-stat-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-2);
  margin-bottom: 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cre-stat-value {
  font-family: "Unbounded", "Inter", sans-serif;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  /* Safety net on top of the wider minimum column above — never wrap to a
     second line (that's what broke the row height in the first place);
     the rare value still too long for its card truncates with an ellipsis
     instead, which reads as "cut off" rather than "the whole row is
     broken". */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cre-section-empty {
  font-size: 12.5px;
  color: var(--text-3);
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-sm);
  padding: 10px 13px;
}
.cre-profile-search { margin-bottom: 8px; max-width: 300px; min-width: 0; }
.cre-mini-table-scroll {
  overflow: auto;
  max-height: 340px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  position: relative;
}
.cre-mini-table { width: 100%; font-size: 12px; }
.cre-mini-table th {
  position: sticky;
  top: 0;
  z-index: 2;
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-2);
  background: var(--surface-2);
  border-bottom: 1px solid var(--border-strong);
  padding: 7px 10px;
  white-space: nowrap;
}
.cre-mini-table td {
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.cre-mini-table tr:last-child td { border-bottom: none; }
.modal-body .crequeue-dropdown-filters { margin-bottom: 8px; }

/* ---------- Narrow ---------- */

@media (max-width: 1080px) {
  /* Collapse is a wide-viewport affordance only — below this width the
     sidebar is already a horizontal strip (its own layout below), so the
     collapse toggle is hidden and any collapsed state from a wider window
     is overridden back to the normal grid. */
  .app,
  body.sidebar-collapsed .app { grid-template-columns: 1fr; }
  .sidebar-toggle { display: none; }
  .sidebar {
    position: static;
    height: auto;
    flex-direction: row;
    align-items: center;
    gap: 14px;
    padding: 0 14px;
  }
  .brand { padding: 12px 0; flex: none; }
  /* min-width:0 is load-bearing: a flex:1 item's default min-width is
     `auto`, which means "never shrink below your content's own size" — so
     #tabs (wider than available space, 10 tabs in a row) was forcing THIS
     item to overflow, which pushed the overflow up to .sidebar's own
     overflow-x:auto instead of scrolling locally. That let the scroll
     carry the brand logo and status pill along with it (both siblings of
     .sidebar-nav in the same flex row) — confirmed live: scrolling to
     reach "MTD & Forecast" scrolled the "HH" logo off the left edge too.
     With min-width:0, .sidebar-nav is finally allowed to shrink to its
     share of the row, and #tabs becomes ITS OWN scroll container below —
     brand and the status pill now stay fixed in place while only the tab
     row scrolls, which is both the better interaction and what makes
     #tabs's own scrollWidth/clientWidth measurement (updateTabsScrollShadow
     in app.js) actually correct instead of silently always reporting no
     overflow. */
  .sidebar-nav { padding: 0; flex: 1; min-width: 0; }
  #tabs {
    display: flex;
    gap: 4px;
    align-items: center;
    overflow-x: auto;
    scrollbar-width: thin;
  }
  /* The fade hints were wired up in JS (updateTabsScrollShadow) but never
     had a matching rule here — confirmed live in a UI audit: several tabs
     were reachable only by a scroll nobody was ever cued to try. White
     fade (not the light tables' fade-to-surface) since this sits on the
     dark sidebar; both edges, since landing on a later tab (a fresh page
     load restores whichever tab was last active) can start you scrolled
     right of tab 1, not just needing a hint to scroll further right. */
  #tabs.has-overflow-right {
    mask-image: linear-gradient(to right, black calc(100% - 24px), transparent);
    -webkit-mask-image: linear-gradient(to right, black calc(100% - 24px), transparent);
  }
  #tabs.has-overflow-left {
    mask-image: linear-gradient(to left, black calc(100% - 24px), transparent);
    -webkit-mask-image: linear-gradient(to left, black calc(100% - 24px), transparent);
  }
  #tabs.has-overflow-left.has-overflow-right {
    mask-image: linear-gradient(to right, transparent, black 24px, black calc(100% - 24px), transparent);
    -webkit-mask-image: linear-gradient(to right, transparent, black 24px, black calc(100% - 24px), transparent);
  }
  /* flex-shrink:0 on both — #tabs is a flex container, and its default
     child behavior is to SHRINK to fit rather than overflow. That would
     squash every tab's padding/text into an unreadable sliver instead of
     genuinely overflowing into the scrollbar #tabs now provides above. */
  .nav-group { display: flex; gap: 3px; align-items: center; flex-shrink: 0; }
  .nav-group + .nav-group { margin-top: 0; }
  .nav-group-label { display: none; }
  .tab { width: auto; padding: 7px 11px; }
  .tab + .tab { margin-top: 0; }
  .tab-icon { display: none; }
  .smr-subtabs {
    flex-direction: row;
    margin: 0 0 0 4px;
    padding-left: 6px;
    border-left: 1px solid rgba(255,255,255,0.12);
    border-top: none;
  }
  /* At this width the whole #tabs row scrolls horizontally, so a pill can
     land anywhere along a long scrolled strip with no visible group
     boundary nearby — the thin border-left above scrolls away with it.
     Confirmed live in a UI audit: once scrolled, a sub-tab pill (e.g.
     "FollowUp Calls") sits flush against an unrelated main tab ("CRE
     Queue") with nothing to tell them apart. A background chip survives
     the scroll regardless of position. */
  .smr-subtabs .crequeue-bucket {
    background: rgba(255,255,255,0.07);
    border-radius: 999px;
  }
  .smr-subtabs .crequeue-bucket.active { background: rgba(255,255,255,0.18); }
  .sidebar-foot {
    flex: none;
    flex-direction: row;
    align-items: center;
    gap: 10px;
    border-top: none;
    padding: 0;
  }
  .source-badge { display: none; }
  .main { padding: 14px; }
  .stat-row { flex-direction: column; }
  .stat-row-label { border-right: none; border-bottom: 1px solid var(--border); flex-direction: row; gap: 7px; align-items: baseline; }
  .table-scroll { max-height: calc(100vh - 220px); }
}

@media (max-width: 720px) {
  .stat-cells { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stat-cell:nth-child(3) { border-left: none; }
  .stat-cell:nth-child(n+3) { border-top: 1px solid var(--border); }
  .topbar-right { width: 100%; }
  .table-search { min-width: 0; }
}

@media print {
  .sidebar, .topbar-right, .panel-toolbar, .crequeue-pagination { display: none; }
  .app { display: block; }
  .table-scroll, .crequeue-table-scroll { max-height: none; overflow: visible; border: none; }
  /* Forecast's 3 sub-tables set their own max-height by ID (see the plain,
     non-media-query rule earlier in this file) specifically so they don't
     eat the whole viewport on screen — but an ID selector always beats a
     class selector regardless of which comes later in the file or which
     @media block it's in, so the .table-scroll/.crequeue-table-scroll reset
     two lines up was silently losing to it here. Confirmed live in a UI
     audit: printing Forecast clipped these 3 tables to their on-screen
     300px scroll height instead of expanding to fit the page. Matching the
     ID specificity (not just the class) is the only way to actually win. */
  #forecastBookingsScroll,
  #forecastCallVolumeScroll,
  #forecastDueServicesScroll { max-height: none; overflow: visible; }
  /* Landscape roughly doubles usable width vs. the browser's portrait
     default — every printed tab benefits, but this is what actually makes
     Customer Lookup's table fit at all (see the rule below). */
  @page { size: landscape; margin: 12mm; }
  /* Customer Lookup has no column-density toggle like the other live
     tables — all 12 LOOKUP_COLUMNS (app.js) always render, and every cell
     is `white-space: nowrap` (see the shared `table`/`tbody td` rules
     earlier in this file). Confirmed live in a UI audit via an actual
     printed PDF: 3 columns (Reached?, Last Call, Last Call Status) fell off
     the right edge of the page and never printed at all. Shrinking the type
     and letting cells wrap is the only way this fits without a real
     column-hide UI, which this tab doesn't have. */
  #lookupTable,
  #lookupTable th,
  #lookupTable td {
    font-size: 9px;
    padding: 4px 6px;
    white-space: normal;
    word-break: break-word;
  }
}
